如何递归使用 __builtin_return_address()?

Posted

技术标签:

【中文标题】如何递归使用 __builtin_return_address()?【英文标题】:How to use __builtin_return_address() recursively? 【发布时间】: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() 本身。要查看稳定减少的地址,请在函数框架中打印局部变量的地址,例如 &amp;i 这看起来像是尾递归优化。尝试在关闭所有优化的情况下编译代码 - 例如使用-O0,这可能会阻止优化发生,从而产生您期望的结果。 @Peng 你注意到&lt;- ESP cmets 了吗? ESP 是 IA32 架构中的堆栈指针寄存器,它是 it 正在增长的。幻灯片中的“位置”不是返回地址;它是堆栈上返回地址的位置。在幻灯片 6 中发现 at Location 000FFFF8Location 000FFFF4 的返回地址指针将是相同的,因为它们都是“经过f 调用的点f" 在函数 f 的机器代码中。 @IwillnotexistIdonotexist,好吧,我想我明白了。夸张地说,我在 main 中手动调用了 a(10) 两次。正如您所说,“返回地址”实际上是紧跟在 objdump 的 a() 部分中的“调用”指令之后的下一条指令。无论如何以及何时调用 a()。正如您在上一篇文章中清晰的解释,我现在知道我混淆了 the 返回地址指令和它实际指向的地址,就像将指针与其指向的值混淆了一样。太感谢了。 :) @IwillnotexistIdonotexist 再次感谢您的解释。 :) 我想我实际上是在寻找“取消引用”返回地址的方法,或者换句话说,在堆栈上找到“返回地址”。再次感谢。 【参考方案1】:
f5 f4 f3 f2 f1 a(5) -->  f5
f5 f4 f3 f2 f1 a(5) a(4) -->  f3
f5 f4 f3 f2 f1 a(5) a(4) a(3) -->  f1
f5 f4 f3 f2 f1 a(5) a(4) a(3) a(2) -->  a(4)
f5 f4 f3 f2 f1 a(5) a(4) a(3) a(2) a(1) -->  a(2)

递归期间不要更改索引!

f5 f4 f3 f2 f1 a(5) -->  f5
f5 f4 f3 f2 f1 a(5) a(5) -->  f4

停在ret_add(i) == ret_add(0)

【讨论】:

避免不安全的递归,这里更喜欢 for while/loop ! while(i&gt;0) printf("The return address is %p\n", __builtin_return_address(i--));

以上是关于如何递归使用 __builtin_return_address()?的主要内容,如果未能解决你的问题,请参考以下文章

如何递归获取 python 包中的所有子模块?

实体如何继承递归模型结构中的属性?

如何使用append / 3以递归方式在prolog中构建列表?

我不明白如何递归地编写函数

如何在keras中递归扩展/解析/展平嵌套模型?

Vue3_15(全局组件,局部组件,递归组件)