你能确定 C 中的堆栈深度吗?

Can you determine stack depth in C?

我想知道 C 中是否有一个函数(让我们将其命名为 int get_stack_depth()),returns 当前函数的数量在栈上执行。例如:

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() 缩进以提高可读性。如果这是编译器相关的,或者任何其他相关的,我会接受所有的约束;现在我想知道这是否至少在某个地方得到支持。

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

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

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

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

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

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

您尝试过使用 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 上)。