1

为了调试递归程序,我发现可视化我的函数调用的嵌套深度很有用。我很想有类似的东西__func__,但是我的堆栈跟踪有多深,而不是我的函数名是什么。我知道编译器不可能简单地知道这一点,因为您的嵌套程度是一个动态生成的值。但是编译器添加功能来实现这一点并不难,您可以简单地在 each 之前将 1 添加到全局计数器,然后call在 eat 之前减去 1 ret


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

#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__制成。


我知道我可以简单地++在每个函数的开头和--结尾都有一个全局计数器,但我刚刚了解了预定义的标识符,它们太酷了!此外,无需重新发明轮子。

我在网上的任何地方都找不到受支持的预定义标识符列表。我发现的只是thisthis,两者都没有声称是全面的。如果存在等价物__NEST__,那么这里的某个人可能知道我正在寻找的一个词。如果它不存在,那么我在哪里可以找到所有预定义标识符的详细记录列表?

4

1 回答 1

1

C 不提供预定义的标识符来为您提供函数调用嵌套级别。如果运行时系统支持这样的标识符,它将为所有函数调用增加一些开销。开销将是每个人都会付出的代价,只有少数使用标识符的人会从中受益。这与 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

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

cpp -dM /dev/null
于 2018-09-09T14:41:13.250 回答