查看: 1572|回复: 0
打印 上一主题 下一主题

C51中 return语句在main()函数中的副作用追踪

[复制链接]
跳转到指定楼层
沙发
发表于 2016-4-15 19:40:46 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
先说结论:
1、        C51中mian()并不是被调用的,而是跳转进去的;
2、        在main()中调用return语句,将会从栈中弹出SP大小的数据到SP中,进而程序会运行到弹出数据指向的地址,C51中可能就是SP指针返回0x0000。

    昨天和我合作一个工程的同事在调试的时候,发现单片机程序会经常出现重启的现象,当时我认为可能是数组溢出、指针越界导致的程序跑飞。
    后来同事将有问题的语句定位到某个分支中的“return 0;”语句上,我一看这居然是在main()函数上使用了“return 0;”,认为程序会在调用这个语句后,
PC指针离开main(),继续执行main()函数后面的代码导致程序跑飞(同事估计是受到VC等PC编译环境的影响,汗~)。
    不过,回到坐位后却想起return对应的指令,会负责将栈中的值取出来赋值给PC指针,这样的话执行“return 0;”后应该返回到调用main()函数的地方。
    既然有疑问就动手吧,首先向同事拿到了出问题的工程,在main()开始的地方设置了两个“return 0;”(分别是第25行和32行)。
    然后进入仿真模式,如下图,可以观察到sp指针指向起始值0x07,PC指针指向0x0000,PC指针的长度为2。

图1 请无视main()里面的那个void~~
    在disassembly界面单步跟踪,发现了跳转到main()的地方,并且可以看到sp是被赋值为0x28的。(不过这LJMP只是跳转而已,看来没有调用main()的过程)

图2
    全速运行到图1中0x0003的断点后,看到的寄存器如下:

图3
    sp指针在之前已经被设置成0x28了,这时候如果有ret语句被执行的话,0x27和0x28地址RAM上的数据应该就是会被弹出到PC指针上的。
    现在观察下RAM中的情况:

图4
    不过因为这两个地址(0x27和0x28)上的数据都是0,所以单步可以发现执行反汇编窗口0x0008地址上的RET语句后PC指针又跑回0x0000上了,这就是重启的原因:

图5 单步到0x0008,黄色的箭头是PC指针

图6 指针返回到了0x0000,并且sp指针退了两格
    下面为了证明,执行RET指令后,SP指针指向的数据是退出函数后的位置,再加个实验,首先将图1中第25行的“return 0;”注释掉。

图7 即将调用MCU_Init()
    运行到调用MCU_Init()这步上,可以看到PC指针为0x02EF,而退出后应该是运行到0x02F2上,单步进入到MCU_Init()后,0x29和0x2A果然被写入了0x02F2(进入函数,SP首先会将PC
压入栈,PC指针自然是在0x29和0x2A上),并且可以看出SP指针的低位是先被压入栈的~

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 加入因仑

本版积分规则

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