1.C語言在對源程序進行編譯以前,會先對一些特殊的預處理指令做解釋(好比以前使用的#include文件包含指令),產生一個新的源程序(這個過程稱爲編譯預處理),以後再進行一般的編譯函數
2.爲了區分預處理指令和通常的C語句,全部預處理指令都以符號"#"開頭,而且結尾不用分號spa
3.預處理指令能夠出如今程序的任何位置,它的做用範圍是從它出現的位置到文件尾。習慣上咱們儘量將預處理指令寫在源程序開頭,這種狀況下,它的做用範圍就是整個源程序文件code
4.C語言提供的預處理指令主要有:宏定義、文件包含、條件編譯orm
這一講先介紹一下宏定義,宏定義能夠分爲2種:不帶參數的宏定義 和 帶參數的宏定義。作用域
#define 宏名 字符串字符串
好比#define ABC 10it
右邊的字符串也能夠省略,好比#define ABCio
它的做用是在編譯預處理時,將源程序中全部"宏名"替換成右邊的"字符串",經常使用來定義常量。編譯
接下來寫個程序根據圓的半徑計算周長class
#include <stdio.h> // 源程序中全部的宏名PI在編譯預處理的時候都會被3.14所代替 #define PI 3.14 // 根據圓的半徑計radius算周長 float girth(float radius) { return 2 * PI *radius; } int main () { float g = girth(2); printf("周長爲:%f", g); return 0; }
在第4行定義了一個叫PI的宏,在編譯預處理以後,第8行中的2 * PI *radius就會變成2 * 3.14 * radius。
輸出結果:
1> 宏名通常用大寫字母,以便與變量名區別開來,但用小寫也沒有語法錯誤
2> 對程序中用雙引號擴起來的字符串內的字符,不進行宏的替換操做。好比:
#define R 10 int main () { char *s = "Radio"; return 0; }
在第1行定義了一個叫R的宏,可是第4行中"Radio"裏面的'R'並不會被替換成10
3> 在編譯預處理用字符串替換宏名時,不做語法檢查,只是簡單的字符串替換。只有在編譯的時候纔對已經展開宏名的源程序進行語法檢查
#define I 100 int main () { int i[3] = I; return 0; }
在作編譯預處理的時候,無論語法對不對,第4行的I都會被替換爲100。不過在編譯的時候就會報第4行的錯。
4> 宏名的有效範圍是從定義位置到文件結束。若是須要終止宏定義的做用域,能夠用#undef命令
#define PI 3.14 /* . . . . */ #undef PI
PI這個宏在第1行到第8行之間是有效的,第8行後就無效了
5> 定義一個宏時能夠引用已經定義的宏名
#define R 3.0 #define PI 3.14 #define L 2*PI*R #define S PI*R*R
#define 宏名(參數列表) 字符串
在編譯預處理時,將源程序中全部宏名替換成字符串,而且將 字符串中的參數 用 宏名右邊參數列表 中的參數替換
#include <stdio.h> #define average(a, b) (a+b)/2 int main () { int a = average(10, 4); printf("平均值:%d", a); return 0; }
第3行中定義了一個帶有2個參數的宏average,第7行其實會被替換成:int a = (10 + 4)/2;,輸出結果爲:。是否是感受這個宏有點像函數呢?
1> 宏名和參數列表之間不能有空格,不然空格後面的全部字符串都做爲替換的字符串
#define average (a, b) (a+b)/2 int main () { int a = average(10, 4); return 0; }
注意第1行的宏定義,宏名average跟(a, b)之間是有空格的,因而,第5行就變成了這樣:
int a = (a, b) (a+b)/2(10, 4);
這個確定是編譯不經過的
> 帶參數的宏在展開時,只做簡單的字符和參數的替換,不進行任何計算操做。因此在定義宏時,通常用一個小括號括住字符串的參數。
下面定義一個宏D(a),做用是返回a的2倍數值:
若是定義宏的時候不用小括號括住參數
#include <stdio.h> #define D(a) 2*a int main () { int b = D(3+4); printf("%d", b); return 0; }
第7行將被替換成int b = 2*3+4;,輸出結果:
若是定義宏的時候用小括號括住參數,把上面的第3行改爲:
#define D(a) 2*(a)
注意右邊的a是有括號的,第7行將被替換成int b = 2*(3+4);,輸出結果:
3> 計算結果最好也用括號括起來
下面定義一個宏P(a),做用是返回a的平方:
若是不用小括號括住計算結果
#include <stdio.h> #define Pow(a) (a) * (a) int main(int argc, const char * argv[]) { int b = Pow(10) / Pow(2); printf("%d", b); return 0; }
注意第3行,沒有用小括號擴住計算結果,只是括住了參數而已。第6行代碼被替換爲:
int b = (10) * (10) / (2) * (2);
簡化以後:int b = 10 * (10 / 2) * 2;,最後變量b爲:
若是用小括號括住計算結果
將上面的第3行代碼改成:
#define Pow(a) ( (a) * (a) )
那麼第6行被替換爲:
int b = ( (10) * (10) ) / ( (2) * (2) );
簡化以後:int b = (10 * 10) / (2 * 2);,最後輸出結果:。這個纔是咱們想要的結果。
也就意味着前面的#define average(a, b) (a+b)/2應該寫成#define average(a, b) (((a)+(b))/2)
從整個使用過程能夠發現,帶參數的宏定義,在源程序中出現的形式與函數很像。可是二者是有本質區別的:
1> 宏定義不涉及存儲空間的分配、參數類型匹配、參數傳遞、返回值問題
2> 函數調用在程序運行時執行,而宏替換隻在編譯預處理階段進行。因此帶參數的宏比函數具備更高的執行效率