【发布时间】:2015-06-08 21:05:19
【问题描述】:
我尝试使用 GCC 中的函数 __builtin_return_address 来学习一些关于运行时数据结构的基本概念。
我的测试代码是这样的
#include <stdio.h>
void a(int i)
{
if (i>0) {
printf("The return address is %p\n", __builtin_return_address(0) );
a(--i);
}
else
return;
}
int main ()
{
a(10);
return 0;
}
输出是
The return address is 0x4005ee
The return address is 0x4005db
The return address is 0x4005db
The return address is 0x4005db
The return address is 0x4005db
...
所以我的问题是,为什么那些递归调用的函数的返回地址都一样,就像它们都返回到顶级调用者一样?它们不应该也是“递归直接调用者”吗?
【问题讨论】:
-
因为虽然每个函数调用的帧在堆栈上越来越低,但函数调用的每个实例都在执行相同的代码,并且返回地址是函数
a()的代码 中的指令紧跟call指令到a()本身。要查看稳定减少的地址,请在函数框架中打印局部变量的地址,例如&i。 -
这看起来像是尾递归优化。尝试在关闭所有优化的情况下编译代码 - 例如使用
-O0,这可能会阻止优化发生,从而产生您期望的结果。 -
@Peng 你注意到
<- ESPcmets 了吗? ESP 是 IA32 架构中的堆栈指针寄存器,它是 it 正在增长的。幻灯片中的“位置”不是返回地址;它是堆栈上返回地址的位置。在幻灯片 6 中发现 atLocation 000FFFF8和Location 000FFFF4的返回地址指针将是相同的,因为它们都是“经过f调用的点f" 在函数f的机器代码中。 -
@IwillnotexistIdonotexist,好吧,我想我明白了。夸张地说,我在 main 中手动调用了 a(10) 两次。正如您所说,“返回地址”实际上是紧跟在 objdump 的 a() 部分中的“调用”指令之后的下一条指令。无论如何以及何时调用 a()。正如您在上一篇文章中清晰的解释,我现在知道我混淆了 the 返回地址指令和它实际指向的地址,就像将指针与其指向的值混淆了一样。太感谢了。 :)
-
@IwillnotexistIdonotexist 再次感谢您的解释。 :) 我想我实际上是在寻找“取消引用”返回地址的方法,或者换句话说,在堆栈上找到“返回地址”。再次感谢。
标签: gcc recursion stack return