编码技巧---调试宏
背景:
在项目开发过程中,我们常常需要打印日志,比如:文件,函数,行数,编译时间等,这样方便我们调试,提升效率。但是在发布阶段,我们要去掉这些调试日志,这样我们要怎么做呢?
1.通过编译器的内置宏变量,对printf 库函数进行封装,变成自己的调试函数。
2.通过条件编译,去掉调试日志。
一、封装自己的函数
1.1 常规写法
__FILE__
、__LINE__
、__FUNCTION__
这三个宏是编译器内置宏定义,分别代表调试信息所在文件、行号、函数。
除此之外,常用的宏还有:__DATE__
、__TIME__
,分别代表当前的编译日期与时间。
1 2 3 4 5 6 | #define DBG_PRINTF(fmt, args...) \ do \ {\ printf ( "<<File:%s Line:%d Function:%s>> " , __FILE__, __LINE__, __FUNCTION__);\ printf (fmt, ##args);\ } while (0) |
测试:
用法和printf一样。
1.2 可变参数宏
#define debugPrintf(...) printf("DEBUG: " __VA_ARGS__); ---> debugPrintf("Hello World!\n"); ---> DEBUG:Hello World!
简单来说,...表示所有剩下的参数,__VA_ARGS__被宏定义中的...参数所替换。这在c语言的GNU扩展语法里是一个特殊规则:当__VA_ARGS__为空时,会消除前面这个逗号。
这点主要用于printf函数,其他函数呢?是否可以用?(待后面实验之后,再补充)
二、关闭debug日志
利用条件编译进行关闭
1 2 3 4 5 6 7 8 9 10 11 12 | #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可以通过文本编辑器打开。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具