這一篇主要是CPP manual第三章macro的心得,所以都是在講述macro。
object-like macro是最簡單的macro,就是直接將macro的name取代成code。 如:
#define NUMBER 1, \ 2, \ 3, int x[] = {NUMBER} -> int x[] = { 1, 2 3 };NUMBER會被展開誠1, 2, 3
當macro被展開之後,會再度的被檢驗是否還有macro需要被展開。 如:
#define TABLESIZE BUFSIZE #define BUFSIZE 1024 TABLESIZE -> BUFSIZE -> 1024TABLESIZE會先被展開成BUFSIZE,接著BUFSIZE再被展成1024。
Function-like macro用起來就像在用function,所以被稱為function-like macro。function-like macro是在macro name後面"緊"接括號。 如:
#define lang_init() c_init() lang_init() -> c_init()
當macro name後面緊接著(),這個macro才會被當成function-like macro,否則會當成一般macro展開。 如:
extern void foo(void); #define foo() boo() .... foo(); // macro funcptr = foo; // function #define lang_init () c_init() // 多了空白在name和()之間 lang_init() -> () c_init()() // 結果就會造成錯誤
macro arguments,function macro可以像一般function一帶參數,這些參數會先被展開,接著再展開macro,參數要以","區隔開來(和一般的function一樣)。 如:
#define min(X, Y) ((X) < (Y) ? (X) : (Y)) x = min(a + 28, *p) -> x = ((a + 28), (*p) ? (a + 28) : (*p)); y = min(min(a, b), c) -> min(((a) < (b) ? (a) : (b)), c) -> .... // 產生的結果可能會和你想的不一樣
stringification(中文不知道怎麼稱呼,暫稱他字串化吧),當macro所帶的參數前面加上"#"之後,這個參數就會被展成字串,這就是stringification。 如:
#define WARN_IF(EXP) \ do { if (EXP) \ fprintf (stderr, "Warning: " #EXP "\n"); } \ while (0) WARN_IF (x == 0); -> do { if (x == 0) fprintf (stderr, "Warning: " "x == 0" "\n"); } while (0);
目前只有stringification,並沒有辦法轉成character。另外,stringification的優先權會高於argument expand。 如:
#define xstr(s) str(s) #define str(s) #s #define foo 4 str (foo) -> "foo" xstr (foo) -> xstr (4) -> str (4) -> "4"
token concatenation或稱token pasting,是利用"##"將兩個token組成一個token,'##'被置於兩個token之間,如:
#define COMMAND(name) {#name, name ## _command} struct command { char *name; void (*f)(void); } cmds[] = { COMMAND(quit), COMMAND(help), }; -> struct command { char *name; void (*f)(void); } cmds[] = { {"quit", quit_command}, {"help", help_command}, };
variadic其實就是不定參數的function-like macro,__VA_ARGS__代表"..."(即後面所有的參數),如:
#define eprintf(...) fprintf(stderr, __VA_ARGS__) eprintf("%s(#%d)\n", __FUNCTION__, __LINE__) -> fprintf(stderr, "%s(#%d)\n", __FUNCTION__, 7)
cpp也可以使用name取代__VA_ARGS__,用法上只是在"..."前面加上name,"_VA_ARGS__"則用name取代即可,如:
#define eprintf(args...) fprintf(stderr, args) eprintf("%s(#%d)\n", __FUNCTION__, __LINE__) -> fprintf(stderr, "%s(#%d)\n", __FUNCTION__, 7)
來看另外一個例子
#define eprintf(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__) eprintf("hello\n"); -> fprintf(stderr, "hello\n", ); // error!!這裡的問題就是如何將__VA_ARGS__前面的","在沒有參數帶入的時候刪除,答案就是"##"。
#define eprintf(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__) eprintf("hello\n"); -> fprintf(stderr, "hello\n"); // correct!!