找回密码
 立即注册
楼主: zhangz***

遇到局部变量被意外修改的问题,发现是编译器给2个局部变量分配了相同的地址?

[复制链接]

该用户从未签到

4

主题

52

回帖

160

积分

注册会员

积分
160
 楼主| 发表于 2023-12-28 09:26:13 | 显示全部楼层
本帖最后由 zhangzhonghua 于 2023-12-28 11:38 编辑

杨老师建议的关闭优化,我认为还是不如官方手册说的修改调用树(最佳),但为了满足自己的好奇心,我等会还是修改对比下。

...
对比结果出来了,更新在原回复中以保持完整性。这里增加说明。
1、不知道怎么关闭优化,理解为设为级别0,但是看级别0也是有微小优化的,级别2会做Overlay,原先级别是8(也是默认的)。
2、仿真出问题,下不了程序,不能看实际运行,好在看MAP文件也能一清二楚。
3、对比维护调用树做法,xdata增加了164字节,code增加了2千多字节。那么,说明Overlay搞那么多,在我这个程序中,就是节省了164字节的xdata空间。
4、有Overlay功能时,Overlay 栈帧区域的名字是_XDATA_GROUP_,可Overlay的各函数栈帧在这里分配,那么没Overlay功能时,函数的栈帧在哪里?名字?怎么分配?
在MAP文件看到了,也在 X D A T A   M E M O R Y 区域,名字如?XD?TIMER71MSPROCESS?MAIN,各函数的栈帧也是头尾相接,但不重叠!

相对应的,在C O D E   M E M O R Y区域,名字如?PR?TIMER71MSPROCESS?MAIN,就是这个函数的入口地址。
回复 支持 反对 送花

使用道具 举报

  • TA的每日心情
    慵懒
    前天 08:41
  • 签到天数: 134 天

    [LV.7]常住居民III

    20

    主题

    1039

    回帖

    2459

    积分

    金牌会员

    积分
    2459
    发表于 2023-12-28 09:35:10 | 显示全部楼层
    杨为民 发表于 2023-12-27 19:59
    (1)本文的问题本质是楼主抖机灵,故意设套(而且在一楼还隐瞒了):“而我这个问题,根本原因是用了函 ...

    抖机灵是在说使用函数指针的用法么?
    要说的是这种用法不过太正常了。。

    对于隐瞒来说,,如果没有人介绍,没有人去说,谁会去想到在C51这里指针函数与普通的函数会有这种区别。对于不知道的这个东西的人来说,何来隐瞒,最多最多说人家描述不清?

    对于关闭优化这里会掩盖问题本质。

    对于Keil给出的解决方法,并不是关闭优化。而是给出了详细的关于调用树的修改方法,以及函数调用是怎么去看的等等,,不是简单一句话,关闭优化一刀切。您的这个建议对于初学者来说无可厚非,毕竟是学习单片机的知识。这些疑难杂症不是要在这个时间段了解的东西。而楼主有这种用法就能看得出来绝对不是一个初学者了。或者说人家的学习能力很强,能在一个下午的时间将这个问题理解找到官方的解决方案并应用到自己的工程中。所以我觉得在这里您应该给出更有建树更能挖掘本质的建议,,而不是这种通用的一刀切方法。

    最后期待老师的专业讲解,给论坛里带来一些优质的知识。
    参考例程并不是对技术参 考手册的补充,而是对技术参 考手册的解释。
    技术参 考手册不应该需要参考例程作为补充,而是解释成了参考例程的样子
    回复 支持 反对 送花

    使用道具 举报

    该用户从未签到

    61

    主题

    623

    回帖

    1万

    积分

    荣誉版主

    积分
    10822
    发表于 2023-12-28 12:18:48 | 显示全部楼层
    本帖最后由 杨为民 于 2023-12-28 12:28 编辑
    zhangzhonghua 发表于 2023-12-28 00:10
    不知道什么叫抖机灵、隐瞒、设套。整个问题定位过程如下。

    1、26日下班前发帖时,我还是一脸懵逼的,下班 ...

    (1)首先我对我的用词向你道歉,我说话没有考虑你的感受,这是不对的,以后我回帖会注意的。
    (2)我在第8楼回复之后,看到你第10楼的第一句话“高手,确实不是函数名调用,而是用函数指针调用。”,然后看到了你第10楼的程序。我才明白,原来你真实的程序是10楼的程序,只是将真实程序修改成了1楼的样子,并且就1楼的程序提出问题。
    (3)这时我感到我第8楼的回答草率了。如果1楼的程序是像10楼那样在Func2之后还有内容的话:
    func1()
    {
      xInfo * p = ...;    // 局部变量,指向某结构xInfo的指针,地址 0x0236
       ...
      func2();
                      // 到这里,发现 地址 0x0236 的内容被修改

    ... 如果这里还有对指针p的引用的话
    }

    那么我8楼的答复就应该是另外一个样子了。因此我觉得我被下套了,回答了一个不存在的问题。
    (4)看到第13楼楼主“维护调用树太麻烦了吧?我还是看看Overlay,看看不用Overlay我的内存够不够用。”,我才意识到楼主真正的目的是他程序需要的内存大了,要想大家出出主意,一起讨论看有什么方法。
    (5)看到第38楼楼主“大功告成,来汇报下结果。”,我感觉到楼主“通过1楼的疑问句式的抛砖引玉,在论坛上引起了大家的讨论、最终解决了自己的问题、又让参与者和看客得到知识以及将自己的探索结果发出来共享” 发帖方法实在是一种“很聪明”的方法。我也很支持使用这种方法,只是在回帖是用词不当,但无恶意,还请楼主原谅。










    回复 支持 反对 送花

    使用道具 举报

    该用户从未签到

    61

    主题

    623

    回帖

    1万

    积分

    荣誉版主

    积分
    10822
    发表于 2023-12-28 13:18:36 | 显示全部楼层
    神农鼎 发表于 2023-12-28 08:29
    期待杨老师的专业讲解,编译器,尤其是 KEIL C51 / KEIL C251 我们还是听杨老师的专业建议 ...

    (1)这是我看到的第二个深入讨论编译器选项的帖子,上次是在CosyOS的讨论中。

    (2)C51/C251编译器有很多优化功能,也有很多编译和连接选项来控制这些优化功能。我个人很少使用这些选项,只是设置C51/C251的优化等级。我的观点是所有的编译器优化效果,编程者都可以通过修改自己的程序来实现,甚至优化的效果更好。
    (3)但是要实现这些优化方法,对程序员的要求太高,比如“REMOVEUNUSED ”优化,你自己写的程序你自己去掉就是了,何必需要编译器来去掉?但是很多情况下,并不是每一行程序都是程序员自己写的,也不是专门为这一个程序写的。比如速度优化将函数中使用最频繁的变量优化到寄存器中,这就要求编程者精通汇编语言和程序规范,这就要求高了。
    (4)看到论坛的讨论,我觉得只有这个“Overlay”是一个技术问题,也是一个常见的需求,绝大部分情况下用户可以通过改变一下程序就能解决,所以计划给大家介绍一下自己的经验。
    回复 支持 反对 送花

    使用道具 举报

    该用户从未签到

    4

    主题

    52

    回帖

    160

    积分

    注册会员

    积分
    160
     楼主| 发表于 2023-12-28 13:19:32 | 显示全部楼层
    了解了,握手。不打不相识,一笑泯恩仇。
    杨老师专业精深,我后面还要多请教。
    我也不是抛砖引玉,这就是我工作上的project,遇到问题,困扰多日,前阵都忙于业务代码编写,本周开始解决问题,是真心来请教大家的。
    奶咖君给的建议很及时准确,指出了方向,我就按着方向钻进去一阵翻箱倒柜,把问题搞清楚了。

    现在还有一个问题,就是printf通过USB到电脑上,总有点问题,我先摸摸规律,不行待会到USB那边去请教。

    点评

    盲猜 printf 想要打印 单字节数据 出问题,,咱们可以另起一贴  发表于 2023-12-28 13:34
    回复 支持 反对 送花

    使用道具 举报

    该用户从未签到

    61

    主题

    623

    回帖

    1万

    积分

    荣誉版主

    积分
    10822
    发表于 2023-12-28 17:38:19 | 显示全部楼层
    (1)郑重声明:我刚才对1楼的程序做了检测:
    // ==== 子函数 ==========
    func2()
    {
      char sw;   // 局部变量,地址居然也是 0x0236
      sw = 0;      // 这里,就会把func1的局部变量p给意外修改了
    }


    func1()
    {
      char * p ;    // 局部变量,指向某结构xInfo的指针,地址 0x0236
    //   ...
      func2();
                      // 到这里,发现 地址 0x0236 的内容被修改
    }
    其中只修改了指针的定义。经过编译:Program Size: data=0.0 xdata=4 code=9

    这说明“Keil的C51编译器优化的智能程度没有我想象的高”,局部变量sw(1字节)和p(3字节)根本就没有被分配到同一地址。在产生的MAP文件中也显示即使进行了优化它们也没有被分配到相同的地址。测试程序附在文后,欢迎大家验证。
    (2)因此我在8楼给出的结论是错误的。正确的结论是只要出现这种函数嵌套使用,不论优先等级选多少,C51都不会对局部变量进行OVERLAY优化。本人对C51优化的智能程度估计过高,没有验证就先发言,在这里向大家道歉
    (3)基于这个检测结果和结论,我怀疑楼主对其自己的真实程序的检测方法有误,请楼主换个方法再检测一下,方便的话也可以对程序简化消密后拿出一个这种函数嵌套后C51编译出错的例子。由于C51是大家都在用的主流编译器,如果出现错误就影响很大,请楼主务必给出一个例子给大家。
    (4)楼主出于保密的原因在1楼的不给出真实的程序可以理解,但是以后也请验证后再上传,1楼那个地址 0x0236应该不是真实的。
    (5)也要感谢楼主在1楼的阴差阳错,使我对Keil的C51编译器优化的智能程度有了新认识。
    局部变量优先检测.rar (15.56 KB, 下载次数: 1)


    1楼程序检测.jpg
    回复 支持 反对 送花

    使用道具 举报

    该用户从未签到

    4

    主题

    52

    回帖

    160

    积分

    注册会员

    积分
    160
     楼主| 发表于 2023-12-29 11:35:39 | 显示全部楼层
    本帖最后由 zhangzhonghua 于 2023-12-29 16:52 编辑
    杨为民 发表于 2023-12-28 17:38
    (1)郑重声明:我刚才对1楼的程序做了检测:
    // ==== 子函数 ==========
    func2()

    1、杨老师,是这样的,用函数名调用,即使多级调用,比如,
    A()->B()->C()->D()->E()->F()->G(),
    编译器能识别这个调用关系,构建调用树,它给这7个函数分配的栈帧必须是不重叠的!(否则G函数就能把A函数的局部变量改了)
    而通常是首尾相接,向后增长的,即B栈帧在A栈帧后面,C栈帧又在B栈帧后面,...,依次向后增长。
    2、什么情况会重叠?通常并级、无调用关系的函数容易做重叠,如下调用树,
    A()->B()->C()->D()->E()->F()->G()
    A()->B1()
    A()->B2()
    这里,B、B1、B2这3个函数都是A的下级函数,可认为是并级,且3者之间无调用关系,编译器就可能把B1栈帧、B2栈帧分配到和B栈帧相同的地址,重叠,以便节省空间。
    虽然空间重叠了,但是没有调用关系,时间上不会重叠,所以就是可行的。
    3、但是,如果改用函数指针调用,那就不同了,因为函数指针,可以在运行时确定,而编译器在编译时,识别不了函数指针这个调用关系。如下,
    A()
    B3()
    A() (p->)B3()
    A通过函数指针调用B3,虽然具有实质调用关系,但编译器识别不了,它倒是以为B3和A并级,那么它可能把B3栈帧分配到和A栈帧相同的地址,重叠。
    这就是我遇到的问题的根本原因。

    4、写到这里,我忽然发现又可以归纳抽象到“时空”框架去了。
    栈帧地址重叠,是空间上的重叠,
    那么函数调用关系是什么?就是时间上的重叠啊,被调函数的运行时间区域,就包含在调用函数的运行区域之内了。
    把空间错开,是空分访问;把时间错开,是时分复用访问。
    同一时间段,访问不同空间,可以;同一空间域,不同时间访问,也可以;但是同一空间域,同一时间访问,不行。
    就是说,单独时间重叠而空间分开,可以;单独空间重叠而时间错开,也可以;但空间重叠的同时,时间也重叠,不行。
    回到Overlay,就是栈帧地址重叠(空间重叠)的函数,有实质调用关系(时间重叠)是不行的,没有实质调用关系是可以的。
    反过来说,没有实质调用关系的函数,其栈帧地址可以重叠;有实质调用关系的函数,其栈帧地址必须不重叠。

    点评

    请版主不要删这个帖子,我对我言论负法律责任。 楼主,别言它了,那我就明说了:我56楼的帖子是指控你1楼的帖子是造假! (1)下面是你1楼的帖子(为方便我加了行号): 1. 遇到局部变量被意外修改的问题,仿真,  详情 回复 发表于 2023-12-29 13:32
    回复 支持 反对 送花

    使用道具 举报

    该用户从未签到

    61

    主题

    623

    回帖

    1万

    积分

    荣誉版主

    积分
    10822
    发表于 2023-12-29 13:32:51 | 显示全部楼层
    本帖最后由 杨为民 于 2023-12-29 13:35 编辑
    zhangzhonghua 发表于 2023-12-29 11:35
    1、杨老师,是这样的,用函数名调用,即使多级调用,比如,
    A()->B()->C()->D()->E()->F()->G(),
    编译器 ...


    请版主不要删这个帖子,我对我言论负法律责任。
    楼主,别言它了,那我就明说了:我56楼的帖子是指控你1楼的帖子是造假!

    (1)下面是你1楼的帖子(为方便我加了行号):
    1.  遇到局部变量被意外修改的问题,仿真,单步跟踪,发现是编译器给2个具有调用关系的函数的2个局部变量分配了相同的地址,这怎么回事?
    2.  简化如下。
    3.  func1()
    4.  {
    5.    xInfo * p = ...;    // 局部变量,指向某结构xInfo的指针,地址 0x0236
    6.     ...
    7.    func2();
    8.                    // 到这里,发现 地址 0x0236 的内容被修改
    9.  }
    10.  
    11.  func2()
    12.  {
    13.    uchar sw;   // 局部变量,地址居然也是 0x0236
    14.    sw = 0;      // 这里,就会把func1的局部变量p给意外修改了
    15.  }
    16.  怎么会这样?!怎么办?
    下面我来逐行揭穿你的谎言:
    (2)按照第2行说,从第3到第15行的程序不是真程序。
    (3)使用C51编译对上面程序进行编译,第5行指针p的地址与13行的变量sw绝不会是同一地址,因此第5行和第13行程序后面的说明是杜撰的假话
    (4)既然p的地址与sw绝不会是同一地址,第14行程序对sw的操作怎么会改变p呢?这是赤裸裸地伪造实验结果。
    (5)因此第1行的所谓实验发现根本不存在,还好意思问“这怎么回事?”,这就是你在造谣说“编译器给2个具有调用关系的函数的2个局部变量分配了相同的地址”这么回事!
    (6)第16行,“怎么会这样?!”,你造假了!
    (7)第16行,“怎么办?”,请你向大家道歉!






    回复 支持 反对 送花

    使用道具 举报

    该用户从未签到

    4

    主题

    52

    回帖

    160

    积分

    注册会员

    积分
    160
     楼主| 发表于 2023-12-29 14:07:45 | 显示全部楼层
    杨为民 发表于 2023-12-29 13:32
    请版主不要删这个帖子,我对我言论负法律责任。
    楼主,别言它了,那我就明说了:我56楼的帖子是指控你1楼 ...

    对事不对人,这样的讨论,我无比欢迎,本着科学而严谨的态度,真理是越辩越明的。

    1、1楼的程序,是没问题的,当时用函数名描述,是因为我匆忙且当时根本不知道函数名和函数指针的区别,所以10楼的程序改过来,用函数指针了。
    10楼的程序,才是有问题的。

    点评

    哥们,你能在1楼用一个 没有问题的程序 就预先得出 一天以后才理解的 在10楼的 有问题的程序 的结论,并且把它预先写出来,然后理直气壮地问大家“怎么会这样?!怎么办?” 而且连出问题的地址都一模一样,你真正  详情 回复 发表于 2023-12-29 17:30
    哈哈哈哈,不打自招了吧 (1)楼主说:“1楼的程序,是没问题的,当时用函数名描述,是因为我匆忙且当时根本不知道函数名和函数指针的区别,所以10楼的程序改过来,用函数指针了。10楼的程序,才是有问题的。” (2  详情 回复 发表于 2023-12-29 17:07
    回复 支持 反对 送花

    使用道具 举报

  • TA的每日心情
    慵懒
    前天 08:41
  • 签到天数: 134 天

    [LV.7]常住居民III

    20

    主题

    1039

    回帖

    2459

    积分

    金牌会员

    积分
    2459
    发表于 2023-12-29 14:09:50 | 显示全部楼层
    杨为民 发表于 2023-12-29 13:32
    请版主不要删这个帖子,我对我言论负法律责任。
    楼主,别言它了,那我就明说了:我56楼的帖子是指控你1楼 ...

    要不您看看10楼中 我点出他的问题后的代码?
    参考例程并不是对技术参 考手册的补充,而是对技术参 考手册的解释。
    技术参 考手册不应该需要参考例程作为补充,而是解释成了参考例程的样子
    回复 支持 反对 送花

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

    GMT+8, 2024-5-2 08:16 , Processed in 0.080422 second(s), 72 queries .

    Powered by Discuz! X3.5

    © 2001-2024 Discuz! Team.

    快速回复 返回顶部 返回列表