c語言源程式在進行編譯、鏈結之前會進行檢測是否有一些特殊的預處理指令,若檢測一些特殊的預處理指令則會進行相應的處理。c語言中多有的預處理指令都以符號「#」開頭,並且結尾不使用分號。預處理指令在程式中出現的位置沒有限定,即可以出現在任何地方,它的作用範圍是從它出現的位置到檔案尾。但一般我們把預處理指令寫在檔案的開頭,這種情況下,他的作用範圍會是整個源程式檔案。c語言主要提供了三大類預處理指令:巨集定義、檔案包含、條件編譯
一、巨集定義
1、不帶引數的巨集定義
有時我們會在程式中使用一些常量,比如使用圓周率pi,這時為了避免重複書寫複雜資料和為了以後修改方便,我們會使用巨集定義來定義這些常量。不帶引數的巨集定義的語法為:#define 巨集名 字串,比如: #define pi 3.1415926。或許有人會疑惑為什麼使用巨集定義而不使用全域性常量,這是因為巨集定義會在程式編譯之前進行處理,而且巨集定義只是單純的字串替換,即在後續所有使用pi的地方都會用3.1415926進行替換掉,並不涉及資料型別、記憶體分配等處理。因此巨集定義更加高效、安全。在使用巨集時,我們習慣將巨集名大寫,並且末尾不加空格。當我們希望在**的某處之後都不再使用之前的某個巨集定義時我們可以使用#undef命令結束乙個巨集,例如結束pi巨集的定義,#undef pi。當然在定義乙個巨集時我們可以引用已經定義的巨集名,例如: #define circle 2*pi*3.0
2、 帶引數的巨集定義
當我們在定義乙個巨集時,我們希望不僅進行我們所希望的字串的替換,我們也希望可以自己指定引數進行替換時我們就可以使用帶引數的巨集定義。其語法為:#define 巨集名(引數列表) 字串。例如:
#include #include #define double(a) 2*(a)
int main()
結果為:
當呼叫double(2)時,編譯器會直接使用2*2進行文字替換,所以得到的結果為4。但我們使用帶引數的巨集定義時需要注意以下幾點:
(1)巨集名與引數列表之間不能有空格,否則編譯器會以為空格之後的字串都是進行字串替換的字串。例如:如果我們書寫#define double (a) 2*a時,當進行替換時會替換成:(a) 2*a(2),此時編譯器不能正確編譯。
(2)在定義巨集時應用小括號將字串的引數括起來,否則會出現意想不到的錯誤。例如:執行double(3+4),本來我們期望的結果為14,但如果我們採用這樣的巨集定義:#define double(a) 2*a,我們會得到這樣的替換:2*3+4 = 10。
(3)我們最好將計算結果也用括號括起來,比如我們定義乙個巨集pow(a),其功能是返回a的平方,若不使用小括號括住結果則:#define pow(a) (a)*(a),我們呼叫pow(10)/pow(2),會得到替換結果為:(10 )*( 10) / (2) * (2) = 100,並不是我們所期望的:100/4 = 25。但如果我們將巨集改為:#define pow(a) ((a) * (a)),則結果會變為:
((10)* (10))/ ((2)* (2)) = 25。
二、檔案包含
在c語言的**檔案開頭,我們通常都會看到這樣一句#include ,這就是c語言中檔案包含的預處理指令。這句話的作用就是在預處理檔案時將stdio.h檔案的內容全部拷貝到當前檔案的當前位置。檔案包含的預處理命令有兩種形式,第一種:#include <>,第二種:#include " "
#include《檔名》的形式與#include " 檔名"的形式在本質上沒有不同,都是找到檔案內容然後拷貝到當前檔案。但是這兩種方式的區別在於對於指定的檔案的路徑搜尋順序不同,#include<>形式將直接到c語言庫函式標頭檔案所在的目錄尋找標頭檔案,如果沒有查詢到目標檔案則會直接報錯;而 #include " "形式將先會去源程式所在的當前目錄下尋找標頭檔案,如果沒找到再到作業系統的path路徑中尋找,如果仍就未找到則會去c語言庫函式標頭檔案所在目錄中查詢,如果依舊未找到則會報錯。因此,我們使用檔案包含指令時一般按如下的規則使用:當我們確定使用庫函式標頭檔案時我們使用#include<>形式,如果我們使用自己自定義的標頭檔案時我們一般使用#include" " 形式。
使用c語言的#include指令有一點需要注意,那就是#include指令有可能造成標頭檔案的重複包含使得編譯效率降低。什麼是標頭檔案的重複包含?比如我們定義了乙個標頭檔案test1.h,而且我們在test1.h中包含了我們自己定義的另乙個標頭檔案#include"test.h",緊接著我又定義了乙個test2.h,並且我在test2.h中包含了test1.h,但是當我在main函式中包含時我使用了#include"test1.h" 和#include "test2.h",那麼我們將會拷貝兩次test.h到當前的檔案。那麼為了解決重複包含的問題我們一般會引入條件編譯指令,使用條件編譯指令來防止標頭檔案的重複包含。
三、條件編譯
在c語言中如果我們希望程式中的一部分**只有在滿足特定的條件時才進行編譯,否則不進行編譯時,我們會採用條件編譯的策略。條件編譯的基本用法為:
#if 條件1
...code1...
#elif 條件2
...code2...
#else
...code3...
#endif
上述指令並不等同於c語言中的if else指令,if與elif後的判定條件一般是巨集定義,如果條件1成立則會將code1表示的**編譯進原始檔,如果條件2成立則只有code2代表的**段會被編譯進源程式。如果條件2也不成立則只有code3會被編譯進原始檔,末尾的#endif一定要有,否則#else以下的所有內容都將會被看成code3的內容,這是不符合實際情況的。例如:
#include #define number 2
int main()
執行結果:
另一組指令#ifdef和#ifndef如此類似,但這組指令一般用在防止標頭檔案重複包含時,其使用的一般語法為:
#ifdef 巨集名
...code...
#endif
或:#ifndef 巨集名
...code...
#endif
第一條命令會判斷指定的巨集是否已經定義,如果定義過則將code編譯進去,後一條命令則與此相反。例如我們在編譯乙個test.h的自定義標頭檔案時我們一般會在標頭檔案開頭這樣寫:
#ifndef _test_h_
#define _test_h_
...
#endif // _test_h_
這樣當第一次#include "test.h"時會正確包含,並定義乙個_test_h_這個巨集,當後續重複包含時會檢測到_test_h_這個巨集已經定義則#ifndef指令不成立,就避免了重複的包含同乙個標頭檔案。 黑馬程式設計師 C語言學習總結22 32
1 c語言在編譯之前,會先對一些預處理指令作解釋,產生新的源程式,這個過程稱為編譯預處理。2 預處理指令以 開頭,並且結尾沒有分號。3 將預處理指令寫在源程式開頭,它的作用範圍是預處理指令之後的整個源程式檔案。4 c語言提供的預處理指令主要有 巨集定義 檔案包含 條件編譯。5 巨集定義分為不帶引數的...
黑馬程式設計師 C語言學習筆記之陣列(九)
ios期待與您交流!1 定義 格式 型別 陣列名 元素個數 裡面的個數必須是乙個固定值,可以是常量 比如6 8 常量表示式 比如3 4 5 7 絕對不能使用變數或者變數表示式來表示元素個數,大多數情況下不要省略元素個數2 初始化 一般形式是 型別 陣列名 元素個數 int a 2 其實相當於 int...
黑馬程式設計師 C語言學習筆記之列舉(十三)
ios期待與您交流!enum 列舉名 c中的列舉非常簡單,它的本質是整型資料,列舉的每個成員都是int型的。enum printf 列舉所佔位元組數 lu n sizeof enum 輸出 列舉所佔位元組數 4 enum 列舉名 變數名 列舉中的成員是從0開始計數的 enum enum man ma...