[Mac-10.7.1 Lion Intel-based]
Q: 預先處理到底幹了什麼事情?
A: 預先處理,顧名思義,預先做的處理。原始碼中的標頭檔包含,宏以及條件編譯的東西都會在預先處理的時候搞定。換句話說,以#開頭的語句即為預先處理。但是,如果#被包含在引號裡面,那就只是單純的字元或者字串了。
Q: 怎麼證明預先處理的存在?
A: 如下代碼,儲存為macro.c:
#include <stdio.h>#define NUM 100int main(){ printf("%d\n", NUM); return 0;}
使用gcc -E macro.c -o macro.i得到預先處理的結果(因為篇幅問題,只截取最後數行代碼):
# 500 "/usr/include/stdio.h" 2 3 4# 2 "macro.c" 2int main(){ printf("%d\n", 100); return 0;}
可以看到,原始碼中的NUM已經被替換成了宏定義的100.
事實上,預先處理中的宏同樣可以在命令列中指定,如下代碼,儲存為macro.c:
#include <stdio.h>int main(){ printf("%d\n", NUM); return 0;}
可以看到代碼中的NUM沒有被定義,然後使用編譯命令加上對NUM的宏定義:
gcc -DNUM=100 macro.c -o macro
編譯結束,沒有問題,運行亦ok.
同理,對於標頭檔包含以及條件編譯都可以通過預先處理命令得到處理之後的代碼形式,這樣會更好地理解預先處理的含義。調試宏也不是一個簡單的事情,如果很難確定某個宏到底有沒有作用或者宏對應的字串到底是什麼的時候,使用預先處理命令得到結果是很好的方式。
Q: 有時看到一個字串前面帶有一個#符號,它表示什麼含義?
形如:
#define PRINT_CH(ch) printf(#ch" is %c\n", (ch));
A:它表示將對應字元組合轉換成相應的字串格式。
使用如下代碼,儲存為macro.c:
#include <stdio.h>#define PRINT_CH(ch) printf(#ch" is %c\n", (ch));int main(){ PRINT_CH('a') return 0;}
使用gcc -E -o macro.i macro.c編譯命令得到預先處理後的檔案macro.i.
使用如下命令得到預先處理檔案的最後10行:
可以看到PRINT_CH('a')宏被處理成: printf("'a'"" is %c\n", ('a'));
#ch的作用就是將它變成"ch"的模樣。兩個字串字面量放一起相當於字串拼接的作用。編譯和運行結果:
Q: 除了上面的#符號,還會看到##符號,它又是什麼含義?
A: 它是代表參數串連,串連過程很單純,讓你看不到一點改變。舉個例子:
#include <stdio.h>#define CATENATE(a, b) a##bint main(){ int ab = 1; printf("%d\n", CATENATE(a, b)); return 0;}
儲存為preprocess.c,預先處理後的結果是(僅截取最後數行代碼):
# 2 "preprocess.c" 2int main(){ int ab = 1; printf("%d\n", ab); return 0;}
編譯運行:
可以發現, a##b得到的結果就是ab這個符號。
Q: 宏定義也是可以續行的,為什麼有的時候用的反斜線來續行,最後編譯依然有錯?
A: 這有可能續行符被用在了字串字面量裡面,這個不允許的;或者續行符後面跟著非換行字元導致的。續行的含義是將隨後緊隨的換行字元幹掉,當成沒發生,如果不是換行字元就可能導致錯誤。不過gcc 4.2對這個要求也不是很嚴格了,如下代碼,儲存為preprocess.c:
#include <stdio.h>#define ADD(a, b) \ ((a) + (b))int main(){ int ret = ADD(1, 2); printf("%d\n", ret); return 0;}
其中ADD宏行末的續行符後面有一個空格,在有的編譯器下會編譯錯誤。
為了確認續行符後面是否有空格,使用cat -e preprocess.c命令得到行末資訊:
可以看到第二行的續行符後面有個空格。編譯它,沒有什麼問題。
Q: 看到printf函數是可變參數的,如果用宏可以定義嗎?
A: 是的。__VA_ARGS__便是可變參數的代表。如下代碼:
#include <stdio.h>#define printf_ex(...) printf(__VA_ARGS__)int main (int argc, const char * argv[]){ printf_ex("hello%d\n", 1); return 0;}
上面的代碼宏定義了printf_ex函數,參數為可變參數,它的實現即為printf函數,__VA_ARGS__即為printf_ex的可變參數。
編譯運行:
Q: 既然可以宏定義,那麼重複的宏定義的結果是什麼呢?
A: 這樣的話,一般前置處理器會按照後者定義的為準,不過一般的前置處理器也會發出警告。
如下代碼,儲存為redefinition.c:
#include <stdio.h>#define A 10#define A 11#define PRINT_D(intValue) printf(#intValue" is %d\n", (intValue));int main (int argc, const char * argv[]){ PRINT_D(A) return 0;}
編譯:
可以發現出現了A重複宏定義的警告。
運行:
Q: 有的時候發現需要列印當前啟動並執行位置,比如檔案名稱,行數以及運行所在的函數,這該怎麼辦?
A: 可以使用__FILE__, __LINE__和__func__這些預定義的符號來處理。這裡以__func__的使用為例子:
#include <stdio.h>int add(int a, int b){ printf("func %s execute begin...", __func__); return a + b;}int main (int argc, const char * argv[]){ add(1, 2); return 0;}
編譯運行:
可以看到add函數裡面的__func__被替換成了add.
xichen
2012-5-13 16:30:10