天天看點

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。

繼續閱讀