奇怪的宏
這一篇介紹這些奇怪的宏:
一、do while(0)
為了交換兩個整型變量的值,前面值傳遞中已經用 包含指針參數的 swap 函數做到了,這次用宏來實現(swap.c):
#include <stdio.h>
#define SWAP(a,b) \
do{ \
int t = a; \
a = b; \
b = t; \
}while(0)
int main()
{
int c=1, d=2;
int t; // 測試 SWAP 與環境的兼容性
SWAP(c,d);
printf("c:%d d:%d\n", c, d);
return 0;
}
這個宏看起來就有點怪了:do while(0) 是寫了個循環 又不讓它循環,蛋疼啊!其實不然,這樣寫是有妙用的:
首先,SWAP 有多條語句,如果這樣寫:
#define SWAP(a,b) \
int t = a; \
a = b; \
b = t;
那麼用的時候就得這麼用:
SWAP(c,d)
不能加分號!不習慣吧?
其次,使用 do{...}while(0), 中間的語句用大括號括起來了,所以是另一個命名空間, 其中的新變量 t 不會發生命名衝突。
SWAP 宏要比之前那個函數的效率要高, 因為沒有發生函數調用,沒有參數傳遞, 宏會在編譯前被替換,所以只是嵌入了一小段代碼。
二、#
標題我沒打錯,這裡要說的就是井號,#的功能是將其後面的 宏參數進行字符串化操作。比如下面代碼中的宏:
#define WARN_IF(EXP) \
do{ if (EXP) \
fprintf(stderr, "Warning: " #EXP "\n"); } \
while(0)
那麼實際使用中會出現下面所示的替換過程:
WARN_IF (divider == 0);
被替換為
do { if (divider == 0)
fprintf(stderr, "Warning: " "divider == 0" "\n");
} while(0);
需要注意的是C語言中多個雙引號字符串放在一起 會自動連接起來,所以如果 divider 為 0 的話,就會打印出:
Warning: divider == 0
三、##
#
還是比較少用的,##
卻比較流行,
在 linux0.01 中就用到過。## 被稱為連接符,
用來將兩個 記號(編譯原理中的詞彙) 連接為一個 記號。
看下面的例子吧(add.c):
#include <stdio.h>
#define add(Type) \
Type add##Type(Type a, Type b){ \
return a+b; \
}
// 下面兩條是奇蹟發生的地方
add(int)
add(double)
int main()
{
int a = addint(1, 2);
double d = adddouble(1.5, 1.5);
printf("a:%d d:%lf\n", a, d);
return 0;
}
那兩行被替換後是這個樣子的:
int addint(int a, int b){ return a+b; }
double adddouble(double a, double b){ return a+b; }
以上內容都可以使用照妖鏡看到宏被替換後的情形。