宏函数在项目开发中用的频率非常高,跟普通函数相比,它没有复杂的调用步骤,也不需要给形参分配空间,所以很多场景都需要宏函数的存在。
简单的宏函数确实也挺简单,比如这样的无参宏函数,在代码中凡是出现debug的地方,都会替换成printf这条输出语句。
#define debug printf("helloworld ") void main() { debug; // 等价于 printf("helloworld"); }复杂一点的,在宏函数中加个参数,我们把它称作有参宏函数,比如这样的:
#define debug(s) printf("%s ", s) void main() { debug("helloworld "); }调用debug的时候,需要传个参数进去,当然这个参数必须是字符串,如果随便写个数字,运行的时候就是段错误。
#define debug(s) printf("%s ", s) void main() { debug(1); //段错误 }这也把宏函数的缺点暴露了出来,参数没有类型限制,不够安全。 再回到文章刚开始的地方。
这个宏函数不仅有参,而且还是可变参数,在代码中凡是出现debug的地方,都把他替换成fprintf。 唯一不太好懂的地方,可能是args前面出现了两个井号。 两个井号在C语言中被称为连接符号,功能就是在带参的宏函数中将两个字串连接成一个新的字符串。 举个例子,有这样一个宏函数:
#define name(x) name_##x void main() { int name_1, name_2; name(1); // 等价于 name_1; }如果调用的时候参数传入1,就被替换成了name_1。 在可变参数中,两个井号就是把所有参数连接在后面。 宏函数的使用场景很多,就拿图上这个来说,可以实现项目开发的时候打开调试信息,方便调试代码。项目完成后关闭调试信息。我们来个测试代码。
#include在主函数中调用debug函数,如果你希望debug函数执行,编译的时候提供DEBUG宏定义就行。#ifdef DEBUG #define debug(format, args...) fprintf(stderr, format, ##args) #else #define debug(format, args...) #endif int main() { int a = 1; debug("a = %d ", a); return 0; }
gcc test.c -o test -DDEBUG如果你不希望信息输出,编译的时候就不要管它。
gcc test.c -o test这个方法比项目完成后,一行一行去删除调试信息来的更方便。 如果你看过一些开源代码,肯定会发现很多宏定义中使用do while语句。
#define NS_GET16(s, cp) do { const unsigned char *t_cp = (const unsigned char *)(cp); (s) = ((uint16_t)t_cp[0] << 8) | ((uint16_t)t_cp[1]) ; (cp) += NS_INT16SZ; } while (0) #define NS_GET32(l, cp) do { const unsigned char *t_cp = (const unsigned char *)(cp); (l) = ((uint32_t)t_cp[0] << 24) | ((uint32_t)t_cp[1] << 16) | ((uint32_t)t_cp[2] << 8) | ((uint32_t)t_cp[3]) ; (cp) += NS_INT32SZ; } while (0)虽然看不懂,但还是觉得这段代码写的非常厉害,那你知道为什么要这样写吗? 1.可以避免空的宏定义出现warning。
#define foo()
有些编译器对这样的代码会提示警告,do while可以消除警告。
#define foo() do {}while(0)
2.作为一个独立的单元,可以定义变量或者是进行更复杂的运算。
#define debug do { int a; printf("helloworld"); foo(); }while (0)
3.放在判断语句中,避免语法错误。
#include#define debug(num) num--; printf("%d ", num) int main() { int num = 1; if (num > 0) debug(num); else printf("error "); return 0; }
这个代码编译的时候会提示语法错误,因为宏定义中包含了两条语句,同时判断语句中又没有使用大括号。do while可以解决这个问题。
#include#define debug(num) do {num--; printf("%d ", num);} while(0) int main() { int num = 1; if (num > 0) debug(num); else printf("error "); return 0; }
总结一下,do while可以把复杂的语句包裹起来,使它成为一个单独的单元,避免语法问题。而且大部分的编译器都能识别while(0)这种无效的循环,并且把它优化掉,不会造成效率上的问题。
审核编辑:汤梓红
评论
查看更多