C 語言 Chapter19 前置處理Preprocessor

前置處理是編譯器開始編譯程式碼之前所做的處理。可透過前置處理將程式碼動態調整成我們想要的形式,前置處理指令有#include, #define, #if #ifdef #ifndef,  #, __LINE__, __FILE__, ##

19.1 #include

直接將標頭檔引入要編譯的程式碼。
標頭檔詳細定義資料結構,以及操作資料結構所需使用的函式名稱、參數及回傳值。
系統標頭檔由 <> 括起
使用者標頭檔由 "" 括起來


19.2 #define

將程式碼中所定義的字串換成另一字串。
一般習慣用 #define定義常數
用typedef定義資料類別,但也可用#define定義資料類別 Ex: #define Data int

透過#defien也可以定義巨集函式,Ex:  #define marco(x)  x * x
巨集函式非一個完整函式,只是進行字串的代換。

透過 gcc -E 指令可以將前置處理的結果送到螢幕。

例如程式碼

#define square(x) x * x

int main(void){

int i,j;
scanf("%d", &i);
scanf("%d", &j);
printf("%d\n", square(i+j));

執行gcc -E xxx.c 後得到的結果為

int main(void){

int i,j;
scanf("%d", &i);
scanf("%d", &j);
printf("%d\n", i+j * i+j);
return 0;
}

透過此方法可以驗證,巨集的使用是否得到預期的結果,以上的算式 i+j * i + j顯然不是我們想要的。

因此,可將巨集修正為 #define square(x) ((x) * (x))


int main(void){

int i,j;
scanf("%d", &i);
scanf("%d", &j);
printf("%d\n", ((i+j) * (i+j)));
return 0;

}

若傳進巨集的為一個函式,而且會有回傳值,使用巨集不是一個好方法。


19.3 條件編譯 #if, #ifdef, #ifndef
依據前置處理指令#if來決定程式碼要不要編譯Ex:
#if condition
source code
#endif

只有condition為true時,上述的source code才會執行,常用於是否列印除錯資訊,例如:

#define DEBUG 0
...
#if DEBUG == 1
printf("This is debug message. \n");
#endif

透過控制DEBUG定義值,決定是否印出除錯資訊。

在編譯時使用-DSYMBOL=VALUE語法可以動態改變#define值,而不需要修改程式。Ex:
gcc -DDEBUG_LEVEL=8 program.c
如此在編譯時會將DEBUG_LEVEL 的值給定為8,但編譯器define,程式碼也define造成重複定義(redefine)。此時要透過#ifndef來處理。如果#ifndef後面的符號未被定義,則#ifndef到#endif中間的程式碼會被編譯,如果已經有定義,就不會被編譯。

透過#ifndef,我們使用下面的程式碼,來避免透過-D造成的重複定義問題,當編譯時未使用-D則DEBUG_LEVEL預設為4
#ifndef DEBUG_LEVEL
#define DEBUG_LEVEL 4
#endif

若程式中只是要單純的開關,透過前置處理指令#ifdef可以達成。
#ifdef DEBUG
source code
#endif

在編譯器中可以透過 gcc -DDEBUG program.c 來開關
在程式中可以透過 #define DEBUG來開關
使用#undef則可以將某符號取消定義。#undef DEBUG


19.4 __LINE__, __FILE__
__LINE__ 知道前置處理目前的行數
__FILE__ 知道前置處理目前的檔名

printf("file %s, line %d", __FILE__, __LINE__);


19.5 ##
##可以將前後的名字連在一起,例如: S_IR ## USR 會成為S_IRUSR

沒有留言:

張貼留言