0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

預(yù)處理相關(guān)知識點總結(jié)

嵌入式應(yīng)用研究院 ? 來源:C語言編程筆記 ? 2023-05-31 09:52 ? 次閱讀

預(yù)處理器

編譯一個 C 程序設(shè)計很多步驟。其中第 1 個步驟被稱為預(yù)處理階段。C 預(yù)處理器在源代碼編譯之前對其進(jìn)行一些文本性質(zhì)的操作。他的主要任務(wù)包括刪除注釋、插入被 #include 指令包含的文件的內(nèi)容、定義、和替換由 #define 指令定義的符號以及確定代碼的部分內(nèi)容是否應(yīng)該根據(jù)一些條件編譯指令進(jìn)行編譯。

預(yù)定義符號

1.預(yù)處理器定義了一些符號,他們的值或者是字符串常量,或者是十進(jìn)制數(shù)字常量,F(xiàn)ILE的含義是進(jìn)行編譯的源文件名。

35a0f28c-ff52-11ed-90ce-dac502259ad0.png

#define

#define 機(jī)制包括了一個規(guī)定,允許把參數(shù)替換到文本中,這種實現(xiàn)通常稱為宏(macro)或定義宏(defined macro)。下面是宏的聲明方式:

#definename(parameter-list)stuff

其中,parameter-list(參數(shù)列表)是一個由逗號分隔的值的列表,每個值都與宏定義中的一個參數(shù)相對應(yīng),整個列表用一對括號包圍。當(dāng)參數(shù)出現(xiàn)在程序中時,與每個參數(shù)對應(yīng)的實際值都將被替換到 stuff 中。這里由一個宏。它接受一個參數(shù):

#defineSQUARE(x)((x)*(x))

如果在上述聲明之后把 SQUARE(5)置于程序中,預(yù)處理器就會用下面的這個表達(dá)式替換上面的表達(dá)式:

((5)*(5))

這里添加括號的原因是,為了避免在使用宏時,由于參數(shù)中的操作符或鄰近的操作符之間不可預(yù)料的相互作用。

#define 替換

在程序中擴(kuò)展#define定義符號和宏時,需要涉及幾個步驟。

在調(diào)用宏時,首先對參數(shù)進(jìn)行檢查,看看是否包含了任何由#define定義的符號。如果是,他們首先被替換。

替換文本隨后被插入到程序中原來文本的位置。對于宏,參數(shù)名被他們的值所替代。

最后,再次對結(jié)果文本進(jìn)行掃描,看看它是否包含了任何由#define定義的符號。如果是,就重復(fù)上述處理過程。

這樣,宏參數(shù)和#define定義可以包含其他#define定義的符號。但是,宏不可以出現(xiàn)遞歸。

#argument 替換符號

當(dāng)預(yù)處理器搜索#define定義的符號時,字符串常量的內(nèi)容并不進(jìn)行檢查。#argument 這種結(jié)構(gòu)被預(yù)處理器翻譯為 "argument",可以把一個宏參數(shù)轉(zhuǎn)化為字符串。如果想把宏參數(shù)插入到字符串常量中,可以使用下面的技巧:

技巧一

首先,臨近字符串自動連接的特性使我們很容易把一個字符串分成幾段,每段實際上都是一個宏參數(shù)。例子如下:

#definePRINT(FORMAT,VALUE)
printf("Thevalueis"#FORMAT"",VALUE)
...
intx=6;
PRINT_FOR(%d,x+3);

輸出結(jié)果:

Thevalueis9

技巧二

#definePRINT_FOR(FORMAT,VALUE)
printf("thevalueof
"#VALUE"is"#FORMAT"
",VALUE)
...
PRINT_FOR(%d,x+3);

輸出結(jié)果:

thevalueofx+3is9

書上的技巧一和技巧二并不能通過編譯,我經(jīng)過修改的上面的兩個例子,我感覺是用了同一個技巧就是將字符串中的 "#VALUE" 替換成傳入的字符,如技巧二中介紹的將 #VALUE替換成了字符串中的 x+3,將 #FORMAT 替換成 %d,最后一個 VALUE 作為實際的參數(shù)只是進(jìn)行了簡單的文本替換,不涉及字符串修改。

技巧三

#definePRINT(VALUE)printf(#VALUE)
...
PRINT(helloSummerGift);

輸出結(jié)果:

helloSummerGift

技巧三展示了基本的 #argument功能,即將 #VALUE替換成 "hello SummerGift",也就是將 hello SummerGift 簡單替換成了 "hello SummerGift"。

##連接符號

##結(jié)構(gòu)把位于它兩邊的符號連接成一個符號。作為用途之一,它允許宏定義從分離的文本片段創(chuàng)建標(biāo)識符。下面的這個例子使用這種連接把一個值添加到幾個變量之一:

#defineADD_TO_SUM(sum_number,value)
sum##sum_number+=value
...
ADD_TO_SUM(5,25);

最后一條語句把值25加到變量 sum5。注意這種連接必須產(chǎn)生一個合法的標(biāo)識符。否則其結(jié)果就是未定義的。

宏與函數(shù)

用宏來完成比較簡單的計算,比如比較兩個表達(dá)式的大小的好處是,如果使用函數(shù)來實現(xiàn),用于調(diào)用和從函數(shù)返回的代碼很可能比實際執(zhí)行這個小型計算任務(wù)的代碼更大,所以使用宏比使用函數(shù)在程序的規(guī)模和速度方面都更勝一籌。#define MAX(A, B) ((A)>(B) ? (A):(B))

更為重要的是,函數(shù)的參數(shù)必須聲明為一種特定的類型,所以它只能在類型合適的表達(dá)式上使用。反之,上面這個宏可以用于整形、長整形、單浮點型、雙浮點數(shù)以及其他任何可以用 > 操作符比較值大小的類型。換句話說,宏是與類型無關(guān)的。

與函數(shù)相比,使用宏的不利之處在于每次使用宏時,一份宏定義代碼的拷貝都將插入到程序中。除非宏非常短,否則使用宏可能會大幅度增加程序的長度。

還有一些任務(wù)根本無法用函數(shù)實現(xiàn)。比如下面的宏定義,這個宏的第二個參數(shù)是一種類型,它無法作為函數(shù)參數(shù)進(jìn)行傳遞。

#defineMALLOC(n,type)
((type*)malloc((n)*sizeof(type)))

實際轉(zhuǎn)換過程如下:

轉(zhuǎn)換前:pi = MALLOC(25, int);
轉(zhuǎn)換后:pi =((int *)malloc((25)* sizeof( int )))

帶副作用的宏參數(shù)

當(dāng)宏參數(shù)在宏定義中出現(xiàn)的次數(shù)超過一次時,如果這個參數(shù)具有副作用,那么當(dāng)你使用這個宏時就出現(xiàn)危險導(dǎo)致不可預(yù)料的結(jié)果。副作用就是在表達(dá)式求值時出現(xiàn)的永久性結(jié)果,例如:

x+1

這個表達(dá)式可以重復(fù)執(zhí)行幾百次,他每次獲得的值結(jié)果都是一樣的。這個表達(dá)式不具有副作用。但是

x++

就具有副作用:它增加 x 的值。當(dāng)這個表達(dá)式下一次執(zhí)行時,它將產(chǎn)生一個不同的結(jié)果。MAX宏可以證明具有副作用的參數(shù)引起的問題。

#defineMAX(A,B)((A)>(B)?(A):(B))
...
x=5;
y=8;
z=MAX(x,y);
printf("x=%d,y=%d,z=%d",x,y,z);
這個宏的結(jié)果是 x = 6, y = 10, z = 9。
檢查替換后的結(jié)果:z =(x++)>(y++) ? (x++):(y++);

那個較小的值只增值了一次,而那個較大的值卻增值了兩次,第一次是在比較的時候,第二次是在執(zhí)行 ?后面的表達(dá)式的時候。

命名約定

為宏采納一種命名約定是很重要的,用來區(qū)分一個名稱是一個函數(shù)還是一個宏,避免發(fā)生混淆。

#undef

這條預(yù)處理指令用于移除一個宏定義。

#undefname

如果一個現(xiàn)存的名字需要被重新定義,那么他的舊定義首先必須用#undef移除。如果一個宏不想讓后面的程序用了,那么就將其 undef ,如果后面使用了這個宏就會報錯。

命令行定義

許多編譯器提供了一種能力,允許在命令行中定義符號,用于啟動編譯過程。當(dāng)我們根據(jù)同一個源文件編譯一個程序的不同版本時,這個特性是很有用的。例如有如下數(shù)組:

intarray[ARRAY_SIZE];

那么在UNIX系統(tǒng)中,編譯這個程序的命令行很可能是下面的樣子:

cc-DARRY_SIZE=100prog.c

條件編譯

常見的條件編譯:

#ifconstant-expression
statments
#endif

其中,constant-expression(常量表達(dá)式)由預(yù)處理器進(jìn)行求值,如果其值為真,則statements部分就被正常編譯,否則預(yù)處理器就安靜地刪除他們。

是否被定義

測試一個符號是否被定義也是可能的。在條件編譯中完成這個任務(wù)往往更方便,因為程序如果并不需要控制編譯的符號所控制的特性,他就不需要被定義。這個測試可以用下面的方式來進(jìn)行:

#ifdefined(symbol)
#ifdefsymbol

#if!defined(symbol)
#ifndefsymbol

每對定義的兩條語句是等價的,但#if形式功能更強(qiáng)。因為常量表達(dá)式可能包含額外的條件,如下面所示:

#ifx>0||defined(ABD)&&defined(BCD)

嵌套指令

上面提到的這些指令可以嵌套于另一個指令內(nèi)部。

#ifxxx
statments
#elifxxx
statments
#endif

文件包含

使用#include指令的替換執(zhí)行方式很簡單,預(yù)處理刪除這條指令,并用包含文件的內(nèi)容取而代之。這樣一個頭文件如果被包含到10個源文件中,它實際被編譯了10次。

編譯器支持兩種不同類型的 #include文件包含:函數(shù)庫文件和本地文件。實際上,他們之間的區(qū)別很小。

函數(shù)庫文件包含

函數(shù)庫頭文件包含使用下面的語法:

#include

這種情況的包含會在系統(tǒng)目錄里尋找函數(shù)庫頭文件。也可以使用編譯器選項添加自己的文件函數(shù)庫。

本地文件包含

本地頭文件包含使用下面的語法:

#include"filename"

這種情況的包含會先在當(dāng)前目錄進(jìn)行查找,如果未找到再像查找函數(shù)庫頭文件一樣在標(biāo)準(zhǔn)位置查找本地頭文件。嵌套文件包含 使用條件編譯來避免重復(fù)包含頭文件出現(xiàn)的錯誤:

#ifndef__HEADERNAME__H
#define__HEADERNAME__H1
/*
**Allthestuffthatyouwangintheheaderfile
*/
#endif

其他指令

#error指令允許生成錯誤信息
#errortextoferrormessage

使用方法:

#errornooptionselsected
#line修改下一行輸入的行號
#linenumber"string"

它通知預(yù)處理器 number 使下一行輸入的行號。如果給出了可選部分 string ,預(yù)處理器就把他當(dāng)作當(dāng)前文件的名字。值得注意的是,這條指令修改__LINE__符號的值,如果加上可選部分,他還將修改__FILE__符號的值。

#progma用于支持因編譯器而異的特性

#progma指令是另一種機(jī)制,用于支持因編譯器而異的機(jī)制。它的語法也是因編譯器而異。有些環(huán)境可能提供一些#progma指令,允許一些編譯選項或者其他任何任何方式無法實現(xiàn)的一些處理方式。例如有些編譯器使用progma指令在編譯過程中打開或者關(guān)閉清單顯示,或者把會變代碼插入到 C 程序中。從本質(zhì)上說,#progma是不可移植的。預(yù)處理器將忽略他們不認(rèn)識的#progma指令,兩個不同的編譯器可能以兩種不同的方式解釋同一條#progma指令。

#(null directive) 無效指令

無效指令就是一個#開頭,但后面不跟任何內(nèi)容的一行。這類指令只是被預(yù)處理器簡單地刪除。下面的例子中無效指令通過把#include與周圍的代碼分隔開來,凸顯它的存在。

#
#include
#

插入空行也可以取得相同的效果。

總結(jié)

警告的總結(jié)

不要在一個宏定義的末尾加上分號,使其成為一條完整的語句。

在宏定義中使用參數(shù),但忘了在他們周圍加上括號。

忘了在整個宏定義的兩邊加上括號。

編程提示的總結(jié)

避免用 #define 指令定義可以用函數(shù)實現(xiàn)的很長序列的代碼。

在那些對表達(dá)式求值的宏中,每個宏參數(shù)出現(xiàn)的地方都應(yīng)該加上括號,并且在整個宏定義的兩邊也加上括號。

避免使用 #define 宏創(chuàng)建一種新的語言。

采用采用命名約定,使程序員很容易看出某個標(biāo)識符是否為 #define 宏。

只要合適就應(yīng)該使用文件包含,不必?fù)?dān)心它的額外開銷。

頭文件只應(yīng)該包含一組函數(shù)和(或)數(shù)據(jù)的聲明。

把不同集合的聲明分離到不同的頭文件中可以改善信息隱藏。

嵌套的 #include 文件是我們很難判斷源文件之間的依賴關(guān)系。

審核編輯:湯梓紅

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 指令
    +關(guān)注

    關(guān)注

    1

    文章

    604

    瀏覽量

    35581
  • 字符串
    +關(guān)注

    關(guān)注

    1

    文章

    567

    瀏覽量

    20432
  • 編譯
    +關(guān)注

    關(guān)注

    0

    文章

    646

    瀏覽量

    32741
  • C程序
    +關(guān)注

    關(guān)注

    4

    文章

    254

    瀏覽量

    35945
  • 預(yù)處理器
    +關(guān)注

    關(guān)注

    0

    文章

    13

    瀏覽量

    2215

原文標(biāo)題:預(yù)處理相關(guān)知識點總結(jié)

文章出處:【微信號:嵌入式應(yīng)用研究院,微信公眾號:嵌入式應(yīng)用研究院】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    嵌入式C語言知識點總結(jié)

    怎么才能做好嵌入式開發(fā)?學(xué)好C語言吧!今天就來推薦一篇大佬寫的嵌入式C語言知識點總結(jié)。
    發(fā)表于 09-27 09:53 ?1143次閱讀

    關(guān)于工業(yè)相機(jī)相關(guān)知識點總結(jié)的太棒了

    關(guān)于工業(yè)相機(jī)相關(guān)知識點總結(jié)的太棒了
    發(fā)表于 09-29 07:56

    C語言程序小知識點總結(jié)

    C語言總結(jié)(stm32嵌入式開發(fā))文章目錄C語言總結(jié)(stm32嵌入式開發(fā))c程序小知識點總結(jié)1.靜態(tài)變量static與外部變量extern的使用2.函數(shù)封裝后返回值的方法3.軟件寄存
    發(fā)表于 11-05 07:45

    高一數(shù)學(xué)知識點總結(jié)

    高一數(shù)學(xué)知識點總結(jié)高一數(shù)學(xué)知識點總結(jié)高一數(shù)學(xué)知識點總結(jié)
    發(fā)表于 02-23 15:27 ?0次下載

    高二數(shù)學(xué)知識點總結(jié)

    高二數(shù)學(xué)知識點總結(jié)高二數(shù)學(xué)知識點總結(jié)高二數(shù)學(xué)知識點總結(jié)
    發(fā)表于 02-23 15:27 ?0次下載

    復(fù)習(xí)圖像處理知識點

    中南大學(xué)數(shù)字圖像預(yù)處理復(fù)習(xí)知識點,里面包含里所有的考點,可以參考。很全面。
    發(fā)表于 05-10 15:48 ?0次下載

    C語言學(xué)習(xí)入門知識點/干貨

    C語言知識點總結(jié)
    的頭像 發(fā)表于 07-18 17:54 ?5839次閱讀
    C語言學(xué)習(xí)入門<b class='flag-5'>知識點</b>/干貨

    Python的知識點總結(jié)詳細(xì)說明

    本文檔的主要內(nèi)容詳細(xì)介紹的是Python的知識點總結(jié)詳細(xì)說明。
    發(fā)表于 09-29 17:13 ?14次下載
    Python的<b class='flag-5'>知識點</b><b class='flag-5'>總結(jié)</b>詳細(xì)說明

    嵌入式知識點總結(jié)

    嵌入式知識點總結(jié)(arm嵌入式開發(fā)led過程)-嵌入式知識點總結(jié)? ? ? ? ? ? ? ? ? ??
    發(fā)表于 07-30 14:20 ?23次下載
    嵌入式<b class='flag-5'>知識點</b><b class='flag-5'>總結(jié)</b>

    開關(guān)電源模塊知識點總結(jié)

    開關(guān)電源模塊知識點總結(jié)(現(xiàn)代電源技術(shù)基礎(chǔ)pdf)-該文檔為開關(guān)電源模塊知識點總結(jié)文檔,是一份不錯的參考資料,感興趣的可以下載看看,,,,,,,,,,,,,,,,,
    發(fā)表于 09-22 13:42 ?27次下載
    開關(guān)電源模塊<b class='flag-5'>知識點</b><b class='flag-5'>總結(jié)</b>

    數(shù)字信號處理知識點總結(jié)

    數(shù)字信號處理知識點總結(jié)
    發(fā)表于 08-15 15:16 ?0次下載

    C語言最重要的知識點

    C語言知識點總結(jié).doc
    發(fā)表于 02-16 16:37 ?8次下載

    數(shù)字電路知識點總結(jié)

    本文整理了數(shù)字電路課程中的相關(guān)基本的知識點和較為重要的知識點,用于求職的數(shù)電部分的知識準(zhǔn)備,差缺補(bǔ)漏。
    的頭像 發(fā)表于 05-30 15:07 ?4451次閱讀
    數(shù)字電路<b class='flag-5'>知識點</b><b class='flag-5'>總結(jié)</b>

    電阻的相關(guān)知識點

    電阻,一個最容易又是最基礎(chǔ)的電子元器件,看似簡單,然而實際情況根據(jù)芯片哥對多位經(jīng)驗豐富的研發(fā)工程師的拜訪溝通得知并非如此;芯片哥如果讓開發(fā)工程師做出一個系統(tǒng)性的歸納總結(jié),把電阻相關(guān)的所有內(nèi)容知識點
    的頭像 發(fā)表于 09-13 10:19 ?1464次閱讀
    電阻的<b class='flag-5'>相關(guān)</b><b class='flag-5'>知識點</b>

    模擬電子技術(shù)知識點問題總結(jié)概覽

    給大家分享模擬電子技術(shù)知識點問題總結(jié)。
    的頭像 發(fā)表于 05-08 15:16 ?1035次閱讀
    模擬電子技術(shù)<b class='flag-5'>知識點</b>問題<b class='flag-5'>總結(jié)</b>概覽