 |
 |
培訓(xùn)信息 |
|
|
|
 |
 |
贊助商 |
|
|
|
|
 |
 |
C語(yǔ)言宏條件編譯詳解 |
|
|
| C語(yǔ)言宏條件編譯詳解 |
| 作者:jdzj868 來(lái)源:機(jī)電之家下載站 錄入:jdzj868 更新時(shí)間:2010-1-26 16:18:26 點(diǎn)擊數(shù):0 |
【字體:
】 |
預(yù)處理過(guò)程掃描源代碼,對(duì)其進(jìn)行初步的轉(zhuǎn)換,產(chǎn)生新的源代碼提供給編譯器。可見(jiàn)預(yù)處理過(guò)程先于編譯器對(duì)源代碼進(jìn)行處理。 在C 語(yǔ)言中,并沒(méi)有任何內(nèi)在的機(jī)制來(lái)完成如下一些功能:在編譯時(shí)包含其他源文件、定義宏、根據(jù)條件決定編譯時(shí)是否包含某些代碼。要完成這些工作,就需要使用預(yù)處理程序。盡管在目前絕大多數(shù)編譯器都包含了預(yù)處理程序,但通常認(rèn)為它們是獨(dú)立于編譯器的。預(yù)處理過(guò)程讀入源代碼,檢查包含預(yù)處理指令的語(yǔ)句和宏定義,并對(duì)源代碼進(jìn)行響應(yīng)的轉(zhuǎn)換。預(yù)處理過(guò)程還會(huì)刪除程序中的注釋和多余的空白字符。 預(yù)處理指令是以#號(hào)開(kāi)頭的代碼行。#號(hào)必須是該行除了任何空白字符外的第一個(gè)字符。#后是指令關(guān)鍵字,在關(guān)鍵字和#號(hào)之間允許存在任意個(gè)數(shù)的空白字符。整行語(yǔ)句構(gòu)成了一條預(yù)處理指令,該指令將在編譯器進(jìn)行編譯之前對(duì)源代碼做某些轉(zhuǎn)換。下面是部分預(yù)處理指令: 指令 用途 # 空指令,無(wú)任何效果 #i nclude 包含一個(gè)源代碼文件 #define 定義宏 #undef 取消已定義的宏 #if 如果給定條件為真,則編譯下面代碼 #ifdef 如果宏已經(jīng)定義,則編譯下面代碼 #ifndef 如果宏沒(méi)有定義,則編譯下面代碼 #elif 如果前面的#if給定條件不為真,當(dāng)前條件為真,則編譯下面代碼 #endif 結(jié)束一個(gè)#if……#else條件編譯塊 #error 停止編譯并顯示錯(cuò)誤信息
一、文件包含 #i nclude預(yù)處理指令的作用是在指令處展開(kāi)被包含的文件。包含可以是多重的,也就是說(shuō)一個(gè)被包含的文件中還可以包含其他文件。標(biāo)準(zhǔn)C編譯器至少支持八重嵌套包含。 預(yù)處理過(guò)程不檢查在轉(zhuǎn)換單元中是否已經(jīng)包含了某個(gè)文件并阻止對(duì)它的多次包含。這樣就可以在多次包含同一個(gè)頭文件時(shí),通過(guò)給定編譯時(shí)的條件來(lái)達(dá)到不同的效果。例如:
#define AAA #i nclude "t.c" #undef AAA #i nclude "t.c"
為了避免那些只能包含一次的頭文件被多次包含,可以在頭文件中用編譯時(shí)條件來(lái)進(jìn)行控制。例如: /*my.h*/ #ifndef MY_H #define MY_H …… #endif
在程序中包含頭文件有兩種格式: #i nclude <my.h> #i nclude "my.h" 第一種方法是用尖括號(hào)把頭文件括起來(lái)。這種格式告訴預(yù)處理程序在編譯器自帶的或外部庫(kù)的頭文件中搜索被包含的頭文件。第二種方法是用雙引號(hào)把頭文件括起來(lái)。這種格式告訴預(yù)處理程序在當(dāng)前被編譯的應(yīng)用程序的源代碼文件中搜索被包含的頭文件,如果找不到,再搜索編譯器自帶的頭文件。 采用兩種不同包含格式的理由在于,編譯器是安裝在公共子目錄下的,而被編譯的應(yīng)用程序是在它們自己的私有子目錄下的。一個(gè)應(yīng)用程序既包含編譯器提供的公共頭文件,也包含自定義的私有頭文件。采用兩種不同的包含格式使得編譯器能夠在很多頭文件中區(qū)別出一組公共的頭文件。
二、宏 宏定義了一個(gè)代表特定內(nèi)容的標(biāo)識(shí)符。預(yù)處理過(guò)程會(huì)把源代碼中出現(xiàn)的宏標(biāo)識(shí)符替換成宏定義時(shí)的值。宏最常見(jiàn)的用法是定義代表某個(gè)值的全局符號(hào)。宏的第二種用法是定義帶參數(shù)的宏,這樣的宏可以象函數(shù)一樣被調(diào)用,但它是在調(diào)用語(yǔ)句處展開(kāi)宏,并用調(diào)用時(shí)的實(shí)際參數(shù)來(lái)代替定義中的形式參數(shù)。 1.#define指令 #define預(yù)處理指令是用來(lái)定義宏的。該指令最簡(jiǎn)單的格式是:首先神明一個(gè)標(biāo)識(shí)符,然后給出這個(gè)標(biāo)識(shí)符代表的代碼。在后面的源代碼中,就用這些代碼來(lái)替代該標(biāo)識(shí)符。這種宏把程序中要用到的一些全局值提取出來(lái),賦給一些記憶標(biāo)識(shí)符。 #define MAX_NUM 10 int array[MAX_NUM]; for(i=0;i<MAX_NUM;i++) /*……*/ 在這個(gè)例子中,對(duì)于閱讀該程序的人來(lái)說(shuō),符號(hào)MAX_NUM就有特定的含義,它代表的值給出了數(shù)組所能容納的最大元素?cái)?shù)目。程序中可以多次使用這個(gè)值。作為一種約定,習(xí)慣上總是全部用大寫字母來(lái)定義宏,這樣易于把程序紅的宏標(biāo)識(shí)符和一般變量標(biāo)識(shí)符區(qū)別開(kāi)來(lái)。如果想要改變數(shù)組的大小,只需要更改宏定義并重新編譯程序即可。 宏表示的值可以是一個(gè)常量表達(dá)式,其中允許包括前面已經(jīng)定義的宏標(biāo)識(shí)符。例如: #define ONE 1 #define TWO 2 #define THREE (ONE+TWO) 注意上面的宏定義使用了括號(hào)。盡管它們并不是必須的。但出于謹(jǐn)慎考慮,還是應(yīng)該加上括號(hào)的。例如: six=THREE*TWO; 預(yù)處理過(guò)程把上面的一行代碼轉(zhuǎn)換成: six=(ONE+TWO)*TWO; 如果沒(méi)有那個(gè)括號(hào),就轉(zhuǎn)換成six=ONE+TWO*TWO;了。 宏還可以代表一個(gè)字符串常量,例如: #define VERSION "Version 1.0 Copyright(c) 2003" 2.帶參數(shù)的#define指令 帶參數(shù)的宏和函數(shù)調(diào)用看起來(lái)有些相似?匆粋(gè)例子: #define Cube(x) (x)*(x)*(x) 可以時(shí)任何數(shù)字表達(dá)式甚至函數(shù)調(diào)用來(lái)代替參數(shù)x。這里再次提醒大家注意括號(hào)的使用。宏展開(kāi)后完全包含在一對(duì)括號(hào)中,而且參數(shù)也包含在括號(hào)中,這樣就保證了宏和參數(shù)的完整性?匆粋(gè)用法: int num=8+2; volume=Cube(num); 展開(kāi)后為(8+2)*(8+2)*(8+2); 如果沒(méi)有那些括號(hào)就變?yōu)?+2*8+2*8+2了。 下面的用法是不安全的: volume=Cube(num++); 如果Cube是一個(gè)函數(shù),上面的寫法是可以理解的。但是,因?yàn)镃ube是一個(gè)宏,所以會(huì)產(chǎn)生副作用。這里的擦?xí)皇呛?jiǎn)單的表達(dá)式,它們將產(chǎn)生意想不到的結(jié)果。它們展開(kāi)后是這樣的: volume=(num++)*(num++)*(num++); 很顯然,結(jié)果是10*11*12,而不是10*10*10; 那么怎樣安全的使用Cube宏呢?必須把可能產(chǎn)生副作用的操作移到宏調(diào)用的外面進(jìn)行: int num=8+2; volume=Cube(num); num++; 3.#運(yùn)算符 出現(xiàn)在宏定義中的#運(yùn)算符把跟在其后的參數(shù)轉(zhuǎn)換成一個(gè)字符串。有時(shí)把這種用法的#稱為字符串化運(yùn)算符。例如:
#define PASTE(n) "adhfkj"#n
main() { printf("%s\n",PASTE(15)); } 宏定義中的#運(yùn)算符告訴預(yù)處理程序,把源代碼中任何傳遞給該宏的參數(shù)轉(zhuǎn)換成一個(gè)字符串。所以輸出應(yīng)該是adhfkj15。 4.##運(yùn)算符 ##運(yùn)算符用于把參數(shù)連接到一起。預(yù)處理程序把出現(xiàn)在##兩側(cè)的參數(shù)合并成一個(gè)符號(hào)?聪旅娴睦樱
#define NUM(a,b,c) a##b##c #define STR(a,b,c) a##b##c
main() { printf("%d\n",NUM(1,2,3)); printf("%s\n",STR("aa","bb","cc")); }
最后程序的輸出為: 123 aabbcc 千萬(wàn)別擔(dān)心,除非需要或者宏的用法恰好和手頭的工作相關(guān),否則很少有程序員會(huì)知道##運(yùn)算符。絕大多數(shù)程序員從來(lái)沒(méi)用過(guò)它。
三、條件編譯指令 條件編譯指令將決定那些代碼被編譯,而哪些是不被編譯的?梢愿鶕(jù)表達(dá)式的值或者某個(gè)特定的宏是否被定義來(lái)確定編譯條件。 1.#if指令 #if指令檢測(cè)跟在制造另關(guān)鍵字后的常量表達(dá)式。如果表達(dá)式為真,則編譯后面的代碼,知道出現(xiàn)#else、#elif或#endif為止;否則就不編譯。 2.#endif指令 #endif用于終止#if預(yù)處理指令。
#define DEBUG 0 main() { #if DEBUG printf("Debugging\n"); #endif printf("Running\n"); }
由于程序定義DEBUG宏代表0,所以#if條件為假,不編譯后面的代碼直到#endif,所以程序直接輸出Running。 如果去掉#define語(yǔ)句,效果是一樣的。 3.#ifdef和#ifndef #define DEBUG
main() { #ifdef DEBUG printf("yes\n"); #endif #ifndef DEBUG printf("no\n"); #endif } #if defined等價(jià)于#ifdef; #if !defined等價(jià)于#ifndef 4.#else指令 #else指令用于某個(gè)#if指令之后,當(dāng)前面的#if指令的條件不為真時(shí),就編譯#else后面的代碼。#endif指令將中指上面的條件塊。
#define DEBUG
main() { #ifdef DEBUG printf("Debugging\n"); #else printf("Not debugging\n"); #endif printf("Running\n"); }
5.#elif指令 #elif預(yù)處理指令綜合了#else和#if指令的作用。
#define TWO
main() { #ifdef ONE printf("1\n"); #elif defined TWO printf("2\n"); #else printf("3\n"); #endif } 程序很好理解,最后輸出結(jié)果是2。
6.其他一些標(biāo)準(zhǔn)指令 #error指令將使編譯器顯示一條錯(cuò)誤信息,然后停止編譯。 #line指令可以改變編譯器用來(lái)指出警告和錯(cuò)誤信息的文件號(hào)和行號(hào)。 #pragma指令沒(méi)有正式的定義。編譯器可以自定義其用途。典型的用法是禁止或允許某些煩人的警告信息。
|
|
|
發(fā)表評(píng)論 告訴好友 打印此文 收藏此頁(yè) 關(guān)閉窗口 返回頂部 |
|
|
 |
 |
網(wǎng)友評(píng)論:(只顯示最新5條。) |
|
|
|
|
|