版權說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權,請進行舉報或認領
文檔簡介
C語言與程序設計
TheCProgrammingLanguage
第6章編譯預處理
華中科技大學計算機學院
盧萍2/4/20231華中科技大學計算機學院C語言課程組第6章編譯預處理
編譯預處理:對源程序進行編譯之前所作的工作,它由預處理程序負責完成。編譯時,系統(tǒng)將自動引用預處理程序?qū)υ闯绦蛑械念A處理指令進行處理。預處理指令:以“#”號開始的指令。本章重點介紹:
#include、#define和條件編譯的功能和用法,以及assert宏的使用。2/4/20232華中科技大學計算機學院C語言課程組6.1文件包含#include用指定文件的內(nèi)容取代該預處理指令行,有2種一般形式:(1)#include<文件名>
在指定的標準目錄下尋找被包含文件(2)
#include"文件名"
首先在用戶當前目錄中尋找被包含文件,若找不到,再在指定的標準目錄下尋找
2/4/20233華中科技大學計算機學院C語言課程組6.2宏定義#define用一個標識符來表示一個字符串,
#define標識符字符串宏名:被定義的標識符。在編譯預處理時,宏代換(宏展開):用字符串去取代宏名預處理前#defineM(y*y+3*y)int
main(void){int
s,y;
printf("Inputanumber:");
scanf("%d",&y);s=3*M+4*M+y*M;
printf("s=%d\n",s);return0;}預處理后int
main(void){int
s,y;
printf("Inputanumber:");
scanf("%d",&y);s=3*(y*y+3*y)+4*(y*y+3*y)
+y*(y*y+3*y);
printf("s=%d\n",s);return0;}2/4/20234華中科技大學計算機學院C語言課程組6.3帶參數(shù)的宏定義#define標識符(標識符,標識符,…,標識符)字符串宏名形式參數(shù)宏調(diào)用:給出實參宏展開:(1)用字符串替換宏,(2)用實參去替換形參2/4/20235華中科技大學計算機學院C語言課程組例定義計算x2的宏
#definesquare(x)((x)*(x))宏調(diào)用:square(a+1)宏展開:
((a+1)*(a+1))宏調(diào)用:square(square(a))宏展開:((((a)*(a)))*(((a)*(a))))2/4/20236華中科技大學計算機學院C語言課程組為什么要這么多的括號?
考慮:#defineSQ(x)x*x宏調(diào)用:
SQ(a+b)宏展開:a+b*a+b
/*與(a+b)*(a+b)不同*/再考慮:#defineSQ(x)(x)*(x)宏調(diào)用:
27/SQ(3)宏展開:27/(3)*(3)
/*值27,與27/32不同*/定義帶參數(shù)的宏時,為了保證計算次序的正確性,表達式中的每個參數(shù)用括號括起來,整個表達式也用括號括起來。2/4/20237華中科技大學計算機學院C語言課程組注意:宏名和與左括號之間不能有空格
#defineSQ
(x)((x)*(x))被認為是無參宏定義。宏調(diào)用:SQ(3)宏展開:(x)((x)*(x))(3)/*顯然錯誤的*/2/4/20238華中科技大學計算機學院C語言課程組帶參的宏雖被認為不安全,但還是很有價值
#defineSQ(x)((x)*(x))宏調(diào)用:SQ(++a)宏展開:
((++a)*(++a))/*a加2次如是函數(shù)調(diào)用,將不會有問題*/宏節(jié)省了函數(shù)調(diào)用的開銷,程序運行速度更快,形式參數(shù)不分配內(nèi)存單元,不必作類型說明。但是,宏展開后使源程序增長。宏比較適合于經(jīng)常使用的簡短表達式,以及小的可重復的代碼段;當任務比較復雜,需要多行代碼才能實現(xiàn)時,或者要求程序越小越好時,就應該使用函數(shù)。2/4/20239華中科技大學計算機學院C語言課程組6.2.3空宏參數(shù)C99允許宏調(diào)用中任意或所有參數(shù)為空。例如:#defineADD(x,y)(x+y) /*定義兩數(shù)相加的宏*/x=ADD(2,3);y=ADD(,3); /*第1個參數(shù)為空*/預處理后變?yōu)椋簒=(2+3); /*x的值為5*/y=(+3);
/*y的值為3*/2/4/202310華中科技大學計算機學院C語言課程組6.2.4可變參數(shù)宏定義C99增加了可變參數(shù)宏(variadicmacros),允許像下面這樣定義可變參數(shù)宏:
#definedebug(format,...)printf(format,__VA_ARGS__)debug是一個可變參數(shù)宏,format是宏的一個參數(shù),省略號代表一個能夠改變的參數(shù)表,在每次被調(diào)用時,“…”被表示成零個或多個參數(shù)。內(nèi)建的預處理器標識符__VA_ARGS__用來把“…”部分傳遞給宏。當宏的調(diào)用展開時,實參就取代__VA_ARGS__。例如,宏調(diào)用
debug("x=%d\n,y=%d\n",10,20);
/*輸出x=10,y=20*/會被展開成:
printf("x=%d\n,y=%d\n",10,20);2/4/202311華中科技大學計算機學院C語言課程組【例6.2】用C99的可變參數(shù)宏,
打印調(diào)試信息。在編寫代碼的過程中,為了調(diào)試程序,經(jīng)常會輸出一些調(diào)試信息到屏幕上,隨著項目的調(diào)試,輸出的信息可能會越來越多,信息的輸出一般要調(diào)用printf等函數(shù)。但是,當調(diào)試完后,又需要手工將這些地方刪除或者注釋掉。這樣做工作量比較大,很麻煩。如何方便地處理這些用于輸出調(diào)試信息的語句?用C99的可變參數(shù)宏,可方便地輸出調(diào)試信息。源程序\ex6_2.c調(diào)試階段定義DEBUG宏,在需要輸出調(diào)試信息的地方用宏msg,調(diào)試成功后,軟件正式發(fā)行時,只需將第2行的#define指令刪除或注釋掉即可,非常方便。2/4/202312華中科技大學計算機學院C語言課程組6.2.5通用類型宏通用類型宏或者泛型宏(type-genericmacros)是一種編譯期技術,它允許開發(fā)人員根據(jù)宏的某個參數(shù)的類型來確定生成的內(nèi)容。早在C99時就有了通用類型宏的概念,只不過當時沒有對它進行標準化。C99中引用了頭文件<tgmath.h>,給開發(fā)人員提供大量初等數(shù)學函數(shù)接口。在C99中,程序員可以用不同的類型來調(diào)用sin函數(shù),比如:sin(1),實際上調(diào)用sin(1.0);sin(1.0F),實際上調(diào)用sinf(1.0F);sin(1.0L),實際上調(diào)用sinl(1.0L)。實際上,sin這個接口是一個宏,它會根據(jù)傳來的實際參數(shù)類型展開成特定的函數(shù)。sin就是一個通用類型宏。2/4/202313華中科技大學計算機學院C語言課程組關鍵字_GenericC11中引入了新關鍵字_Generic來實現(xiàn)通用類型宏,它根據(jù)第一個參數(shù)的類型和后面的類型-表達式關聯(lián)來實現(xiàn)編譯期的替換。利用_Generic,C99的sin可定義如下:#definesin(x)_Generic((x),longdouble:sinl,double:sinf,defalt:sin)(x)_Generic對第一個參數(shù)進行類型判斷,然后根據(jù)從第二參數(shù)開始的類型-表達式關聯(lián)表來進行編譯期替換。如果x為longdouble類型,那么_Generic((x),…)的結果為sinl,如果x為double類型,那么_Generic((x),…)的結果為sinf,否則結果為sin??梢姡鶕?jù)x的類型,宏sin(x)轉換為sinl(x),sinf(x)或sin(x)。2/4/202314華中科技大學計算機學院C語言課程組【例6.3】用_Generic,編寫求和的通用類型宏sum。int
sumi(int*arr,int
cnt) /*整數(shù)求和*/{intsum=0;
inti;
for(i=0;i<cnt;++i)sum+=arr[i];returnsum;}doublesumf(double*arr,int
cnt) /*浮點數(shù)求和*/{doublesum=0.0;
inti;
for(i=0;i<cnt;++i)sum+=arr[i]returnsum;}/*通用類型宏sum,它會根據(jù)傳遞的實際類型來決定最終調(diào)用的函數(shù)*/#definesum(_arr,_cnt)_Generic(_arr[0],int:sumi,defualt:sumf)(_arr,_cnt)2/4/202315華中科技大學計算機學院C語言課程組6.4取消宏定義#undef
終止宏名的作用域其,形式為:
#undef
標識符何時使用#undef指令?防止宏名的沖突
#include"everything.h"#undefSIZE/*everything.h中定義了SIZE,就取消它;否則該指令不起作用*/#defineSIZE100保證調(diào)用的是一個實際函數(shù)而不是宏
#undef
getchar
int
getchar(void){…}2/4/202316華中科技大學計算機學院C語言課程組6.5條件編譯預處理程序提供了條件編譯指令,用于在預處理中進行條件控制,根據(jù)所求條件的值有選擇地包含不同的程序部分,因而產(chǎn)生不同的目標代碼。這對于程序的移植和調(diào)試是很有用的。對源程序的各部分有選擇地進行編譯稱為條件編譯。條件編譯指令三種形式,控制流與if-else語句類似。如表6.1所示.2/4/202317華中科技大學計算機學院C語言課程組6.4.1#if、#ifdef和#ifndef指令條件編譯有三種形式,如表6.1所示,每種形式的控制流與if語句的控制流類似?!俺绦蚨巍敝锌梢园?include和#define預處理行,常量表達式必須是整型的并且不能含有sizeof與強制類型轉換運算符或枚舉常量。2/4/202318華中科技大學計算機學院C語言課程組例
利用R計算圓或正方形的面積預處理前#defineRint
main(void){floatc,r,s;
printf(“inputanumber:”);
scanf(“%f”,&c);
#ifdefR
r=3.14159*c*c;
printf(“%f\n”,r);
#else
s=c*c;
printf("%f\n",s);
#endifreturn0;}
預處理后int
main(void){
floatc,r,s;
printf(“inputanumber:”);
scanf(“%f”,&c);
r=3.14159*c*c;
printf(“%f\n”,r);return0;}生成的目標程序較短2/4/202319華中科技大學計算機學院C語言課程組6.4.2defined運算符defined是預處理運算符,其形式為:
defined(標識符)或defined標識符
它用來判斷標識符是否被#define定義了,如被定義,值為1,否則為0defined運算符,可以將第2種和第3種形式的條件編譯指令改用第1種形式的條件編譯指令。例如,例6.4中的#ifdef可為
#ifdefined(R)用該運算符可以寫比較復雜的條件編譯指令。#ifdef只能判斷一個宏,如果條件比較復雜實現(xiàn)起來會比較煩鎖,而用#ifdefined()就比較方便。有兩個宏MACRO_1和MACRO_2,只有兩個宏都定義過才會編譯程序段A,可通過如下方式實現(xiàn):#ifdefined(MACRO_1)&&defined(MACRO_2)
程序段A#endif2/4/202320華中科技大學計算機學院C語言課程組6.4.3條件編譯的應用【例6.5】采用條件編譯,避免多次包含同一個頭文件。為了避免一個頭文件被多次包含,可以在頭文件的最前面兩行和最后一行加上預編譯指令,讓頭文件在被多個源文件引用時不會多次編譯。#ifndef_NAME_H#define_NAME_H /*定義頭文件的標識符*/…… /*頭文件的內(nèi)容*/#endif其中,NAME是頭文件的名字。比如頭文件為myFile.h,則其標識符可為_MYFILE_H。在創(chuàng)建一個頭文件時,用#define指令為它定義一個唯一的標識符。通過#ifndef指令檢查這個標識符是否已被定義,如果已被定義,則說明該頭文件已經(jīng)被包含了,就不要再次包含該頭文件,#ifndef就幫助編譯器跳過直到#endif的所有文本;反之,則定義這個標識符,以避免以后再次包含該頭文件。2/4/202321華中科技大學計算機學院C語言課程組【例6.6】條件編譯允許有選擇地編譯程序的某些部分,可以將程序的特殊性能納入不同版本。例如對于不同語言版本中的某個應用程序,需要改變貨幣的顯示,可以使用以下條件編譯,使用預定義常數(shù)ACTIVE_COUNTRY的值來決定貨幣符號。#defineUS 0#defineENGLAND 1#defineFRANCE 2#defineACTIVE_COUNTRY US#ifACTIVE_COUNTRY==UScharcurrency[]="dollar"; /*美元*/#elifACTIVE_COUNTRY==ENGLANDcharcurrency[]="pound"; /*英鎊*/#elsecharcurrency[]="franc"; /*法郎*/#endif#elif指令的意義與elseif相同,它形成一個if-else-if階梯狀語句,可進行多種編譯選擇。每個#elif
后跟一個常量表達式。如果表達式為非0,則編譯其后的程序段,不再對其他#elif表達式進行測試。否則,順序測試下一個條件。
2/4/202322華中科技大學計算機學院C語言課程組調(diào)試程序時臨時忽一些代碼在程序開發(fā)過程中,程序員經(jīng)常需要臨時忽略或封閉一些代碼,從而防止編譯器編譯這些代碼。要做到這一點,可以把代碼放在注釋中。然而,如果代碼中也含有注釋,這個方法就會導致語法錯誤。使用條件編譯能解決這個問題:#if0
不編譯的代碼#endif要讓編譯器編譯這段代碼,把原來的0改為1就可以了。2/4/202323華中科技大學計算機學院C語言課程組調(diào)試程序時跟蹤程序的執(zhí)行在源程序的調(diào)試中,常常需要跟蹤程序的執(zhí)行情況,為此,在程序中加一些輸出信息的語句,通過這些輸出信息來跟蹤判斷程序是否有錯誤。在調(diào)試結束后,需要把為了調(diào)試而增加的那些輸出信息的語句刪除掉,然而手工刪除既不方便,也易出錯。使用條件編譯方便得多,把這些為調(diào)試而增加的語句放在條件編譯指令之間,在調(diào)試時編譯這些語句。如:#ifdefDEBUG
printf("Variablex=%d\n",x);#endif
在調(diào)試程序時,在前面加#defineDEBUG,就編譯printf語句,輸出供判斷參考的x值。完成調(diào)試后,從程序中去掉#define指令,編譯就忽略為調(diào)試而插入的printf語句,相當于它被“自動”刪除了。2/4/202324華中科技大學計算機學院C語言課程組6.5assert斷言和靜態(tài)斷言使用斷言可以創(chuàng)建更穩(wěn)定、品質(zhì)更好且不易于出錯的代碼。斷言用于在代碼中捕捉一些假設,當假設不成立時中斷當前操作,可以將斷言看作是異常處理的一種高級形式。assert斷言是動態(tài)斷言,只能在程序運行出現(xiàn)錯誤時做出判斷。C11增加了靜態(tài)斷言,它可以在編譯時就對程序的錯誤做出判斷。2/4/202325華中科技大學計算機學院C語言課程組6.5.1assert斷言在頭文件assert.h中,用來測試表達式的值是否符合要求,其形式如下:
assert(condition)如果condition值非0,程序繼續(xù)執(zhí)行下一個語句。如果condition值0,就輸出錯誤信息,并通過調(diào)用實用庫中的函數(shù)abort終止程序的執(zhí)行。2/4/202326華中科技大學計算機學院C語言課程組用assert宏判斷數(shù)據(jù)是否合法assert(x<=10);如果x大于10,就會輸出如下包含行號和文件名的錯誤信息并中斷執(zhí)行:
Assertionfailed:x<=0,filetest.c,line12對于大多數(shù)編譯器來說,在頭文件assert.h的assert宏定義中,如果定義了符號常量NDEBUG,其后的assert將被忽略。因此,如果不再需要assert,那么可把代碼行
#defineNDEBUG
插入到程序中,而無需手工刪除assert。2/4/202327華中科技大學計算機學院C語言課程組6.5.2靜態(tài)斷言assert宏只能在程序運行出現(xiàn)錯誤時進行退出操作并產(chǎn)生調(diào)試信息,而靜態(tài)斷言(Staticassertions)可用于在編譯時進行檢查,不會產(chǎn)生任何運行時的額外開銷(包括時間和空間)。在C11標準中,從語言層面加入了對靜態(tài)斷言的支持,引入了新的關鍵字_Static_assert來表示靜態(tài)斷言,斷言失敗會產(chǎn)生有意義的且充分的診斷信息。2/4/202328華中科技大學計算機學院C語言課程組關鍵字_Static_assert
_Static_assert(constant-expression,string-literal);其中,第一個參數(shù)constant-expression必須是一個編譯時可知的整型常量表達式,如果用一個變量作為第一個參數(shù)會遇到編譯錯誤;第二個參數(shù)string-literal是在斷言失敗時輸出的提示信息(即字符串)。當constant-expression的布爾值為true時,該靜態(tài)斷言聲明不會產(chǎn)生任何影響;否則,編譯器將給出錯誤診斷信息string-literal。例如:_Static_assert(sizeof(int)==8,"A64-bitmachineneeded!");在32位機上編譯這條語句時,就會輸出如下診斷信息:staticassertionfailed:"A64-bitmachineneeded!"在頭文件assert.h中,定義static_assert宏為關鍵字_Static_assert的同義詞。2/4/202329華中科技大學計算機學院C語言課程組6.6_func_預定義標識符C99標準中引入了預定義標識符(predefinedidentifier)的概念,并定義了一個預定義標識符_func_。它的性質(zhì)和關鍵字相似,盡管它本身并不是關鍵字。_func_定義為字符數(shù)組,用于指出_func_所在的函數(shù)名,類似于字符串賦值。例如:#include<stdio.h>voidmyfunc(void){
printf("%s\n",__fun
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025-2030年中國中端酒店行業(yè)并購重組擴張戰(zhàn)略制定與實施研究報告
- 2025-2030年中國家庭服務機器人行業(yè)商業(yè)模式創(chuàng)新戰(zhàn)略制定與實施研究報告
- 2025-2030年中國虛擬養(yǎng)老院行業(yè)營銷創(chuàng)新戰(zhàn)略制定與實施研究報告
- 2025-2030年中國新型健康服務行業(yè)營銷創(chuàng)新戰(zhàn)略制定與實施研究報告
- 2025-2030年中國礦山開發(fā)服務行業(yè)開拓第二增長曲線戰(zhàn)略制定與實施研究報告
- 建設社會主義文化強國論文
- 中國心理測試儀器行業(yè)市場深度分析及發(fā)展趨勢預測報告
- 一年級數(shù)學計算題專項練習匯編
- 大客車常用知識培訓課件
- 年產(chǎn)40000噸環(huán)保新能源材料生產(chǎn)線項目可行性研究報告寫作模板-拿地申報
- CTD申報資料撰寫模板:模塊三之3.2.S.4原料藥的質(zhì)量控制
- (正式版)JTT 1482-2023 道路運輸安全監(jiān)督檢查規(guī)范
- 圍手術期血糖的管理
- 2024年度醫(yī)療器械監(jiān)督管理條例培訓課件
- 項目七電子商務消費者權益保護的法律法規(guī)
- 100以內(nèi)不進位不退位加減法練習題
- 企業(yè)安全生產(chǎn)評估報告
- 水庫大壩深基坑開挖專項方案樣本
- 經(jīng)橈動脈腦血管造影術前術后護理
- 品質(zhì)經(jīng)理工作總結
- 運行設備巡回檢查制度模版
評論
0/150
提交評論