背景:
在项目开发过程中,我们常常需要打印日志,比如:文件,函数,行数,编译时间等,这样方便我们调试,提升效率。但是在发布阶段,我们要去掉这些调试日志,这样我们要怎么做呢?
1.通过编译器的内置宏变量,对printf 库函数进行封装,变成自己的调试函数。
2.通过条件编译,去掉调试日志。
一、封装自己的函数
__FILE__
、__LINE__
、__FUNCTION__
这三个宏是编译器内置宏定义,分别代表调试信息所在文件、行号、函数。
除此之外,常用的宏还有:__DATE__
、__TIME__
,分别代表当前的编译日期与时间。
#define DBG_PRINTF(fmt, args...) \ do\ {\ printf("<<File:%s Line:%d Function:%s>> ", __FILE__, __LINE__, __FUNCTION__);\ printf(fmt, ##args);\ }while(0)
测试:
用法和printf一样。
二、关闭debug日志
利用条件编译进行关闭
#define DEBUG 1 #if DEBUG #define DBG_PRINTF(fmt, args...) \ do\ {\ printf("<<File:%s Line:%d Function:%s>> ", __FILE__, __LINE__, __FUNCTION__);\ printf(fmt, ##args);\ }while(0) #else #define DBG_PRINTF(fmt, args...) #endif
这种情况,就是利用条件编译,如果,没有定义DEBUG变量,就使DBG_PRINTF定义为空。这样在预处理的时候,就巧妙的去掉了debug日志。
同时,需要注意,DEBUG宏有没有在其他地方定义,会不会出现重复定义?这一点也需要注意。
三、关于#和##
注意到 printf(fmt, ##args); 了吗? 其中:##符号是为了处理args不代表任何参数的情况。如果:
当不加##符号时: DBG_PRINTF("Hello world"); 第二条语句会转化为:printf("Hello world\n", );
当加##符号时: DBG_PRINTF("Hello world"); 第二条语句会转化为:printf("Hello world\n");
前者比或者多了一个逗号,当然不是我们想要的。
3.1 #号
#号
作为一个预处理运算符
,可以把记号转换成字符串。
例如,如果A是一个宏形参,那么#A就是转换为字符串"A"的形参名。这个过程称为字符串化(stringizing)
。以下程序演示这个过程:
#define ADD(A,B) printf(#A "+" #B "=%d\n",((A)+(B))) ---> ADD(5,20); ---> 5+20=25
即:将一个宏参数转换成字符串。
3.2 ##号
##运算符可以把两个记号组合成一个记号。例:
即:将两个或者多个宏参数转换成字符串,之后连接在一起,然后返回。
3.3 #@
#todo
四、宏展开
使用编译器,单独执行宏展开的步骤,查看宏展开的结果。
gcc -E main.c -o main.i
main.i可以通过文本编辑器打开。