C語言程序設(shè)計ppt-第6章_第1頁
C語言程序設(shè)計ppt-第6章_第2頁
C語言程序設(shè)計ppt-第6章_第3頁
C語言程序設(shè)計ppt-第6章_第4頁
C語言程序設(shè)計ppt-第6章_第5頁
已閱讀5頁,還剩27頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

C語言與程序設(shè)計

TheCProgrammingLanguage

第6章編譯預(yù)處理

華中科技大學(xué)計算機學(xué)院

盧萍2/4/20231華中科技大學(xué)計算機學(xué)院C語言課程組第6章編譯預(yù)處理

編譯預(yù)處理:對源程序進行編譯之前所作的工作,它由預(yù)處理程序負責(zé)完成。編譯時,系統(tǒng)將自動引用預(yù)處理程序?qū)υ闯绦蛑械念A(yù)處理指令進行處理。預(yù)處理指令:以“#”號開始的指令。本章重點介紹:

#include、#define和條件編譯的功能和用法,以及assert宏的使用。2/4/20232華中科技大學(xué)計算機學(xué)院C語言課程組6.1文件包含#include用指定文件的內(nèi)容取代該預(yù)處理指令行,有2種一般形式:(1)#include<文件名>

在指定的標(biāo)準(zhǔn)目錄下尋找被包含文件(2)

#include"文件名"

首先在用戶當(dāng)前目錄中尋找被包含文件,若找不到,再在指定的標(biāo)準(zhǔn)目錄下尋找

2/4/20233華中科技大學(xué)計算機學(xué)院C語言課程組6.2宏定義#define用一個標(biāo)識符來表示一個字符串,

#define標(biāo)識符字符串宏名:被定義的標(biāo)識符。在編譯預(yù)處理時,宏代換(宏展開):用字符串去取代宏名預(yù)處理前#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;}預(yù)處理后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華中科技大學(xué)計算機學(xué)院C語言課程組6.3帶參數(shù)的宏定義#define標(biāo)識符(標(biāo)識符,標(biāo)識符,…,標(biāo)識符)字符串宏名形式參數(shù)宏調(diào)用:給出實參宏展開:(1)用字符串替換宏,(2)用實參去替換形參2/4/20235華中科技大學(xué)計算機學(xué)院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華中科技大學(xué)計算機學(xué)院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華中科技大學(xué)計算機學(xué)院C語言課程組注意:宏名和與左括號之間不能有空格

#defineSQ

(x)((x)*(x))被認為是無參宏定義。宏調(diào)用:SQ(3)宏展開:(x)((x)*(x))(3)/*顯然錯誤的*/2/4/20238華中科技大學(xué)計算機學(xué)院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)常使用的簡短表達式,以及小的可重復(fù)的代碼段;當(dāng)任務(wù)比較復(fù)雜,需要多行代碼才能實現(xiàn)時,或者要求程序越小越好時,就應(yīng)該使用函數(shù)。2/4/20239華中科技大學(xué)計算機學(xué)院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ù)處理后變?yōu)椋簒=(2+3); /*x的值為5*/y=(+3);

/*y的值為3*/2/4/202310華中科技大學(xué)計算機學(xué)院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)建的預(yù)處理器標(biāo)識符__VA_ARGS__用來把“…”部分傳遞給宏。當(dāng)宏的調(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華中科技大學(xué)計算機學(xué)院C語言課程組【例6.2】用C99的可變參數(shù)宏,

打印調(diào)試信息。在編寫代碼的過程中,為了調(diào)試程序,經(jīng)常會輸出一些調(diào)試信息到屏幕上,隨著項目的調(diào)試,輸出的信息可能會越來越多,信息的輸出一般要調(diào)用printf等函數(shù)。但是,當(dāng)調(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華中科技大學(xué)計算機學(xué)院C語言課程組6.2.5通用類型宏通用類型宏或者泛型宏(type-genericmacros)是一種編譯期技術(shù),它允許開發(fā)人員根據(jù)宏的某個參數(shù)的類型來確定生成的內(nèi)容。早在C99時就有了通用類型宏的概念,只不過當(dāng)時沒有對它進行標(biāo)準(zhǔn)化。C99中引用了頭文件<tgmath.h>,給開發(fā)人員提供大量初等數(shù)學(xué)函數(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華中科技大學(xué)計算機學(xué)院C語言課程組關(guān)鍵字_GenericC11中引入了新關(guān)鍵字_Generic來實現(xiàn)通用類型宏,它根據(jù)第一個參數(shù)的類型和后面的類型-表達式關(guān)聯(lián)來實現(xiàn)編譯期的替換。利用_Generic,C99的sin可定義如下:#definesin(x)_Generic((x),longdouble:sinl,double:sinf,defalt:sin)(x)_Generic對第一個參數(shù)進行類型判斷,然后根據(jù)從第二參數(shù)開始的類型-表達式關(guān)聯(lián)表來進行編譯期替換。如果x為longdouble類型,那么_Generic((x),…)的結(jié)果為sinl,如果x為double類型,那么_Generic((x),…)的結(jié)果為sinf,否則結(jié)果為sin??梢?,根據(jù)x的類型,宏sin(x)轉(zhuǎn)換為sinl(x),sinf(x)或sin(x)。2/4/202314華中科技大學(xué)計算機學(xué)院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華中科技大學(xué)計算機學(xué)院C語言課程組6.4取消宏定義#undef

終止宏名的作用域其,形式為:

#undef

標(biāo)識符何時使用#undef指令?防止宏名的沖突

#include"everything.h"#undefSIZE/*everything.h中定義了SIZE,就取消它;否則該指令不起作用*/#defineSIZE100保證調(diào)用的是一個實際函數(shù)而不是宏

#undef

getchar

int

getchar(void){…}2/4/202316華中科技大學(xué)計算機學(xué)院C語言課程組6.5條件編譯預(yù)處理程序提供了條件編譯指令,用于在預(yù)處理中進行條件控制,根據(jù)所求條件的值有選擇地包含不同的程序部分,因而產(chǎn)生不同的目標(biāo)代碼。這對于程序的移植和調(diào)試是很有用的。對源程序的各部分有選擇地進行編譯稱為條件編譯。條件編譯指令三種形式,控制流與if-else語句類似。如表6.1所示.2/4/202317華中科技大學(xué)計算機學(xué)院C語言課程組6.4.1#if、#ifdef和#ifndef指令條件編譯有三種形式,如表6.1所示,每種形式的控制流與if語句的控制流類似?!俺绦蚨巍敝锌梢园?include和#define預(yù)處理行,常量表達式必須是整型的并且不能含有sizeof與強制類型轉(zhuǎn)換運算符或枚舉常量。2/4/202318華中科技大學(xué)計算機學(xué)院C語言課程組例

利用R計算圓或正方形的面積預(yù)處理前#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;}

預(yù)處理后int

main(void){

floatc,r,s;

printf(“inputanumber:”);

scanf(“%f”,&c);

r=3.14159*c*c;

printf(“%f\n”,r);return0;}生成的目標(biāo)程序較短2/4/202319華中科技大學(xué)計算機學(xué)院C語言課程組6.4.2defined運算符defined是預(yù)處理運算符,其形式為:

defined(標(biāo)識符)或defined標(biāo)識符

它用來判斷標(biāo)識符是否被#define定義了,如被定義,值為1,否則為0defined運算符,可以將第2種和第3種形式的條件編譯指令改用第1種形式的條件編譯指令。例如,例6.4中的#ifdef可為

#ifdefined(R)用該運算符可以寫比較復(fù)雜的條件編譯指令。#ifdef只能判斷一個宏,如果條件比較復(fù)雜實現(xiàn)起來會比較煩鎖,而用#ifdefined()就比較方便。有兩個宏MACRO_1和MACRO_2,只有兩個宏都定義過才會編譯程序段A,可通過如下方式實現(xiàn):#ifdefined(MACRO_1)&&defined(MACRO_2)

程序段A#endif2/4/202320華中科技大學(xué)計算機學(xué)院C語言課程組6.4.3條件編譯的應(yīng)用【例6.5】采用條件編譯,避免多次包含同一個頭文件。為了避免一個頭文件被多次包含,可以在頭文件的最前面兩行和最后一行加上預(yù)編譯指令,讓頭文件在被多個源文件引用時不會多次編譯。#ifndef_NAME_H#define_NAME_H /*定義頭文件的標(biāo)識符*/…… /*頭文件的內(nèi)容*/#endif其中,NAME是頭文件的名字。比如頭文件為myFile.h,則其標(biāo)識符可為_MYFILE_H。在創(chuàng)建一個頭文件時,用#define指令為它定義一個唯一的標(biāo)識符。通過#ifndef指令檢查這個標(biāo)識符是否已被定義,如果已被定義,則說明該頭文件已經(jīng)被包含了,就不要再次包含該頭文件,#ifndef就幫助編譯器跳過直到#endif的所有文本;反之,則定義這個標(biāo)識符,以避免以后再次包含該頭文件。2/4/202321華中科技大學(xué)計算機學(xué)院C語言課程組【例6.6】條件編譯允許有選擇地編譯程序的某些部分,可以將程序的特殊性能納入不同版本。例如對于不同語言版本中的某個應(yīng)用程序,需要改變貨幣的顯示,可以使用以下條件編譯,使用預(yù)定義常數(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華中科技大學(xué)計算機學(xué)院C語言課程組調(diào)試程序時臨時忽一些代碼在程序開發(fā)過程中,程序員經(jīng)常需要臨時忽略或封閉一些代碼,從而防止編譯器編譯這些代碼。要做到這一點,可以把代碼放在注釋中。然而,如果代碼中也含有注釋,這個方法就會導(dǎo)致語法錯誤。使用條件編譯能解決這個問題:#if0

不編譯的代碼#endif要讓編譯器編譯這段代碼,把原來的0改為1就可以了。2/4/202323華中科技大學(xué)計算機學(xué)院C語言課程組調(diào)試程序時跟蹤程序的執(zhí)行在源程序的調(diào)試中,常常需要跟蹤程序的執(zhí)行情況,為此,在程序中加一些輸出信息的語句,通過這些輸出信息來跟蹤判斷程序是否有錯誤。在調(diào)試結(jié)束后,需要把為了調(diào)試而增加的那些輸出信息的語句刪除掉,然而手工刪除既不方便,也易出錯。使用條件編譯方便得多,把這些為調(diào)試而增加的語句放在條件編譯指令之間,在調(diào)試時編譯這些語句。如:#ifdefDEBUG

printf("Variablex=%d\n",x);#endif

在調(diào)試程序時,在前面加#defineDEBUG,就編譯printf語句,輸出供判斷參考的x值。完成調(diào)試后,從程序中去掉#define指令,編譯就忽略為調(diào)試而插入的printf語句,相當(dāng)于它被“自動”刪除了。2/4/202324華中科技大學(xué)計算機學(xué)院C語言課程組6.5assert斷言和靜態(tài)斷言使用斷言可以創(chuàng)建更穩(wěn)定、品質(zhì)更好且不易于出錯的代碼。斷言用于在代碼中捕捉一些假設(shè),當(dāng)假設(shè)不成立時中斷當(dāng)前操作,可以將斷言看作是異常處理的一種高級形式。assert斷言是動態(tài)斷言,只能在程序運行出現(xiàn)錯誤時做出判斷。C11增加了靜態(tài)斷言,它可以在編譯時就對程序的錯誤做出判斷。2/4/202325華中科技大學(xué)計算機學(xué)院C語言課程組6.5.1assert斷言在頭文件assert.h中,用來測試表達式的值是否符合要求,其形式如下:

assert(condition)如果condition值非0,程序繼續(xù)執(zhí)行下一個語句。如果condition值0,就輸出錯誤信息,并通過調(diào)用實用庫中的函數(shù)abort終止程序的執(zhí)行。2/4/202326華中科技大學(xué)計算機學(xué)院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華中科技大學(xué)計算機學(xué)院C語言課程組6.5.2靜態(tài)斷言assert宏只能在程序運行出現(xiàn)錯誤時進行退出操作并產(chǎn)生調(diào)試信息,而靜態(tài)斷言(Staticassertions)可用于在編譯時進行檢查,不會產(chǎn)生任何運行時的額外開銷(包括時間和空間)。在C11標(biāo)準(zhǔn)中,從語言層面加入了對靜態(tài)斷言的支持,引入了新的關(guān)鍵字_Static_assert來表示靜態(tài)斷言,斷言失敗會產(chǎn)生有意義的且充分的診斷信息。2/4/202328華中科技大學(xué)計算機學(xué)院C語言課程組關(guān)鍵字_Static_assert

_Static_assert(constant-expression,string-literal);其中,第一個參數(shù)constant-expression必須是一個編譯時可知的整型常量表達式,如果用一個變量作為第一個參數(shù)會遇到編譯錯誤;第二個參數(shù)string-literal是在斷言失敗時輸出的提示信息(即字符串)。當(dāng)constant-expression的布爾值為true時,該靜態(tài)斷言聲明不會產(chǎn)生任何影響;否則,編譯器將給出錯誤診斷信息string-literal。例如:_Static_assert(sizeof(int)==8,"A64-bitmachineneeded!");在32位機上編譯這條語句時,就會輸出如下診斷信息:staticassertionfailed:"A64-bitmachineneeded!"在頭文件assert.h中,定義static_assert宏為關(guān)鍵字_Static_assert的同義詞。2/4/202329華中科技大學(xué)計算機學(xué)院C語言課程組6.6_func_預(yù)定義標(biāo)識符C99標(biāo)準(zhǔn)中引入了預(yù)定義標(biāo)識符(predefinedidentifier)的概念,并定義了一個預(yù)定義標(biāo)識符_func_。它的性質(zhì)和關(guān)鍵字相似,盡管它本身并不是關(guān)鍵字。_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)系上傳者。文件的所有權(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)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論