是否有预定义标识符来说明函数调用的嵌套方式?

时间:2018-09-04 17:56:50

标签: debugging macros predefined-variables

为了调试递归程序,我发现可视化函数调用的嵌套深度很有用。我很想拥有__func__之类的东西,但是对于我的堆栈跟踪的深度而不是我的函数名称的深度。我了解编译器不可能仅知道这一点,因为您的嵌套方式是动态生成的值。但是对于编译器来说,向实现添加功能并不困难,您可以在每个call之前将1添加到全局计数器,然后在食用ret之前减去1。


我正在使用以下调试语句(找到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__个空格,让我直观地看到每个调试语句从多深处进入堆栈。


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

我在任何在线位置都找不到支持的预定义标识符的列表。我发现的只是thisthis,它们都不是全面的。如果存在__NEST__的等效词,那么这里的某人可能知道我要寻找的一个词。如果不存在,那么我在哪里可以找到记录良好的所有预定义标识符的列表?

1 个答案:

答案 0 :(得分: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

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

cpp -dM /dev/null