




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
第15章 預(yù)處理和宏 程序在編譯之前先要經(jīng)過預(yù)編譯階段。預(yù)編譯的任務(wù)是根據(jù)程序中的宏指令補(bǔ)充和完善源代碼。有了宏指令,可以使得某些源代碼編輯任務(wù)變得輕松,同時(shí)也可以控制哪些源代碼需要編譯,哪些不需要編譯。15.1
預(yù)處理概述
預(yù)處理器是專門用來處理宏指令的程序。在編譯器運(yùn)行之前,會先運(yùn)行預(yù)處理器,查找所有的預(yù)處理指令。預(yù)處理指令以“#”開頭,而且不以“;”結(jié)束,以區(qū)別于一般的語句。預(yù)處理器根據(jù)預(yù)處理指令生成新的源代碼文件(臨時(shí)文件,可以通過編譯器的選項(xiàng)輸出到指定目錄中)。
編譯器的作用是把源代碼轉(zhuǎn)換成匯編語言或機(jī)器指令。但是,編譯器并不是直接編譯程序員寫成的源文件,而是編譯經(jīng)預(yù)處理器處理后所產(chǎn)生的新的源文件。這些新的源文件經(jīng)過編譯器生成目標(biāo)文件,再經(jīng)過鏈接器生成最終的可執(zhí)行程序。
預(yù)處理器的任務(wù)就是執(zhí)行源代碼中的預(yù)處理指令,并對源代碼進(jìn)行相應(yīng)的處理。因此,從預(yù)處理指令的類型來講,預(yù)處理器的任務(wù)有這幾部分:將其他文件包含到當(dāng)前文件中、定義宏、定義類似函數(shù)的宏、實(shí)施條件編譯。接下來,將詳細(xì)介紹預(yù)處理器的各項(xiàng)任務(wù)。15.2
宏
所謂宏,是程序中定義的用于替換復(fù)雜文本的簡短文本。宏定義的一般格式如圖15-1所示:圖15-1宏的定義
宏名,即宏的名稱,在預(yù)編譯時(shí)被可替換文本替換??商鎿Q文本,即宏名所指代的文本內(nèi)容。在#define、宏名和可替換文本之間用空格(制表符)分隔。預(yù)編譯程序?qū)?define之后的第一個(gè)和第二個(gè)空格之間的文本作為宏名,其后所有文本作為“可替換文本”,而不管中間有多少個(gè)空格。15.2.1
宏展開
在程序的預(yù)編譯期,預(yù)編譯程序會解析源代碼文本,執(zhí)行替換源程序的動(dòng)作,把宏引用的地方替換成定義處的文本。這個(gè)動(dòng)作叫做宏的展開。圖15-2這段程序是宏的一個(gè)簡單例子。圖15-2宏的使用
上圖代碼中cout后面的NUMBER在預(yù)編譯時(shí)會被替換成100。
注意:在源代碼文本中,并不是所有的宏名出現(xiàn)的地方都會進(jìn)行展開。如果這個(gè)宏名出現(xiàn)在一個(gè)雙引號中,則該宏名就成了字符串的一部分,也就不會進(jìn)行展開了。15.2.2替代常量
用宏替代字面常量,其好處是直觀、簡潔、修改方
便。譬如對于圓周率,其值是一個(gè)無理常量
3.1415926···。如果在程序中每一處要使用圓周率地方都直接書寫這個(gè)常量,那么源代碼修改起來就不大方便。一旦要求改變數(shù)字的精度,則所有使用的地方都要修改。無疑修改量就比較大,而且也不能保證每一處都修改對。
如果使用宏替換字面常量,而程序中使用圓周率的地方都使用宏而非圓周率本身,那么當(dāng)修改圓周率數(shù)值時(shí),只要修改這個(gè)宏定義即可,大大減少了編程人員的工作量。例如圖15-3所示代碼,用宏替換常量3.1415926。圖15-3宏替換字面常量
上圖中使用宏P(guān)I替換了常量3.1415926,若程序中的常量需要變化,則只需要更改宏定義即可。
相對于常量,使用宏也有缺點(diǎn)。字面常量在編譯時(shí)處理,有類型信息。而宏則是在預(yù)編譯時(shí)展開,只是進(jìn)行單純的文本替換,沒有類型信息。因此對于一些類型要求比較嚴(yán)格的地方,使用宏有一定的風(fēng)險(xiǎn)。
定義宏時(shí)可以使用前面已經(jīng)定義的宏。例如:假設(shè)已經(jīng)存在一個(gè)宏NUMBER,其替換的文本是100,當(dāng)定義新的宏時(shí),可以在宏的可替換文本中使用NUMBER,如圖15-4所示。圖15-4宏展開15.2.3替代運(yùn)算符 除了可以用宏替代字面常量,還可以用宏替代某些運(yùn)算符,包括加減乘除、邏輯與和邏輯非等,甚至函數(shù)和語句塊的花括號。利用這些宏定義,可以編寫出貌似違反C++語法、但實(shí)際上合法的源代碼。例如:如圖15-5所示的代碼。圖15-5宏替代運(yùn)算符15.3
帶參數(shù)的宏
簡單的宏定義,如上節(jié)例子所示,只能進(jìn)行簡單的文字替換,擴(kuò)展能力有限。如果宏能夠像函數(shù)那樣帶有參數(shù),并根據(jù)參數(shù)的不同自動(dòng)展開成不同的文本,則宏的擴(kuò)展能力將大大提高。實(shí)際上,在C++標(biāo)準(zhǔn)中,可以利用預(yù)處理命令#define定義帶參數(shù)的宏。15.3.1
定義帶參數(shù)的宏
有參數(shù)宏的宏名后需要帶參數(shù),其語法格式如圖15-6所示:圖15-6帶參數(shù)宏的定義1
若可替換文本一行寫不完,可以分成多行,并在每一行的末尾加上分行的符號“\”(除了最后一行)。其語法格式如圖15-7所示:圖15-7帶參數(shù)宏的定義2
注意:要定義帶參數(shù)的宏,則在宏名和左括號之間不能有空格(或制表符),否則就成了普通的宏定義,括號及其里面的內(nèi)容也將會成為可替換文本的一部分。但是,在括號中的各個(gè)參數(shù)之間可以任意添加空格。
在可替換文本中可以引用括號中的參數(shù),從而根據(jù)參數(shù)的不同,展開成不同的源代碼文本。例如,下例定義了一個(gè)宏,接受兩個(gè)參數(shù),然后比較兩個(gè)參數(shù)的大小,并輸出其中的較小值,示例代碼如圖15-8所示。圖15-8帶參數(shù)的宏
注意:在上述例子中使用了帶參數(shù)的宏
LESS,但是在語句的結(jié)尾處并沒有分號。乍一看好像不符合C++的語法規(guī)范,但是實(shí)際上并沒有錯(cuò)。這是因?yàn)楹晔窃陬A(yù)編譯時(shí)處理的,而不是編譯期。而且,宏展開后的結(jié)果符合C++語法規(guī)范,所以這些語句的結(jié)尾處沒有分號并不會出錯(cuò)。15.3.2
注意宏展開的結(jié)果
宏的行為有時(shí)候和所認(rèn)知的行為會有些不同,主要原因是通常認(rèn)為宏作為語句塊,會優(yōu)先執(zhí)行,但這是不對的,宏并沒有那么“聰明”,或者可以說編譯器并沒有那么“聰明”,預(yù)編譯器所做的只是忠實(shí)地將宏展開。我們來看圖15-9所示的程序,預(yù)編譯器不會自作聰明為“x+x”加括號,因此最后輸出的結(jié)果是30。圖15-9帶參數(shù)宏的展開
如果憑借第一印象,很可能會做這樣的計(jì)算:5*(5+5)=50。但是,預(yù)編譯器并不這么認(rèn)為,而會做這樣的解釋:5*5+5=30。忠實(shí)的將原來的宏展開。所以讀者寫宏的時(shí)候一定要小心,預(yù)防宏的行為不符合預(yù)期。
若想讓結(jié)果輸出為50,只要給宏加上括號即可,如圖15-10所示。圖15-10展開宏時(shí)注意括號
上圖中的“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í)并不存在。次,則其語句也將重復(fù)多次。換。息。(1)函數(shù)在運(yùn)行時(shí)依然存在,但宏只存在于預(yù)編譯期(2)函數(shù)所占內(nèi)存空間只有一份,但如果宏被調(diào)用多(3)函數(shù)調(diào)用有時(shí)間和空間方面的開銷,但宏沒有。(4)函數(shù)接受參數(shù)的傳遞,但宏只是對參數(shù)的簡單替(5)函數(shù)的參數(shù)有類型信息,但宏的參數(shù)沒有類型信(6)函數(shù)可以調(diào)試,宏不可以。15.4
條件編譯
條件編譯指令可以對程序源代碼的各部分有選擇的進(jìn)行編譯,該過程就稱為條件編譯。常見的條件編譯指令有#if、#elif、#else、#ifndef、#ifdef、#endif等。它們主用來在編譯時(shí)進(jìn)行選擇,屏蔽掉一些代碼。
以下內(nèi)容將對條件編譯內(nèi)容進(jìn)行相關(guān)介紹。15.4.1宏指令
C++中的宏指令都是在ANSI標(biāo)準(zhǔn)中的,表
15-1列出了常見的宏指令。表15-1常見的宏指令
#define在前面已經(jīng)介紹過了,這里就不再討論。#error可以強(qiáng)迫編譯程序停止編譯,用來在編譯期間檢查環(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)鍵字,就會停止編譯,產(chǎn)生一個(gè)錯(cuò)誤信息,并且輸出后面的token-string。例如:#if
!defined(
cplusplus)#error
C++
compiler
required.#endif
上面這段代碼的意思是在預(yù)編譯期間檢查當(dāng)前的程序是否是C++編譯環(huán)境,如果不是,就定義#error,讓編譯器停止編譯。#if和#endif會在后面介紹,這里只需要將#if視為普通的if判斷
語句,將#endif看成if判斷的結(jié)束。
#include使編譯程序?qū)?include所指向的源文件導(dǎo)入進(jìn)當(dāng)前的源文件,被包含的文件必須被尖括號或者引號包圍起來。#if,#else,#elif,#endif,#ifdef和#ifndef屬于條件編命令,可以對程序的各個(gè)部分有選擇地進(jìn)行編譯。#undef命令用來取消前面定義過的宏名。來看一個(gè)宏使用的例子,如圖15-12所示。圖15-12宏指令的使用
在程序的第一行定義了NUMBER宏,在第六行輸出10。在第八行取消了NUMBER宏的定義,所以后面會輸出“找不到
NUMBER的定義”。15.4.2
條件編譯
#if,#else,#elif,#endif,#ifdef和#ifn是條件編譯命令。所謂條件編譯,就是可以將源文件中的代碼分成幾個(gè)部分,有選擇的編譯各個(gè)部分。對于前三個(gè)宏,可以理解為if,else和else
if,#endif表示這個(gè)條件編譯選擇的結(jié)束。看圖15-13中的代碼:圖15-13用#if、#else和#elif進(jìn)行條件編譯
程序的第一行定義NUMBER宏,令其等于5。后面的代碼判斷是否等于相應(yīng)的值,選擇性地編譯后面的語句,由于定義NUMBER等于5,編譯器輸出“Number
is
equal
to
5”
另一種條件編譯的方式就是使用#ifdef和#ifndef。同樣,這兩個(gè)命令也用#endif作為作用域的結(jié)束。#ifdef判斷后面的標(biāo)識符是否被定義,通常都是指預(yù)定義的宏,#ifndef就是#ifdef的取反??磮D15-14中的程序。圖15-14用#ifdef和#ifndef進(jìn)行條件編譯
第四行判斷是否定義DEBUG標(biāo)識符(在第一行定義),然后編譯后面的語句。再來看一個(gè)例子,示例代碼如圖15-15所示。圖15-15條件編譯1
代碼第四行的disp函數(shù)中采用了條件編譯指令編寫,因?yàn)槌绦蛑卸x了WINDOWS宏,所以輸出“本程序當(dāng)前運(yùn)行在Windows環(huán)境下”,假若將“define
WINDOWS”語句注釋掉,程序?qū)⑤敵觥氨境绦蜻\(yùn)行在非
WINDOWS環(huán)境下”,如圖15-16所示。圖15-16條件編譯2
代碼第六行“#if
defined(WINDOWS)”語句中的“define(宏名)”的含義是若宏被定義則返回1,否則返回0,defined之間可以進(jìn)行邏輯運(yùn)算。15.5
文件包含和頭文件衛(wèi)士15.5.1
包含文件指令
包含文件的預(yù)處理指令是“#include”,其作用就是將別的文件包含到當(dāng)前文件中。
在實(shí)際使用中,一般被包含的文件是頭文
件。頭文件中一般是函數(shù)、類等的聲明,
包含到當(dāng)前文件中后,就可以在當(dāng)前文件
中引用頭文件中的函數(shù)、類等。例如對于
下圖中的源代碼文件進(jìn)行預(yù)編譯后產(chǎn)生的
臨時(shí)源代碼文件如圖15-17所示。圖15-17包含頭文件按照C/C++的語法要求,要使用某個(gè)標(biāo)識符(變量、函數(shù)、類等),必須在使用之前
先聲明。雖然在Some.cpp文件中沒有函數(shù)
SomeFunction的聲明,但是由于包含了頭文件header.h,所以仍然可以使用該函數(shù)。15.5.2
搜索頭文件
使用“#include”指令包含頭文件時(shí),其后的頭文件有兩種方式,一種是使用雙引號,一種是使用尖括號。如圖15-18所示。圖15-18頭文件的兩種包含方式如果文件名用尖括號括起來,表明這個(gè)文件是一個(gè)工程或C++標(biāo)準(zhǔn)庫頭文件。預(yù)編譯器會首先搜索在工程中預(yù)定義的目錄,然后搜索C++編譯器的安裝目錄??梢酝ㄟ^設(shè)置工程搜索路徑環(huán)境變量或命令行選項(xiàng)來修改。如果文件名用一對引號括起來,則表明該文件是用戶提供的頭文件。預(yù)編譯器首先從當(dāng)前文件目錄開始搜索,如果找不到,就從工程中定義的目錄和編輯器的安裝目錄查找。15.5.3
頭文件衛(wèi)士
當(dāng)工程中文件眾多是,很有可能出現(xiàn)一個(gè)頭文件被多次包含的情況。但是,C++中同一個(gè)類型被聲明兩次是非法的,來看一個(gè)例子。,如圖15-19所示,文件Animal.h中定義了類CAnimal,文件pig.h中定義了類
CPig,文件horse.h中定義了類CHorse,主函數(shù)中用類CPig和CHorse分別定義了變量
pig和horse。圖15-19重復(fù)定義1
如上圖所示,在main函數(shù)中使用了CHorse和CPig兩個(gè)類,所以需要包含這兩個(gè)類的頭文件,但是,這兩個(gè)類都包含CAnimal的定義,編譯器不知道該選擇哪個(gè),會報(bào)告重復(fù)定義的錯(cuò)誤。如下圖15-20所示,對上述代碼進(jìn)行編譯,編譯器給出了錯(cuò)誤信息。圖15-20 重復(fù)定義2為了避免因?yàn)橹貜?fù)包含頭文件而重復(fù)定義類型,導(dǎo)致編譯失敗,這個(gè)時(shí)候就需要頭文件衛(wèi)士的幫助。所謂頭文件衛(wèi)士就是用一組宏命令將頭文件包起來,使其不會被重復(fù)包含,看Animal.h的例子:#ifndef
ANIMAL_H#define
ANIMAL_H??#endif#ifndef是定義在頭文件所有內(nèi)容之前的,#endif是定義在所有內(nèi)容之后的,用預(yù)編譯命令#ifndef和#endif將整個(gè)Animal.h頭文件中的內(nèi)容包含起來。這樣便不會有編譯錯(cuò)誤了,下面來分析一下。
當(dāng)main函數(shù)所在文件第一次包含pig.h文件時(shí),同時(shí)會導(dǎo)入Animal.h中的內(nèi)容,這時(shí)預(yù)編譯器分析當(dāng)前文件沒有定義ANIMAL_H,就會在當(dāng)前文件中定義。當(dāng)再次包含horse.h文件時(shí),導(dǎo)入Animal.h,發(fā)現(xiàn)文件中已經(jīng)定義了ANIMAL_H,就會查找#else或者
#endif,這時(shí)會直接跳轉(zhuǎn)到#endif,不會包含當(dāng)前文件的任何內(nèi)容。
同理,如果有人想繼續(xù)從Horse上繼承,例如Whitehorse
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 社區(qū)居民健康教育活動(dòng)實(shí)施方案
- 少兒女生教育體系構(gòu)建與實(shí)踐路徑
- 心理健康教育體系構(gòu)建
- 學(xué)校冬季安全教育
- 康寶萊體重管理課
- 【荊州】2025年湖北荊州市石首市企事業(yè)單位人才引進(jìn)170人筆試歷年典型考題及考點(diǎn)剖析附帶答案詳解
- 小學(xué)一年級清廉教學(xué)課件
- 幼兒天氣教學(xué)課件
- 課件教學(xué)的問題
- 整體護(hù)理課件
- 2024年省石棉縣人力資源和社會保障局關(guān)于公開考核招考綜合類事業(yè)單位工作人員高頻考題難、易錯(cuò)點(diǎn)模擬試題(共500題)附帶答案詳解
- JJG 971-2019液位計(jì)檢定規(guī)程
- 云南省楚雄州2022-2023學(xué)年高一下學(xué)期期末考試化學(xué)試題(解析版)
- 自動(dòng)售貨機(jī)投放方案
- 規(guī)范預(yù)防接種知情告知課件
- 2023陜西省中考英語真題試卷和答案
- 中國傳媒大學(xué)開題報(bào)告模板
- 水電預(yù)埋預(yù)留培訓(xùn)課件
- 注塑車間工作總結(jié)計(jì)劃
- WS-T 10010-2023 衛(wèi)生監(jiān)督快速檢測通用要求(代替WS-T 458-2014)
- 醫(yī)院零星維修工程投標(biāo)方案(技術(shù)標(biāo))
評論
0/150
提交評論