編譯程序之前,先由預處理器檢查程序(因此稱爲預處理器)。根據程序中使用的預處理器指令,預處理用符號縮略語所代表的內容替換程序中的縮略語。
預處理器可以根據你的請求包含其他文件,還可以讓編譯器處理哪些代碼。預處理器不能理解C,它一般是接受一些文本並將其轉換成其他文本。-- (C Primer Plus中文第五版)
預處理符號
明顯常量 #define
#define
定義的作用域從定義出現的位置開始直到文件的結尾。這裏表明在函數裏使用#define
,凡是在它定義的後面的所有函數都是可以用的:
#include <stdio.h>
void pp();
int main() {
#define FF 10
pp();
}
void pp(){
printf("FF: %d ",FF);
}
輸出:
FF: 10
要時刻記住:#define
會把它之後的文件裏的所有符合要求的文本替換掉。
每個#define
行由三部分組成:
- 指令自身
#define
- 縮略語,即宏(macro)
宏的名字中不允許有空格,必須遵循C變量名規則 - 替換列表或者主體(body)
預處理器在程序中發現了宏的實例後,總會用實體代替該宏。從宏變成最終的替換文本的過程稱爲宏展開(macro expansion)。
宏可以分兩種:
- 類對象宏 object-like macro
宏用來代表值 - 類函數宏 function-like macro
外形和作用都與函數相似
#define TWO 2;
#define PX printf("X is: %d\n",x)
#define FMT "X+1 is: %d\n"
int main() {
int x =TWO;
PX;
printf(FMT,x+1);
}
輸出:
X is: 2
X+1 is: 3
上面的程序其實被預處理器改成了:
int x = 2;
printf("X is: %d\n",x);
printf("X+1 is: %d\n",x+1);
//先變成int y = TWO * TWO;
int y = 2*2;
printf("y is: %d\n",y);
注意,宏展開過程中,是進行替換,並不進行計算。C編譯器在編譯時對所有常量表達式(只包含常量的表達式)求值,所以實際相乘過程發生在編譯階段,而不是預處理階段。預處理器不進行計算,它只是按照指令進行文字替換操作。
宏展開過程中,會用宏的等價(即body
)來替換文本,如果宏的body
本身還含有宏的話,會繼續展開這些宏。但是,雙引號中的與宏縮略語一樣的字符串無法被替換。
重定義常量
假設一個縮略語被定義後又在同文件中被定義,這樣被稱爲重定義(redefinng a constant)。有的編譯器會對這樣提出警告,但允許重定義存在,有的則直接報錯。
#define SIX 3 * 3
#define SIX 3 * 3
//上面這樣的重定義會被編譯器認爲是重複定義,是相同的
#define SIX 3*3
//這樣的重定義與上面兩種是不同的
在#define
中使用參數
類函數宏的定義中,用圓括號闊氣一個或多個參數,隨後這些參數出現在替換部分。
#define SQUARE(X) X*X
//一個參數X
//使用
int y = SQUARE(2);
宏調用和函數調用存在着區別:
程序運行時,函數調用把參數的值傳遞給函數,而編譯前,宏調用把參數的語言符號傳遞給程序,僅僅是替換字符,而不計算。
#define SQUARE(X) X*X
int main() {
int x = 2;
int y = SQUARE(x);
printf("SQUARE(x) is: %d\n",y);
printf("SQUARE(x+2) is: %d\n",SQUARE(x+2));
}
輸出:
SQUARE(x) is: 4
SQUARE(x+2) is: 8
按理說square(2+2)
應該是16啊,怎麼會是8呢?原來像剛纔上面說的,預處理只是替換,因此SQUARE(x+2)
中的X
被x+2
替換,最後成了x+2*x+2
。*
優先級高,,因此程序運行時先計算2*x
,再加上x
和2
,也就成了8
。要想實現平方的效果,需要重新定義:
#define SQUARE(X) ((X)*(X))
即使這樣定義,還是無法避免自增、自減情況下的錯誤:
int x = 3;
int a = SQUARE(++x);
這裏替換成++x
字符後,進行了兩次增量運算,最後結果肯定不是平方了。因此,在宏中不要使用增量或減量運算符。而且一定要充分的使用圓括號來保證正確的運算順序。
在類函數宏中使用#
運算符
上面說了在引號表示的字符串無法替換掉宏參數,,但是使用#
預處理運算符,可以把傳入的參量轉化爲文本替換到字符串裏。
#define SQUARE(X) (X)*(X)
#define PF(X) printf("The square of " #X " is : %d\n",SQUARE(X))
int main() {
int x = 10;
PF(x);
PF(2+4);
}
輸出:
The square of x is : 100
The square of 2+4 is : 36
在宏中使用##
運算符
##
運算符把兩個語言符號組合成單個語言符號:
#define XVAR(X) x ## X
int XVAR(2) = 11;//聲明瞭一個標識符爲x2的變量
可變宏:...
和__VA_ARGS__
宏定義中參數列表的最後一個參數爲省略號,預定義宏__VA_ARGS_
就可以被用在替換部分,以代表省略號省略了什麼。
#define PF(X,...) printf("Result " #X " : " __VA_ARGS__)
int main() {
PF(1,"%d\n",10);
PF(2,"%d's power is %d\n",4,16);
}
輸出:
Result 1 : 10
Result 2 : 4's power is 16
省略號只能代替最後的宏參數。
#define WRONG(X, ... ,Y) #X #__VA_ARGS__ #Y)//錯誤
這裏有個有趣的現象:一般想要打印字符串,字符串都得用雙引號括起來,這裏不用:
#define STRING(... ) #__VA_ARGS__
int main() {
printf(STRING(abcdefg));
}
輸出:
abcdefg