天天看点

C陷阱篇之define的缺陷

#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。

继续阅读