#define存在一些先天不足。在以下情形下要格外小心:
避免宏参数中的++与--操作。
带参数的宏定义形式上与函数很接近,程序员调用时有时会把它们视为等价。但实际define定义的伪函数相比真正函数有一些使用限制,比如:
#define max(A,B)((A)>(B)?(A):(B)) //看上去是想得到两个数中较大的
void main()
{
int i=1, j=2, x;
x = max(i++, j++);
printf("x = %d\n", x);
printf("i = %d\n", i);
printf("j = %d\n", j);
}
输出结果:x = 3 i = 2 j = 4。为什么不是x = 2, i = 2, j = 3? 因为预处理器对宏做文本替换后变成:x = ((i++) > (j++) ? (i++) : (j++)),其中j将被计算两次。因此用宏定义时,一定要小心操作数中的++ --,如果max是真正的函数,处理这种带++ --的参数没问题,但#define定义的伪函数却不行。
C库中的大小写转换函数toupper()也是一个典型例子,为减少函数调用开销,很多C库里toupper用宏实现:#define toupper(c) ((c) >= 'a' && (c) <= 'z' ? (c) + ('A' - 'a') : (c))。它确实比函数调用快,但一旦遇到toupper(*p++)这种调用方式,也会出现奇怪结果。
宏定义的括号漏洞
#define add(A,B) A+B
x = add(1,2) * 10;
结果x=21。为什么不是30?仔细看:add宏展开后表达式变成x = 1+2*10,定义里没用括号保护,展开后与预期功能不符,这种问题非常常见,再比如:
#define BLK_LEN 32 * 1024
block_num = bufsize / BLK_LEN;
替换后:block_num = stat_buf.st_size/32*1024;。所以宏定义里要多用括号,避免干扰。
不要用define代替typedef
#define pin (int*)
pin a,b;
本意a和b都定义为int指针,但替换后变成int* a,b;即a是int指针,而b是int变量。所以对这种类型重定义,应该用typedef,而不是不伦不类的define。
define多行定义续行符后不能有空格
define可定义多行代码,但续行符后不能有空格,否则会产生很多编译错误
#define MACRO(arg1, arg2) do { \
stmt1; \
\
} while(0)
这个定义有问题么?是的,只是每行结尾的空格在代码编辑器中一般看不到。一旦写完换行符’\’后大拇指习惯性一按,一个“隐形”错误就产生了。如果总提示多行define定义有错,又找不到问题时,先把换行符\后的空格清理一下。
此外,define语句后不能随便加;号:
#define MIN(A,B) ((A) <= (B) ? (A) : (B));
仔细看,行尾多了;号,#define不是C语句,不能随便加;号,否则替换时;号也会跟着。
总结:
define仅仅是代码替换,有时形似“函数定义”但不是真正的函数定义。另外define还会使调试不方便,所以建议用const量和inline函数替换#define。