宏函数在项目开发中用的频率非常高,跟普通函数相比,它没有复杂的调用步骤,也不需要给形参分配空间,所以很多场景都需要宏函数的存在。
简单的宏函数确实也挺简单,比如这样的无参宏函数,在代码中凡是出现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)这种无效的循环,并且把它优化掉,不会造成效率上的问题。
审核编辑:汤梓红
电子发烧友App












评论