找回密码
 立即注册
楼主: zhangzhonghua

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

[复制链接]
  • 打卡等级:以坛为家II
  • 打卡总天数:435
  • 最近打卡:2025-04-30 08:43:23

33

主题

2351

回帖

4860

积分

论坛元老

积分
4860
发表于 2023-12-27 12:30:13 | 显示全部楼层
zxcv*** 发表于 2023-12-27 12:24
他用的是STC32吧,不用模拟栈,性能损失可忽略。这种函数指针动态调用函数,想治本就得用可重入函数 ...

而C51里 维护调用树是治本,懒一点儿就不用这个功能就算了
参考例程并不是对技术参 考手册的补充,而是对技术参 考手册的解释。
技术参 考手册不应该需要参考例程作为补充,而是解释成了参考例程的样子
回复 支持 反对

使用道具 举报 送花

4

主题

52

回帖

160

积分

注册会员

积分
160
发表于 2023-12-27 12:50:13 | 显示全部楼层
_奶*** 发表于 2023-12-27 12:09
中间用英文逗号分隔,,,
也不知道你百度了点啥,,哪个贴子教你这个东西要填在上面的,,,,直接喷那个 ...

确实是这个放错位置了,按你说的,就链接正常了。
回复 支持 反对

使用道具 举报 送花

4

主题

52

回帖

160

积分

注册会员

积分
160
发表于 2023-12-27 12:51:51 | 显示全部楼层
_奶*** 发表于 2023-12-27 12:28
啊 人用的C51 编译器 你是从那里看出来他用的STC32 ?

STC8H8K64U芯片,Lx51链接器。
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:435
  • 最近打卡:2025-04-30 08:43:23

33

主题

2351

回帖

4860

积分

论坛元老

积分
4860
发表于 2023-12-27 13:02:54 | 显示全部楼层
zhangzh*** 发表于 2023-12-27 12:50
确实是这个放错位置了,按你说的,就链接正常了。

恩 你可以再看看 变量的地址 应该就不一样了,,
参考例程并不是对技术参 考手册的补充,而是对技术参 考手册的解释。
技术参 考手册不应该需要参考例程作为补充,而是解释成了参考例程的样子
回复 支持 反对

使用道具 举报 送花

4

主题

52

回帖

160

积分

注册会员

积分
160
发表于 2023-12-27 13:08:18 | 显示全部楼层
zxcv*** 发表于 2023-12-27 12:24
他用的是STC32吧,不用模拟栈,性能损失可忽略。这种函数指针动态调用函数,想治本就得用可重入函数 ...

我的看法:
1、可重入函数,根本出发点是解决循环调用、多入口(如main + 中断服务程序)调用的问题,方法是每次调用都生成栈帧,还要用reentrant这个非标准C的扩展关键字。
2、而我这个问题,根本原因是用了函数指针调用,而编译器不能正确理出调用树,又要Overlay去节省空间,结果搞得调用函数和被调用函数的局部变量地址重叠了。
3、所以,最合适的,应该是维护调用树,其次,是不进行Overlay。我接下来都试下,对比效果。

点评

偶看好你哟~(老邢摊手  发表于 2023-12-27 13:41
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:413
  • 最近打卡:2025-05-01 08:31:52

5

主题

296

回帖

2030

积分

金牌会员

积分
2030
发表于 2023-12-27 14:27:08 | 显示全部楼层
估计是优化的问题
回复 支持 反对

使用道具 举报 送花

2

主题

185

回帖

1048

积分

金牌会员

积分
1048
发表于 2023-12-27 15:08:52 | 显示全部楼层
zhangzh*** 发表于 2023-12-27 13:08
我的看法:
1、可重入函数,根本出发点是解决循环调用、多入口(如main + 中断服务程序)调用的问题,方 ...

STC8不熟,要是STC32的话,最省事的解决办法就是所有函数都为可重入函数(工程配置里有一个选项勾上即可),可避免很多意想不到的BUG
回复 支持 反对

使用道具 举报 送花

4

主题

52

回帖

160

积分

注册会员

积分
160
发表于 2023-12-27 16:21:41 | 显示全部楼层
本帖最后由 zhangzh*** 于 2023-12-28 11:08 编辑
zhangzhonghua 发表于 2023-12-27 13:08
我的看法:
1、可重入函数,根本出发点是解决循环调用、多入口(如main + 中断服务程序)调用的问题,方 ...

大功告成,来汇报下结果。
overlay对比.PNG
1、出错时的情况:Program Size: data=10.2 xdata=633 const=1084 code=13940,
问题:因为使用了函数指针,导致编译器不能识别这种调用关系,又要做Overlay,所以调用函数和被调函数的局部变量地址重叠,意外修改了。

2、不做Overlay的情况:Program Size: data=16.3 xdata=980 const=1116 code=18385,
优劣:调用函数和被调函数的局部变量地址不会重叠了。但未用代码没移除,占空间。xdata,const增加几百字节,而code增加了几千字节。

3、手工维护调用树的情况:Program Size: data=10.2 xdata=633 const=1084 code=13940,
优劣:调用函数和被调函数的局部变量地址不会重叠了,且空间占用和情况1时一样少。但需手工维护调用树,好在只需增加函数指针调用部分即可。这个是完美解决方案。

12.28补充:
4、C51编译器优化级别设为0的情况:Program Size: data=10.2 xdata=797 const=1084 code=16126
优劣:比情况3多占一点空间,xdata增加164字节,code增加2千多字节。

overlay对比.PNG

点评

三论 楼主38楼实验结果造假 (1)从11楼起到37楼,楼主和网友们交流了在C51编译器中用维护函数调用树的方法 来解决由于OVERLAY产生的局部变量地址重合的问题 的方法。最后楼主在38楼宣告“大功告成”的实验结果,还  详情 回复 发表于 2023-12-30 20:57
回复 支持 反对

使用道具 举报 送花

4

主题

52

回帖

160

积分

注册会员

积分
160
发表于 2023-12-27 16:31:21 | 显示全部楼层
本帖最后由 zhangzhonghua 于 2023-12-27 16:40 编辑

怎么维护调用树?
原来以为麻烦,后来一看,挺简单的。
比如processEvent()通过函数指针调用了proc_Up()、proc_Down(),编译器识别不出来这种调用关系,那么你告诉编译器就好了,
在Overlay的框里加  processEvent ! proc_Up,processEvent ! proc_Down
这样,Lx51命令行会出 OVERLAY(processEvent ! proc_Up,processEvent ! proc_Down)指示,链接器就能正确做Overlay了。
回复 支持 反对

使用道具 举报 送花

4

主题

52

回帖

160

积分

注册会员

积分
160
发表于 2023-12-27 17:34:54 | 显示全部楼层
本帖最后由 zhangzhonghua 于 2023-12-28 12:01 编辑

怎么观察具体修改效果的?

看链接器生成的MAP文件。MAP文件主要5个部分。
1、ACTIVE MEMORY CLASSES,例如
BASE        START       END         USED      MEMORY CLASS
==========================================================
C:000000H   C:000000H   C:00FFFFH   003674H   CODE
X:000000H   X:000000H   X:00FFFFH   000279H   XDATA
C:000000H   C:000000H   C:00FFFFH   00043CH   CONST
I:000020H.0 I:000020H.0 I:00002FH.7 000001H.2 BIT
I:000000H   I:000000H   I:00007FH   000008H   DATA
I:000000H   I:000000H   I:0000FFH   000001H   IDATA
这里,把code、xdata、const、data等区域的位置和大小都列出来了。

2、MEMORY MAP,又分成4小部分,
(1)  D A T A   M E M O R Y,这里是data区域的具体分配。
(2)  C O D E   M E M O R Y,这里是code区域的具体分配。
(3)X D A T A   M E M O R Y,这里是xdata区域的具体分配。
(4)  R E M O V E D     S E G M E N T S,这里是移除的段,REMOVEUNUSED的效果怎样,看这里。
和Overlay相关的是X D A T A   M E M O R Y里面,有一段名字叫_XDATA_GROUP_,例如,
00021DH   000241H   000025H   BYTE   UNIT     XDATA          _XDATA_GROUP_
这是什么?
就是编译器给函数局部变量安排的地方!局部变量都在这个区域里。怎么用的?看下面的OVERLAY MAP。

3、OVERLAY MAP,例如
TIMER71MSPROCESS/MAIN                      0228H 0234H
  +--> _PROCESSEVENT/GD
这里,说明给Timer71mSProcess()函数,分配了0228H到0234H的地址(_XDATA_GROUP_的一部分)用于其局部变量,另外调用了processEvent()函数。
_PROCESSEVENT/GD                           0235H 023BH
  +--> _PROC_UP/GD
  +--> _PROC_DOWN/GD
这里,说明给processEvent()函数,分配了0235H到023BH的地址(也是_XDATA_GROUP_的一部分)用于其局部变量,另外调用了proc_Up()、proc_Down()函数。

这里要重点注意了:Timer71mSProcess()的局部变量地址尾巴是0234H,其调用了processEvent(),而processEvent()的局部变量地址头是0235H,正好接到Timer71mSProcess()的尾巴上。
说明了,(1)调用函数和被调函数,他们的局部变量地址是不重叠的!(2)被调函数的局部变量地址在调用函数的后面增长。
咦,好熟悉,这做法和PC的函数Stack Frame栈帧不是很像吗?
0228H~0234H,不就是Timer71mSProcess()的Stack Frame吗?
0235H~023BH,不就是processEvent()的Stack Frame吗?
就是的,所以,Keil C51把这个做法叫做“编译时栈帧”,相对的,我们通常PC的叫做“运行时栈帧”,
因为,C51这种栈帧,是编译时就确定的,而PC的栈帧,是运行时才确定的。

4、PUBLIC SYMBOLS,公共符号表。也能看到REMOVEUNUSED的效果。
5、SYMBOL TABLE,各模块的符号表,包括私有的。

上面说的栈帧,地址并不重叠,那么,Overlay怎么体现呢?
还是看OVERLAY MAP,例如
TIMER1SPROCESS/MAIN                        0228H 0233H
  +--> _PROCESSEVENT/GD
这里,说明给Timer1SProcess()函数,分配了0228H到0233H的地址(也是_XDATA_GROUP_的一部分)用于其局部变量,另外调用了processEvent()函数。
对比下,
Timer1SProcess()函数,栈帧是0228H~0233H,
Timer71mSProcess()函数,栈帧是0228H~0234H,
它们的栈帧地址是重叠的,都是0228H开头,编译器不给这2个函数分配不重叠地址的栈帧,节省了空间,这就是Overlay。
那么,不会出现意外修改问题吗?
不会,因为它们之间没有调用关系!
所以,总结:
(1)没有调用关系的函数,其栈帧可能重叠即Overlay,目的是节省空间。
(2)如果有调用关系的函数,其栈帧不能重叠,否则局部变量会被意外修改。
(3)如果调用关系是用函数指针实现的,编译器不能识别,需手工维护调用树。

等一下,Timer1SProcess()和Timer71mSProcess()的栈帧地址重叠,条件是它们之间没有调用关系。
可是,如果不同的入口分支(如main和中断服务程序),分别调用了这2个函数,由于地址重叠,还是会出现意外修改的情况啊?
这好像就是重入函数的题目了。
12.28补充:再想想,似乎多虑了,没到重入函数的时候。现在Timer1SProcess()和Timer71mSProcess()的栈帧地址重叠,是因为它们都是由main()调用,如果把其中1个改到中断服务程序去调用,那么调用关系就会改变,编译器识别得出来,会重新构建调用树,根据新调用树重新分配栈帧地址,不会重叠吧?


回复 支持 反对

使用道具 举报 送花

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

本版积分规则

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

GMT+8, 2025-5-2 00:28 , Processed in 0.124304 second(s), 106 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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