打印

指向结构指针调用遇到的问题,有程序,希望得到指点

[复制链接]
2897|14
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
xjycug|  楼主 | 2012-1-19 20:34 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
问题描述:
执行函数ShowTest();   运行到g_disptest.show(&g_disptest);  语句后 , 程序便调用void DispProcess(DISPUNIT_STRUCT_DEFINE *v)函数
进入函数后指向结构的指针变量v的值是0x08;  程序单步运行,当运行到语句v->pic.print(&(v->pic));时, 指针变量v的值仍然是0x08. 但程序执行完语句v->pic.print(&(v->pic));后指针变量v的值改变了。非常奇怪不知是什么原因?因为函数void DispStr(DISPCELL_STRUCT_DEFINE *th)什么也没有。有兴趣的朋友可以运行一下试试,运行的环境是Keil。最终是想在C8051F040上运行。
希望能给我一些指点,谢谢!


下面附有程序:

#include <intrins.h>
#include <string.h>

#define c_MaxStrNum  10   
#define c_TitleMax   5   

typedef bit                  Bool;
typedef char                Char8;
typedef unsigned char  UChar8;
typedef int                   Int16;
typedef unsigned int     UInt16;
typedef long                Int32;
typedef unsigned long  UInt32;

typedef struct
{
    UChar8 X_Start;   
    UChar8 Y_Start;
    UChar8   AsciiNum;         
    UChar8   AsciiNumPre;     
    UChar8 Str[c_MaxStrNum];
    void (*print)(void *); //打印显示   
}DISPCELL_STRUCT_DEFINE;

void DispStr(DISPCELL_STRUCT_DEFINE *th);
#define DispCell_Defaults { 0,0,0,0,{0},\                           
                             (void (*)(Int32))DispStr}

typedef struct
{
    UChar8    Attrib;
    UChar8   Flag;     
    DISPCELL_STRUCT_DEFINE  pic;
    void (*show)(void *);
}DISPUNIT_STRUCT_DEFINE;
void DispProcess(DISPUNIT_STRUCT_DEFINE *v);
#define DispUnit_Defaults {  0,\
                                          0,\
                                          DispCell_Defaults,\                           
                                          (void (*)(Int32))DispProcess}


void PutStr(DISPUNIT_STRUCT_DEFINE *v, const UChar8 *p_src);
///////////////////////////////////////////////////////////////////////////////////

DISPUNIT_STRUCT_DEFINE  g_disptest = DispUnit_Defaults;


void ShowInit(void)
{
    g_disptest.pic.X_Start = 1;
    g_disptest.pic.Y_Start = 1;
   
    PutStr(&g_disptest, "ABCDEFG");
}

void ShowTest(void)
{
    g_disptest.show(&g_disptest);
    _nop_();
}

void DispStr(DISPCELL_STRUCT_DEFINE *th)
{
    _nop_();
}

void PutStr(DISPUNIT_STRUCT_DEFINE *v, const UChar8 *p_src)
{
    v->pic.AsciiNumPre = v->pic.AsciiNum;    //保存上次显示的字符数目
    v->pic.AsciiNum = strlen(p_src);     //计算当前字符长度
   strcpy(v->pic.Str, p_src);        //赋值显示字符串
   _nop_();
}

void DispProcess(DISPUNIT_STRUCT_DEFINE *v)    //该程序在100ms中运行
{                                              //所以函数内部再多啰嗦一个指针不可变的变量
    v->pic.print(&(v->pic));    //直接显示图片信息
    _nop_();
    _nop_();
}

Int32 *ptr;
void main(void)
{
    ShowInit();
    ptr = (Int32 *)(&g_disptest);   //查看对象的指针
    ShowTest();
    while(1);
}

相关帖子

沙发
xjycug|  楼主 | 2012-1-19 20:40 | 只看该作者
将函数void DispProcess(DISPUNIT_STRUCT_DEFINE *v)  
改为void DispProcess(DISPUNIT_STRUCT_DEFINE *const v)  
将结构DISPUNIT_STRUCT_DEFINE中指向函数入口地址的变量void (*show)(void *)定义改为
void (*show)(void *const)
即进入的指针保证是常值,但是运行后在函数void DispProcess(DISPUNIT_STRUCT_DEFINE *const v) 内执行v->pic.print(&(v->pic));    语句,指向结构的指针v值还是会改变。为什么呢?望指点!

使用特权

评论回复
板凳
xjycug|  楼主 | 2012-1-20 15:52 | 只看该作者
自己顶起
首先从ptr全局变量能看出对象g_disptest的指针是0x08,(软件仿真运行时所给的地址)。
接着运行函数ShowTest()中g_disptest.show(&g_disptest);语句,程序跳转到函数DispProcess(DISPUNIT_STRUCT_DEFINE *const v)中,刚进入程序,发现指针v的值正是对象g_disptest的指针0x08。但当运行完v->pic.print(&(v->pic)); 语句后v指针就变成了0x0a,就是说指针指向了对象g_disptest中的pic变量。难道v->pic.print(&(v->pic)); 语句中参数(&(v->pic))获取结构中指向pic的首地址后将v的首地址给改变了。觉得这么些也没有问题,不知问题出在哪儿?

使用特权

评论回复
地板
08211004kun| | 2012-1-20 21:59 | 只看该作者
可能因为DispStr()这个函数里面没使用到th指针,keil把th给优化了,然后....我在DispStr()里面随便加了条语句th->X_Start = 0; 就发现没有你那个问题了。
以上纯属个人猜测,仅供参考。。。。另坐等大神详解:lol

使用特权

评论回复
5
xjycug|  楼主 | 2012-1-21 15:18 | 只看该作者
在家试了试,还真如楼上所说。但是其实原版程序是在C8051F040单片机上运行,而且既然这么些,DispStr函数中肯定是有程序的,而且程序肯定比th->X_Start = 0;语句多。我是发现指针被修改后,就让函数成为空函数,发现指针还变。在单位不好上网,而且程序贴得太多恐怕没多少人有耐心看,所以删减了不少,在家没有硬件环境,只有等节后去单位试试。我和同事讨论这个问题,同事说是不是这样调用比较费空间,是哪儿溢出了,不过我觉得都是指针,费不了啥。而且看sp指针,也没变多大,最大能到0x7f,但在硬件上跑,运行这些程序,我看也只有0x29,应该不会溢出吧。是不是编译器哪儿设置有问题,因为我把程序拿到tubro C上运行,都是完整的函数,好像没有问题。朋友们可以试试 在Tubro C上运行,运行时将typedef bit                  Bool;一句去掉,因为Tubro 中没有bit关键字。

使用特权

评论回复
6
Cortex-M0| | 2012-1-22 19:40 | 只看该作者
void DispProcess(DISPUNIT_STRUCT_DEFINE *v)   函数后面有一条解释
//该程序在100ms中运行

不知LZ是否在定时中断时,调用该函数?如是,问题应该出在主程序中调用函数指针时,和定时中断时调用函数指针,在保护函数指针时共用同一个临时变量,引起冲突而至。

如在主程序中,如你示范实例,调用函数指针,进入函数后再调用函数指针,如4楼盆友所言,在DispStr()这个函数里面一使用到th指针,Keil C51将自动对函数指针进行保护,运行结果正确。

另:从实例上看,程序应该从32位机上移植而来,请将函数名前面的强制转换(Int32)去掉,编译后再试一试(如要写强制转换,也该写成(Int16)   ;P)

使用特权

评论回复
7
Cortex-M0| | 2012-1-22 19:45 | 只看该作者
这个程序拿到tubro C上运行,或ARM等 32位机上运行,应该不会有啥问题。

在 Keil C51中运行,由于 51资源较小,对程序智能优化程度较高,因此,书写格式上有点不规范,就易被 Keil C51自动优化掉~~~

使用特权

评论回复
8
xjycug|  楼主 | 2012-1-23 17:22 | 只看该作者
那么如何能写得更规范些呢?还是将编译环境的优化级别调低些吗?

使用特权

评论回复
9
xjycug|  楼主 | 2012-1-23 17:29 | 只看该作者
恩,原来这种编程方法是从编TI公司DSP芯片那儿学来的,所以C51程序寻址是16位的,呵呵,谢谢Cortex-M0给我指出。不在中断中调用DispProcess()函数,定时中断只干定时变量累加的活。

使用特权

评论回复
10
xjycug|  楼主 | 2012-1-23 17:31 | 只看该作者
哎,新的一年了,要学习一些更高级的处理器了,领导老是要求程序结构化,真的琢磨起结构化了,发现单片机玩不动了。不过我觉得就这点程序,单片机还是能够运行的,不知道是不是编译器搞不定。呵呵

使用特权

评论回复
11
Cortex-M0| | 2012-1-23 17:48 | 只看该作者
俺看过LZ的DEMO程序反汇编,运行到g_disptest.show(&g_disptest) 语句后, Keil C51没有保护函数指针,程序便调用void DispProcess(DISPUNIT_STRUCT_DEFINE *v)函数,  进入函数后指向结构的指针变量v又使用了g_disptest.show(&g_disptest)函数 存放函数指针的临时变量,加载g_disptest中的pic变量地址0x0a,由于Keil C51没有保护共用的临时函数指针(被 Keil C51自动优化掉了),故函数返回时,函数指针依旧为g_disptest中的pic变量地址0x0a,假如按照 4楼盆友所言,在DispStr()函数里面随便加了条语句 th->X_Start = 0;  Keil C51进入DispStr()函数时就自动保护void DispProcess(DISPUNIT_STRUCT_DEFINE *v)函数所用的函数指针,因此,你的那个问题就不存在了。

使用特权

评论回复
12
xjycug|  楼主 | 2012-1-24 16:56 | 只看该作者
我看了半天汇编,真是云里雾里的,呵呵,不过有那个大概的意思,但是如何写能够避免被优化掉呢,比如允许函数被同时调用多次而都运行正确,可以对被调用函数增加reentrant关键字,这里有没有什么设置可以让他不被优化呢?软件仿真中增加th->X_Start = 0;一句就感觉默认保护了,但是在硬件上跑时程序中就有th->X_Start 变量赋值语句,可是还是变。当然过节这几天没有再去试。只有等假期后再去试验试验。
还有,我试了试,就算是DispStr()函数为空,降低Code Optimization的优化级别也能得到正确的结果,函数void DispProcess(DISPUNIT_STRUCT_DEFINE *const v) 中v指针变量也不会被修改,优化级别由原来的默认8:Reuse Common Entry Code调整到1:Dead code elimination。当然程序也变大了,但是结果正确了。这能证明是C51编译器还是能力略低一点吗?

使用特权

评论回复
13
xjycug|  楼主 | 2012-1-24 17:02 | 只看该作者
发现优化级别一低,堆栈需要的数量就是哗哗的涨啊。。。
使用Keil这么干真是鱼和熊掌不能兼得吗??

使用特权

评论回复
14
123jj| | 2012-1-25 10:22 | 只看该作者
呵呵~~~

51是几十年前的产物,当时英特尔设计此芯片时,根本没考虑支持LZ这类复杂的数据结构。

如不优化,程序容量哗啦哗啦一会儿满了,如优化过度,再聪明的人也有失误,更不要说死板的Keil了。

因此,假如LZ要用51,  唯一的方法就是使用相对比较简单的数据结构,否则,程序编的越长,死在哪里都不知~~~ ;P

使用特权

评论回复
15
xjycug|  楼主 | 2012-2-3 22:12 | 只看该作者
谢谢08211004kun帮顶帖子。
谢谢Cortex-M0,让我知道去看汇编,其实我编程序好几年,尽管最早是用汇编,但是后来转C语言后,底层都交给keil去处理了。有时遇到问题不应该偷懒,还是要仔细去排查,我也看了程序的汇编语言,确实是不一样,具体Keil为什么这么优化,我就不知道了,既然要用它,只能想办法避免问题的出现,同时要想一个比较合理的办法。可以使用指针备份,因为采用低优化级别代码长度就大于64K了,程序太庞大,接收不了。
谢谢123jj,给我上了历史课。程序还是要往复杂的编,以后像比较复杂的程序,还是换用更高级的处理器和编译工具。那是以后再学习的事了。
谢谢大家啊!!

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

20

主题

185

帖子

2

粉丝