c语言系统默认宏__VA_ARGS__、__LINE__等用于高效Debug

来源:互联网 发布:淘宝新规则2016及处罚 编辑:程序博客网 时间:2024/06/15 22:08

对于涉及多文件较大规模的程序员而言,在不同地方设置printf()用于查看代码运行进展以及查看距离故障点最近位置是很有用的,但是手动地在系统中插入一个个printf(...),最终版一个个删显然太蠢了。这里介绍一个成体系的Debug函数组。

#ifdef _DEBUG#define DEBUG(...) fprintf(stderr, __VA_ARGS__ )  #else#define   DEBUG(format,...)#endif

这样在编译时cl /D _DEBUG...便是启动这些printf()函数,不定义_DEBUG便是取消Debug功能。但是这里面的__VA_ARGS__又是些什么东西?
__VA_ARGS__可变参数宏,使用场景:#define PRINT_ERROR(…) fprintf(stderr, VA_ARGS)
在C89年代,可变参数stdarg机理依旧只能使用在函数中,如经典的
int printf(const char* format, ...);
直到C99才允许定义可变参数宏,如上面PRINT_ERROR(...)中的 ... 代表着这是一个可以变化的参数表,使用保留名__VA_ARGS__把参数传给宏函数。

对于GCC采用ANSI标准而言,其支持的方式更为直观:
#define PRINT_ERROR(args...) fprintf(stderr, args)
即其支持为可变参数序列起一个名称,并在后续的宏函数中直接使用代替,而不是项上面那样一直使用__VA_ARGS__这一系统默认的伪宏。

还有可以提供更为详细位置信息的其他宏吗?是的,看下面。

#include <stdarg.h>#include <stdio.h>#define DEBUG(...)  printf(__VA_ARGS__)#define XNAME(n)    x##n#define PXN(n)      printf("x"#n" = %d", x##n)   //这里面古怪的"#n"中的双引号并没有用\转义,其实是因为这个部分标记并非是给程序运行段查看的//这个"#n"是给编译器的预编译器看的,告诉它这部分内容是需要替换的宏参数,需要先进行宏参数转换操作//比如如下这个更夸张的操作方式更是表明了""囊括的的部分会先作为宏展开的部分进行参数解读//#define DEBUG(format,...) printf("File: "__FILE__", Line: %05d: "format"/n", __LINE__, ##__VA_ARGS__)  int main(){    DEBUG("当前源代码函数名:__FUNCTION__ = %s \n", __FUNCTION__ );    DEBUG("当前源代码行数:__LINE__ = %d \n", __LINE__ );    DEBUG("当前源代码文件名:__FILE__ = %s \n", __FILE__ );    DEBUG("当前编译日期〔注意和当前系统日期区别开来〕:__DATE__==%s\n", __DATE__);    DEBUG("当前编译时间〔注意和当前系统日期区别开来〕:__TIME__==%s\n", __TIME__);    DEBUG("当前系统时间戳:__TIMESTAMP__==%s\n", __TIMESTAMP__);    //DEBUG("当要求程序严格遵循ANSIC标准时该标识符被赋值为1:__STDC__==%d\n", __STDC__);    DEBUG("当用C++编译程序编译时,标识符__cplusplus就会被定义:__cplusplus==%d\n", __cplusplus);    int XNAME(1) = 12;    PXN(1);    return 0;}

这样便可以定义出最终版的Debug的组合函数

#define PRINT_ERROR(...) fprintf(stderr, __VA_ARGS__)#define MY_ASSERT(value)  if(!(value)) { PRINT_ERROR("\"%s\" failed at %s:%d %s \n",\                                                         #value,__FILE__,__LINE__,__FUNCTION__);}#define SHOW_LOCA()     PRINT_ERROR(" current line_NO = %d \n", __LINE__ );static FILE*   g_out_file = NULL; //甚至还可以定义Log登陆文件信息,配合如下函数实现信息保存static   void InitLog( const char* file_name ){#ifdef BINDINGS_LOG    MY_ASSERT( file_name );    if ( g_out_file == NULL )    {        g_out_file = fopen( file_name, "w" );    }    if ( g_out_file == NULL )    {        printf( "Create %s failed\n", file_name );    }    printf( "Bindings were written into %s, please check it for details.\n", file_name );#endif    return ;}