函数调用的嵌套程度是否有预定义标识符?

Is there a Predefined Identifier for how nested a function call is?

为了调试递归程序,我发现可视化我的函数调用的嵌套深度很有用。我很想拥有 __func__ 之类的东西,但我的堆栈跟踪有多深,而不是我的函数名称是什么。我知道编译器不可能简单地知道这一点,因为你的嵌套程度是一个动态生成的值。但是编译器向 implement 添加功能并不难,你可以简单地在每个 call 之前向全局计数器加 1 并在 eat [=] 之前减去 1 13=].


我正在使用以下调试语句(找到 here):

#define printdbg(Level, formatString, ...)                          \
    do {                                                            \
        fprintf(stderr, "%s:%d %s: " formatString "\n",             \
            __FILE__, __LINE__, __func__, ##__VA_ARGS__);           \
        if (Level == LEVEL_ERROR) { printf("quitting\n"); exit(1); }\
    } while (0)

我想在开始时添加一个额外的预定义标识符,我可以在其中利用 printf("%*s", __NEST__+1, ":") 形式的内容,以便在每次调试开始时打印总共 __NEST__ 个空格语句,让我想象每个调试语句在堆栈中的深度。


我知道我可以简单地在每个函数的开头有一个全局计数器,我 ++,最后 --,但我 只是 学习了预定义的标识符,它们太酷了!另外,无需重新发明轮子。

我无法在网上的任何地方找到受支持的预定义标识符列表。我只找到了 this and this,它们都声称不全面。如果 __NEST__ 的等价物存在,这里的某个人可能知道我要找的那个词。如果它不存在,那么我在哪里可以找到所有预定义标识符的详细记录列表?

C 不提供预定义的标识符来为您提供函数调用嵌套级别。如果 运行time 系统支持这样的标识符,它将为所有函数调用增加一些开销。开销将是每个人都会付出的代价,只有少数使用标识符的人会从中受益。这违背了 C 编程语言的精神,在 C 语言中,您只希望为您使用的功能付费。

在大多数当前的 CPU 体系结构和 C 编译器中,您可以通过查看局部变量的地址获得一个随着每次函数调用而增加的数字。这是一个例子。

#include <stdio.h>

// Base nesting level (must be initialized by main)
static char *main_nesting;

// Return an integer corresponding to the current function call nesting level
int
nesting_level(void)
{
    int a;
    return (main_nesting - (char *)&a);
}

void
nest3(void)
{
    printf("%s=%d\n", __func__, nesting_level());
}

void
nest2(void)
{
    printf("%s=%d\n", __func__, nesting_level());
    nest3();
}

void
nest1(void)
{
    printf("%s=%d\n", __func__, nesting_level());
    nest2();
}

int
main(int argc, char *argv[])
{
    main_nesting = (char *)&argc;
    printf("%s=%d\n", __func__, nesting_level());
    nest1();
}

当你运行这个程序时,你会得到如下输出。

main=20
nest1=68
nest2=116
nest3=164

§6.10.8 of the ISO/IEC 9899:2018 C standard 中指定了一组预定义的宏名称。此外,对于类似 GCC 的编译器,您可以通过 运行在 Unix 系统上执行以下命令来获取所有预定义宏的列表。

cpp -dM /dev/null