为了调试递归程序,我发现可视化函数调用的嵌套深度很有用。我很想拥有__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 了解了预定义的标识符他们是如此的酷!另外,无需重新发明轮子。
我在任何在线位置都找不到支持的预定义标识符的列表。我发现的只是this和this,它们都不是全面的。如果存在__NEST__
的等效词,那么这里的某人可能知道我要寻找的一个词。如果不存在,那么我在哪里可以找到记录良好的所有预定义标识符的列表?
答案 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