版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
第15章 預(yù)處理和宏 程序在編譯之前先要經(jīng)過(guò)預(yù)編譯階段。預(yù)編譯的任務(wù)是根據(jù)程序中的宏指令補(bǔ)充和完善源代碼。有了宏指令,可以使得某些源代碼編輯任務(wù)變得輕松,同時(shí)也可以控制哪些源代碼需要編譯,哪些不需要編譯。15.1
預(yù)處理概述
預(yù)處理器是專門(mén)用來(lái)處理宏指令的程序。在編譯器運(yùn)行之前,會(huì)先運(yùn)行預(yù)處理器,查找所有的預(yù)處理指令。預(yù)處理指令以“#”開(kāi)頭,而且不以“;”結(jié)束,以區(qū)別于一般的語(yǔ)句。預(yù)處理器根據(jù)預(yù)處理指令生成新的源代碼文件(臨時(shí)文件,可以通過(guò)編譯器的選項(xiàng)輸出到指定目錄中)。
編譯器的作用是把源代碼轉(zhuǎn)換成匯編語(yǔ)言或機(jī)器指令。但是,編譯器并不是直接編譯程序員寫(xiě)成的源文件,而是編譯經(jīng)預(yù)處理器處理后所產(chǎn)生的新的源文件。這些新的源文件經(jīng)過(guò)編譯器生成目標(biāo)文件,再經(jīng)過(guò)鏈接器生成最終的可執(zhí)行程序。
預(yù)處理器的任務(wù)就是執(zhí)行源代碼中的預(yù)處理指令,并對(duì)源代碼進(jìn)行相應(yīng)的處理。因此,從預(yù)處理指令的類(lèi)型來(lái)講,預(yù)處理器的任務(wù)有這幾部分:將其他文件包含到當(dāng)前文件中、定義宏、定義類(lèi)似函數(shù)的宏、實(shí)施條件編譯。接下來(lái),將詳細(xì)介紹預(yù)處理器的各項(xiàng)任務(wù)。15.2
宏
所謂宏,是程序中定義的用于替換復(fù)雜文本的簡(jiǎn)短文本。宏定義的一般格式如圖15-1所示:圖15-1宏的定義
宏名,即宏的名稱,在預(yù)編譯時(shí)被可替換文本替換??商鎿Q文本,即宏名所指代的文本內(nèi)容。在#define、宏名和可替換文本之間用空格(制表符)分隔。預(yù)編譯程序?qū)?define之后的第一個(gè)和第二個(gè)空格之間的文本作為宏名,其后所有文本作為“可替換文本”,而不管中間有多少個(gè)空格。15.2.1
宏展開(kāi)
在程序的預(yù)編譯期,預(yù)編譯程序會(huì)解析源代碼文本,執(zhí)行替換源程序的動(dòng)作,把宏引用的地方替換成定義處的文本。這個(gè)動(dòng)作叫做宏的展開(kāi)。圖15-2這段程序是宏的一個(gè)簡(jiǎn)單例子。圖15-2宏的使用
上圖代碼中cout后面的NUMBER在預(yù)編譯時(shí)會(huì)被替換成100。
注意:在源代碼文本中,并不是所有的宏名出現(xiàn)的地方都會(huì)進(jìn)行展開(kāi)。如果這個(gè)宏名出現(xiàn)在一個(gè)雙引號(hào)中,則該宏名就成了字符串的一部分,也就不會(huì)進(jìn)行展開(kāi)了。15.2.2替代常量
用宏替代字面常量,其好處是直觀、簡(jiǎn)潔、修改方
便。譬如對(duì)于圓周率,其值是一個(gè)無(wú)理常量
3.1415926···。如果在程序中每一處要使用圓周率地方都直接書(shū)寫(xiě)這個(gè)常量,那么源代碼修改起來(lái)就不大方便。一旦要求改變數(shù)字的精度,則所有使用的地方都要修改。無(wú)疑修改量就比較大,而且也不能保證每一處都修改對(duì)。
如果使用宏替換字面常量,而程序中使用圓周率的地方都使用宏而非圓周率本身,那么當(dāng)修改圓周率數(shù)值時(shí),只要修改這個(gè)宏定義即可,大大減少了編程人員的工作量。例如圖15-3所示代碼,用宏替換常量3.1415926。圖15-3宏替換字面常量
上圖中使用宏P(guān)I替換了常量3.1415926,若程序中的常量需要變化,則只需要更改宏定義即可。
相對(duì)于常量,使用宏也有缺點(diǎn)。字面常量在編譯時(shí)處理,有類(lèi)型信息。而宏則是在預(yù)編譯時(shí)展開(kāi),只是進(jìn)行單純的文本替換,沒(méi)有類(lèi)型信息。因此對(duì)于一些類(lèi)型要求比較嚴(yán)格的地方,使用宏有一定的風(fēng)險(xiǎn)。
定義宏時(shí)可以使用前面已經(jīng)定義的宏。例如:假設(shè)已經(jīng)存在一個(gè)宏NUMBER,其替換的文本是100,當(dāng)定義新的宏時(shí),可以在宏的可替換文本中使用NUMBER,如圖15-4所示。圖15-4宏展開(kāi)15.2.3替代運(yùn)算符 除了可以用宏替代字面常量,還可以用宏替代某些運(yùn)算符,包括加減乘除、邏輯與和邏輯非等,甚至函數(shù)和語(yǔ)句塊的花括號(hào)。利用這些宏定義,可以編寫(xiě)出貌似違反C++語(yǔ)法、但實(shí)際上合法的源代碼。例如:如圖15-5所示的代碼。圖15-5宏替代運(yùn)算符15.3
帶參數(shù)的宏
簡(jiǎn)單的宏定義,如上節(jié)例子所示,只能進(jìn)行簡(jiǎn)單的文字替換,擴(kuò)展能力有限。如果宏能夠像函數(shù)那樣帶有參數(shù),并根據(jù)參數(shù)的不同自動(dòng)展開(kāi)成不同的文本,則宏的擴(kuò)展能力將大大提高。實(shí)際上,在C++標(biāo)準(zhǔn)中,可以利用預(yù)處理命令#define定義帶參數(shù)的宏。15.3.1
定義帶參數(shù)的宏
有參數(shù)宏的宏名后需要帶參數(shù),其語(yǔ)法格式如圖15-6所示:圖15-6帶參數(shù)宏的定義1
若可替換文本一行寫(xiě)不完,可以分成多行,并在每一行的末尾加上分行的符號(hào)“\”(除了最后一行)。其語(yǔ)法格式如圖15-7所示:圖15-7帶參數(shù)宏的定義2
注意:要定義帶參數(shù)的宏,則在宏名和左括號(hào)之間不能有空格(或制表符),否則就成了普通的宏定義,括號(hào)及其里面的內(nèi)容也將會(huì)成為可替換文本的一部分。但是,在括號(hào)中的各個(gè)參數(shù)之間可以任意添加空格。
在可替換文本中可以引用括號(hào)中的參數(shù),從而根據(jù)參數(shù)的不同,展開(kāi)成不同的源代碼文本。例如,下例定義了一個(gè)宏,接受兩個(gè)參數(shù),然后比較兩個(gè)參數(shù)的大小,并輸出其中的較小值,示例代碼如圖15-8所示。圖15-8帶參數(shù)的宏
注意:在上述例子中使用了帶參數(shù)的宏
LESS,但是在語(yǔ)句的結(jié)尾處并沒(méi)有分號(hào)。乍一看好像不符合C++的語(yǔ)法規(guī)范,但是實(shí)際上并沒(méi)有錯(cuò)。這是因?yàn)楹晔窃陬A(yù)編譯時(shí)處理的,而不是編譯期。而且,宏展開(kāi)后的結(jié)果符合C++語(yǔ)法規(guī)范,所以這些語(yǔ)句的結(jié)尾處沒(méi)有分號(hào)并不會(huì)出錯(cuò)。15.3.2
注意宏展開(kāi)的結(jié)果
宏的行為有時(shí)候和所認(rèn)知的行為會(huì)有些不同,主要原因是通常認(rèn)為宏作為語(yǔ)句塊,會(huì)優(yōu)先執(zhí)行,但這是不對(duì)的,宏并沒(méi)有那么“聰明”,或者可以說(shuō)編譯器并沒(méi)有那么“聰明”,預(yù)編譯器所做的只是忠實(shí)地將宏展開(kāi)。我們來(lái)看圖15-9所示的程序,預(yù)編譯器不會(huì)自作聰明為“x+x”加括號(hào),因此最后輸出的結(jié)果是30。圖15-9帶參數(shù)宏的展開(kāi)
如果憑借第一印象,很可能會(huì)做這樣的計(jì)算:5*(5+5)=50。但是,預(yù)編譯器并不這么認(rèn)為,而會(huì)做這樣的解釋:5*5+5=30。忠實(shí)的將原來(lái)的宏展開(kāi)。所以讀者寫(xiě)宏的時(shí)候一定要小心,預(yù)防宏的行為不符合預(yù)期。
若想讓結(jié)果輸出為50,只要給宏加上括號(hào)即可,如圖15-10所示。圖15-10展開(kāi)宏時(shí)注意括號(hào)
上圖中的“cout<<5*INT(5)<<endl;”經(jīng)預(yù)處理后,轉(zhuǎn)換為“cout<<5*(5+5)<<endl;”。15.3.3帶參數(shù)的宏與函數(shù)的比較帶參數(shù)的宏在定義和使用方面同函數(shù)非常相似,都可以接受參數(shù),并且可以根據(jù)不同的參數(shù)產(chǎn)生不同的結(jié)果。但是這兩者畢竟是不同的東西,存在很大差異,例如:間,程序運(yùn)行時(shí)并不存在。次,則其語(yǔ)句也將重復(fù)多次。換。息。(1)函數(shù)在運(yùn)行時(shí)依然存在,但宏只存在于預(yù)編譯期(2)函數(shù)所占內(nèi)存空間只有一份,但如果宏被調(diào)用多(3)函數(shù)調(diào)用有時(shí)間和空間方面的開(kāi)銷(xiāo),但宏沒(méi)有。(4)函數(shù)接受參數(shù)的傳遞,但宏只是對(duì)參數(shù)的簡(jiǎn)單替(5)函數(shù)的參數(shù)有類(lèi)型信息,但宏的參數(shù)沒(méi)有類(lèi)型信(6)函數(shù)可以調(diào)試,宏不可以。15.4
條件編譯
條件編譯指令可以對(duì)程序源代碼的各部分有選擇的進(jìn)行編譯,該過(guò)程就稱為條件編譯。常見(jiàn)的條件編譯指令有#if、#elif、#else、#ifndef、#ifdef、#endif等。它們主用來(lái)在編譯時(shí)進(jìn)行選擇,屏蔽掉一些代碼。
以下內(nèi)容將對(duì)條件編譯內(nèi)容進(jìn)行相關(guān)介紹。15.4.1宏指令
C++中的宏指令都是在ANSI標(biāo)準(zhǔn)中的,表
15-1列出了常見(jiàn)的宏指令。表15-1常見(jiàn)的宏指令
#define在前面已經(jīng)介紹過(guò)了,這里就不再討論。#error可以強(qiáng)迫編譯程序停止編譯,用來(lái)在編譯期間檢查環(huán)境是否符合要求或
者與約束的條件發(fā)生了沖突。其使用格式
如圖15-11所示:#define#error#include#if#else#elif#endif#ifdef#ifndef#undef#line#pragma圖15-11強(qiáng)迫停止編譯指令#error
當(dāng)程序遇到這個(gè)關(guān)鍵字,就會(huì)停止編譯,產(chǎn)生一個(gè)錯(cuò)誤信息,并且輸出后面的token-string。例如:#if
!defined(
cplusplus)#error
C++
compiler
required.#endif
上面這段代碼的意思是在預(yù)編譯期間檢查當(dāng)前的程序是否是C++編譯環(huán)境,如果不是,就定義#error,讓編譯器停止編譯。#if和#endif會(huì)在后面介紹,這里只需要將#if視為普通的if判斷
語(yǔ)句,將#endif看成if判斷的結(jié)束。
#include使編譯程序?qū)?include所指向的源文件導(dǎo)入進(jìn)當(dāng)前的源文件,被包含的文件必須被尖括號(hào)或者引號(hào)包圍起來(lái)。#if,#else,#elif,#endif,#ifdef和#ifndef屬于條件編命令,可以對(duì)程序的各個(gè)部分有選擇地進(jìn)行編譯。#undef命令用來(lái)取消前面定義過(guò)的宏名。來(lái)看一個(gè)宏使用的例子,如圖15-12所示。圖15-12宏指令的使用
在程序的第一行定義了NUMBER宏,在第六行輸出10。在第八行取消了NUMBER宏的定義,所以后面會(huì)輸出“找不到
NUMBER的定義”。15.4.2
條件編譯
#if,#else,#elif,#endif,#ifdef和#ifn是條件編譯命令。所謂條件編譯,就是可以將源文件中的代碼分成幾個(gè)部分,有選擇的編譯各個(gè)部分。對(duì)于前三個(gè)宏,可以理解為if,else和else
if,#endif表示這個(gè)條件編譯選擇的結(jié)束。看圖15-13中的代碼:圖15-13用#if、#else和#elif進(jìn)行條件編譯
程序的第一行定義NUMBER宏,令其等于5。后面的代碼判斷是否等于相應(yīng)的值,選擇性地編譯后面的語(yǔ)句,由于定義NUMBER等于5,編譯器輸出“Number
is
equal
to
5”
另一種條件編譯的方式就是使用#ifdef和#ifndef。同樣,這兩個(gè)命令也用#endif作為作用域的結(jié)束。#ifdef判斷后面的標(biāo)識(shí)符是否被定義,通常都是指預(yù)定義的宏,#ifndef就是#ifdef的取反??磮D15-14中的程序。圖15-14用#ifdef和#ifndef進(jìn)行條件編譯
第四行判斷是否定義DEBUG標(biāo)識(shí)符(在第一行定義),然后編譯后面的語(yǔ)句。再來(lái)看一個(gè)例子,示例代碼如圖15-15所示。圖15-15條件編譯1
代碼第四行的disp函數(shù)中采用了條件編譯指令編寫(xiě),因?yàn)槌绦蛑卸x了WINDOWS宏,所以輸出“本程序當(dāng)前運(yùn)行在Windows環(huán)境下”,假若將“define
WINDOWS”語(yǔ)句注釋掉,程序?qū)⑤敵觥氨境绦蜻\(yùn)行在非
WINDOWS環(huán)境下”,如圖15-16所示。圖15-16條件編譯2
代碼第六行“#if
defined(WINDOWS)”語(yǔ)句中的“define(宏名)”的含義是若宏被定義則返回1,否則返回0,defined之間可以進(jìn)行邏輯運(yùn)算。15.5
文件包含和頭文件衛(wèi)士15.5.1
包含文件指令
包含文件的預(yù)處理指令是“#include”,其作用就是將別的文件包含到當(dāng)前文件中。
在實(shí)際使用中,一般被包含的文件是頭文
件。頭文件中一般是函數(shù)、類(lèi)等的聲明,
包含到當(dāng)前文件中后,就可以在當(dāng)前文件
中引用頭文件中的函數(shù)、類(lèi)等。例如對(duì)于
下圖中的源代碼文件進(jìn)行預(yù)編譯后產(chǎn)生的
臨時(shí)源代碼文件如圖15-17所示。圖15-17包含頭文件按照C/C++的語(yǔ)法要求,要使用某個(gè)標(biāo)識(shí)符(變量、函數(shù)、類(lèi)等),必須在使用之前
先聲明。雖然在Some.cpp文件中沒(méi)有函數(shù)
SomeFunction的聲明,但是由于包含了頭文件header.h,所以仍然可以使用該函數(shù)。15.5.2
搜索頭文件
使用“#include”指令包含頭文件時(shí),其后的頭文件有兩種方式,一種是使用雙引號(hào),一種是使用尖括號(hào)。如圖15-18所示。圖15-18頭文件的兩種包含方式如果文件名用尖括號(hào)括起來(lái),表明這個(gè)文件是一個(gè)工程或C++標(biāo)準(zhǔn)庫(kù)頭文件。預(yù)編譯器會(huì)首先搜索在工程中預(yù)定義的目錄,然后搜索C++編譯器的安裝目錄??梢酝ㄟ^(guò)設(shè)置工程搜索路徑環(huán)境變量或命令行選項(xiàng)來(lái)修改。如果文件名用一對(duì)引號(hào)括起來(lái),則表明該文件是用戶提供的頭文件。預(yù)編譯器首先從當(dāng)前文件目錄開(kāi)始搜索,如果找不到,就從工程中定義的目錄和編輯器的安裝目錄查找。15.5.3
頭文件衛(wèi)士
當(dāng)工程中文件眾多是,很有可能出現(xiàn)一個(gè)頭文件被多次包含的情況。但是,C++中同一個(gè)類(lèi)型被聲明兩次是非法的,來(lái)看一個(gè)例子。,如圖15-19所示,文件Animal.h中定義了類(lèi)CAnimal,文件pig.h中定義了類(lèi)
CPig,文件horse.h中定義了類(lèi)CHorse,主函數(shù)中用類(lèi)CPig和CHorse分別定義了變量
pig和horse。圖15-19重復(fù)定義1
如上圖所示,在main函數(shù)中使用了CHorse和CPig兩個(gè)類(lèi),所以需要包含這兩個(gè)類(lèi)的頭文件,但是,這兩個(gè)類(lèi)都包含CAnimal的定義,編譯器不知道該選擇哪個(gè),會(huì)報(bào)告重復(fù)定義的錯(cuò)誤。如下圖15-20所示,對(duì)上述代碼進(jìn)行編譯,編譯器給出了錯(cuò)誤信息。圖15-20 重復(fù)定義2為了避免因?yàn)橹貜?fù)包含頭文件而重復(fù)定義類(lèi)型,導(dǎo)致編譯失敗,這個(gè)時(shí)候就需要頭文件衛(wèi)士的幫助。所謂頭文件衛(wèi)士就是用一組宏命令將頭文件包起來(lái),使其不會(huì)被重復(fù)包含,看Animal.h的例子:#ifndef
ANIMAL_H#define
ANIMAL_H??#endif#ifndef是定義在頭文件所有內(nèi)容之前的,#endif是定義在所有內(nèi)容之后的,用預(yù)編譯命令#ifndef和#endif將整個(gè)Animal.h頭文件中的內(nèi)容包含起來(lái)。這樣便不會(huì)有編譯錯(cuò)誤了,下面來(lái)分析一下。
當(dāng)main函數(shù)所在文件第一次包含pig.h文件時(shí),同時(shí)會(huì)導(dǎo)入Animal.h中的內(nèi)容,這時(shí)預(yù)編譯器分析當(dāng)前文件沒(méi)有定義ANIMAL_H,就會(huì)在當(dāng)前文件中定義。當(dāng)再次包含horse.h文件時(shí),導(dǎo)入Animal.h,發(fā)現(xiàn)文件中已經(jīng)定義了ANIMAL_H,就會(huì)查找#else或者
#endif,這時(shí)會(huì)直接跳轉(zhuǎn)到#endif,不會(huì)包含當(dāng)前文件的任何內(nèi)容。
同理,如果有人想繼續(xù)從Horse上繼承,例如Whitehorse
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2026甘肅張掖市生態(tài)環(huán)境局甘州分局招聘環(huán)境監(jiān)管監(jiān)測(cè)輔助人員4人備考考試題庫(kù)附答案解析
- 2026年上半年黑龍江事業(yè)單位聯(lián)考牡丹江市招聘817人備考考試試題附答案解析
- 2026山東日照市市屬事業(yè)單位招聘初級(jí)綜合類(lèi)崗位人員參考考試題庫(kù)附答案解析
- 2026年甘肅酒泉敦煌空港經(jīng)創(chuàng)發(fā)展有限公司招聘參考考試題庫(kù)附答案解析
- 2026廣西北海市合浦縣民政局招錄城鎮(zhèn)公益性崗位人員11人備考考試題庫(kù)附答案解析
- 2026年吉安吉星養(yǎng)老服務(wù)有限公司招聘護(hù)理員參考考試試題附答案解析
- 涪城公安招聘24名警務(wù)輔助人員備考考試題庫(kù)附答案解析
- 2026河南鄭州嵩山少林武術(shù)職業(yè)學(xué)院招聘70人參考考試試題附答案解析
- 2026河南信陽(yáng)圣德健康養(yǎng)護(hù)中心招聘?jìng)淇伎荚囶}庫(kù)附答案解析
- 2026云南昆明市呈貢區(qū)中智集團(tuán)人力資源服務(wù)運(yùn)營(yíng)管理崗招聘1人備考考試題庫(kù)附答案解析
- QCT1067.5-2023汽車(chē)電線束和電器設(shè)備用連接器第5部分:設(shè)備連接器(插座)的型式和尺寸
- 胎兒宮內(nèi)生長(zhǎng)遲緩的表觀遺傳學(xué)改變
- 防腐保溫施工應(yīng)急預(yù)案
- 票據(jù)業(yè)務(wù)承諾函
- 幼兒園中班語(yǔ)言課《愛(ài)心樹(shù)》教學(xué)設(shè)計(jì)【含教學(xué)反思】
- 巖溶地區(qū)橋梁樁基施工監(jiān)控及質(zhì)量控制
- 美國(guó)AAMA檢驗(yàn)標(biāo)準(zhǔn)
- 三片罐制作工藝流程
- 一年級(jí)《背土豆》教學(xué)反思
- 37000DWT-近海散貨船-船舶建造檢驗(yàn)項(xiàng)目表
- 軟件項(xiàng)目系統(tǒng)巡檢報(bào)告
評(píng)論
0/150
提交評(píng)論