【问题标题】:Can you determine stack depth in C?你能确定 C 中的堆栈深度吗?
【发布时间】:2019-12-29 23:22:38
【问题描述】:

我想知道 C 中是否有一个函数(我们称之为 int get_stack_depth()),它返回当前在堆栈上执行的函数的数量。例如:

int foo(){
    return get_stack_depth();
}

int bar2(){
    return get_stack_depth();
}

int bar1(){
    return bar2();
}

int bar(){
    return bar1();
}

int main(){
    get_stack_depth();      // = 0
    foo();                  // = 1
    bar();                  // = 3
    return 0;
}

我想将它用于调试信息,其中每个 printf 都将包含 get_stack_depth() 缩进以增加可读性。如果这是依赖于编译器,或者依赖于其他任何东西,我会接受所有约束;现在我想知道这是否至少在某个地方得到支持。

编辑:建议副本的答案根本没有帮助我,正如此处接受的答案所暗示的那样,您无法纯粹根据堆栈的大小来确定堆栈上有多少个函数;信息根本不存在。

【问题讨论】:

  • 这能回答你的问题吗? How to find stack depth?
  • 对于您定义的每个函数,您可以声明一个对您定义的所有函数可见的文件范围变量,并在函数进入时增加它并在从函数返回之前减少它。这是一个丑陋的解决方案,但我认为除此之外不能以便携的方式完成。
  • 你可以看到 glibc 为你提供了什么here;没有界面可以准确地告诉您堆栈的深度,但是如果您有上限backtrace 将报告有多少插槽正在使用中。请注意,并非所有函数调用都使用堆栈帧,如文档所示。
  • @rici 谢谢你的链接,我一定会试试的。

标签: c stack printf size


【解决方案1】:

C 中堆栈的确切机制是特定于实现的,因此没有单一、正确、标准的方法来查找堆栈的深度。不过,有一些方法可以模拟这种行为。

  1. 使用计数器。定义一个全局的unsigned depth,在每个关心栈深度的函数中,在开头注入depth++,在结尾注入depth--。这显然是比较繁琐的方法,如果不增加或减少,很容易出现很多令人沮丧的问题。

  2. 检查堆栈指针。在 x86 系统(几乎每个台式机和笔记本电脑设备)上,堆栈向下增长,这意味着进入函数调用将减小堆栈指针的值。在许多情况下(但不是全部,例如启用优化时)堆栈指针寄存器%rsp 指向当前函数堆栈帧的“顶部”。获取此值的一种相当老套的方法是将其分配给一个变量:register uint64_t rsp asm ("rsp");。值越低,堆栈的深度越大。

    不幸的是,函数调用之间递减的大小取决于该函数的堆栈帧有多大——如果一个函数将一个大数组声明为局部变量,那么它调用的函数的堆栈指针会低得多,因为数组占用了更多空间。

最终,我知道找到函数调用的准确回溯的唯一可靠方法是在诸如gdb 之类的调试器中运行程序并发出backtrace 命令,该命令将打印当前调用堆栈。当程序独立于任何调试器运行时,似乎无法获得这种支持。

【讨论】:

  • 好吧,好像是反例。感谢您的澄清。
【解决方案2】:

您是否尝试过使用backtrace()? 例如:

#include <execinfo.h>
unsighed int getDepth(){
    const unsigned int max_depth = 200;
    void* buffer[max_depth];
    return backtrace(buffer, max_depth)-5;
}

-5 之所以存在,是因为我想忽略main(由 libc 添加)之上的一些函数。但是,如果您想自动计算,请将我的函数更改为

int get_stack_depth(int set_caller_as_root){
    static int root_depth = 0;

    if (set_caller_as_root){
        root_depth = get_stack_depth(0) - 1;
    }

    const int max_depth = 200;
    void* buffer[max_depth];
    return backtrace(buffer, max_depth) - root_depth;
}

并将您的代码更新为

int foo(){
    return get_stack_depth(0);
}

int bar2(){
    return get_stack_depth(0);
}

int bar1(){
    return bar2();
}

int bar(){
    return bar1();
}

int main(){
    printf("%d\n", get_stack_depth(1));     // = 0
    printf("%d\n", foo());                  // = 1
    printf("%d\n", bar());                  // = 3
    return 0;
}

这会产生正确的预期结果。

注意execinfo.h默认没有安装在windows 10上(它是在linux上)。

【讨论】:

  • 感谢您提请我注意。这个问题已经过时了,但这绝对是更好的解决方案。
猜你喜欢
  • 2014-06-19
  • 1970-01-01
  • 2012-01-13
  • 2015-12-17
  • 2010-12-12
  • 1970-01-01
  • 2010-09-27
  • 2011-05-29
  • 2015-08-13
相关资源
最近更新 更多