當前位置:首頁 > 公眾號精選 > 嵌入式微處理器
[導讀]當使用參數(shù)調(diào)用宏時,會將參數(shù)替換為宏主體,并與其他輸入文件一起檢查結(jié)果,以進行更多的宏調(diào)用,可以將部分來自宏主體和部分自變量的宏調(diào)用組合在一起。


語法錯誤

當使用參數(shù)調(diào)用宏時,會將參數(shù)替換為宏主體,并與其他輸入文件一起檢查結(jié)果,以進行更多的宏調(diào)用,可以將部分來自宏主體和部分自變量的宏調(diào)用組合在一起。例如,

#define?twice(x)?(2*(x))
#define?call_with_1(x)?x(1)
call_with_1?(twice)

//x=1
?→?twice(1)
?→?(2*(1))

宏定義不必帶有括號,通過在宏主體中編寫不平衡的開放括號,可以創(chuàng)建一個從宏主體內(nèi)部開始但在宏主體外部結(jié)束的宏調(diào)用。例如,

#define?strange(file)?fprintf?(file,?"%s?%d",

strange(stderr)?p,?35)
?????→?fprintf?(stderr,?"%s?%d",?p,?35)

組合宏調(diào)用的功能可能會很有用,但是在宏主體中使用不平衡的開放括號只會造成混淆,應該避免。

運算符優(yōu)先級問題

在大多數(shù)宏定義示例中,每次出現(xiàn)的宏參數(shù)名稱都帶有括號,并且另一對括號通常會包圍整個宏定義,這是編寫宏最好的方式。舉個例子

#define?ceil_div(x,?y)?(x?+?y?-?1)?/?y

假定其用法如下:

a?=?ceil_div(b&c,sizeof(int));

拓展開是

a?=(b&c?+?sizeof(int)-1)/?sizeof(int);

這沒有達到我們的預期,C的運算符優(yōu)先級規(guī)則使其等效于此,而我們想要的是:

a?=(((b&c)+?sizeof(int)-1))/?sizeof(int);

如果我們將宏定義為

#define?ceil_div(x,y)((x)+(y)-1)/(y)

可能導致另一種情況,sizeof ceil_div(1,2)是一個C表達式,可以計算ceil_div(1,2)類型的大小,它擴展為:

sizeof((1)+(2)-1)/(2)

這將采用整數(shù)的大小并將其除以2,而除法包含在內(nèi)部的sizeof之外。所以整個宏定義的括號可防止此類問題。那么,下面是定義ceil_div的正確方法如下

#define?ceil_div(x,y)((((x)+(y)-1)/(y))

吞噬分號

通常需要定義一個擴展為復合語句的宏。例如,考慮以下宏,該宏跨空格字符前進一個指針(參數(shù)p表示在何處查找):

#define?SKIP_SPACES(p,?limit)??\
{?char?*lim?=?(limit);?????????\
??while?(p?????if?(*p++?!=?'?')?{?????????\
??????p--;?break;?}}}

該宏定義必須是單個邏輯行,嚴格來說,該調(diào)用擴展為復合語句,這是一個完整的語句,不需要用分號結(jié)束。

但是,由于它看起來像函數(shù)調(diào)用,因此,如果可以像使用函數(shù)調(diào)用一樣使用它,則可以最大程度地減少混亂,然后再寫一個分號,就像在SKIP_SPACES(p,lim)中一樣。

這可能會在else語句之前出問題,因為分號實際上是空語句。假設(shè)你寫

if?(*p?!=?0)
??SKIP_SPACES?(p,?lim);
else?…

在if條件和else條件之間存在兩個語句(復合語句和null語句)使C代碼無效。

怎么解決?我們可以使用do…while語句更改宏SKIP_SPACES的定義以解決此問題。方法如下:

#define?SKIP_SPACES(p,?limit)?????\
do?{?char?*lim?=?(limit);?????????\
?????while?(p????????if?(*p++?!=?'?')?{?????????\
?????????p--;?break;?}}}??????????\
while?(0)

SKIP_SPACES (p, lim);擴展為

do?{…}?while?(0);

這是一個陳述,循環(huán)僅執(zhí)行一次,而且大多數(shù)編譯器不會為此生成任何額外的代碼。

重復調(diào)用

我們常見的“最小”定義一個宏min,如下所示:

#define?min(X,?Y)??((X)?

當將此宏與包含副作用的參數(shù)一起使用時,如此處所示,

next?=?min(x?+?y,foo(z));

它擴展如下:

next?=?((x?+?y)?

其中x + y替換了X,而foo(z)替換了Y。

函數(shù)foo出現(xiàn)在程序中的語句中僅使用一次,但是表達式foo(z)已兩次替換到宏擴展中。結(jié)果,執(zhí)行該語句時可能會兩次調(diào)用foo,所以min是一個不安全的宏。

解決此問題的最佳方法是以僅計算一次foo(z)值的方式定義min。C語言沒有提供執(zhí)行此操作的標準方法,但是可以使用GNU擴展來完成此操作,如下所示:

#define?min(X,?Y)????????????????\
({?typeof?(X)?x_?=?(X);??????????\
???typeof?(Y)?y_?=?(Y);??????????\
???(x_?

“({{…})”符號產(chǎn)生一個復合表達式,它的值是其最后一條語句的值。

如果不使用GNU C擴展,唯一的解決方案是在使用宏min時要小心。例如計算foo(z)的值時,將其保存在變量中,然后在min中使用該變量:

//假設(shè)foo返回int類型
#define?min(X,?Y)??((X)?

{
??int?tem?=?foo?(z);
??next?=?min?(x?+?y,?tem);
}

自引用宏

自引用宏是其名稱出現(xiàn)在其定義中的宏。我們知道所有宏定義都將被重新掃描以查找更多要替換的宏,如果自引用被認為是宏的使用,它將產(chǎn)生無限大的擴展。

為防止這種情況,自引用不被視為宏調(diào)用。它原樣傳遞到預處理器輸出中。舉個例子

#define?foo?(4?+?foo)

按照普通規(guī)則,其宏定義分析如下

  1. 對foo的每個引用都將擴展為(4 + foo);

  2. 然后將對其進行重新掃描,并將其擴展為(4 +(4 + foo));

  3. 以此類推,直到計算機內(nèi)存耗盡。

自引用規(guī)則將這一過程縮短了一步,即(4 + foo),因此此宏定義可能會導致程序在引用foo的任何地方將foo的值加4。

閱讀程序的人看到foo是變量,就難以記得它也是宏,真的會坑爹的。它的一種常見有用用法是創(chuàng)建一個可擴展為其自身的宏。如果你寫

#define?EPERM?EPERM

然后宏EPERM擴展為EPERM。實際上,每當在運行文本中使用預處理器時,預處理器都會將其單獨保留。

如果宏x擴展為使用宏y,而y的擴展引用了宏x,則這是x的間接自引用。在這種情況下,x也不展開,舉個例子

#define?x?(4?+?y)
#define?y?(2?*?x)

然后x和y擴展如下:

x→(4?+?y)
?????→(4?+(2?*?x))

y→(2?*?x)
?????→(2?*(4?+?y))

當每個宏出現(xiàn)在另一個宏的定義中時,它們將被展開,但是當它間接出現(xiàn)在其自己的定義中時,則不會被展開。

參數(shù)預掃描處理

宏參數(shù)在被替換為宏主體之前必須經(jīng)過完全宏擴展,替換后,將再次掃描整個宏主體,包括替換的參數(shù),以查找要擴展的宏。

如果參數(shù)包含任何宏調(diào)用,則它們將在第一次掃描時擴展,那么結(jié)果不包含任何宏調(diào)用,因此第二次掃描不會更改它。

如果按照給定的方式替換了參數(shù),并且沒有進行預掃描,則剩余的單個掃描將找到相同的宏調(diào)用并產(chǎn)生相同的結(jié)果。

預掃描處理在以下三種特殊情況下有大的作用。

對宏的嵌套調(diào)用

當宏的參數(shù)包含對該宏的調(diào)用時,就會發(fā)生對宏的嵌套調(diào)用,舉個例子。

如果f是期望一個參數(shù)的宏,則f(f(1))是對f的嵌套調(diào)用對。通過擴展f(1)并將其代入f的定義來進行所需的擴展。預掃描會導致發(fā)生預期的結(jié)果。

如果沒有預掃描,f(1)本身將被替換為參數(shù),并且f的內(nèi)部使用將在主掃描期間作為間接自引用出現(xiàn),并且不會擴展。

調(diào)用其他可進行字符串化或連接的宏的宏

如果參數(shù)是字符串化或串聯(lián)的,則不會進行預掃描。

如果要擴展宏,然后對其擴展進行字符串化或串聯(lián),則可以通過使一個宏調(diào)用進行該字符串化或串聯(lián)的另一宏來實現(xiàn)。舉個例子

#define?AFTERX(x)?X_?##?x
#define?XAFTERX(x)?AFTERX(x)
#define?TABLESIZE?1024
#define?BUFSIZE?TABLESIZE

然后AFTERX(BUFSIZE)擴展為X_BUFSIZE,而XAFTERX(BUFSIZE)擴展為X_1024而不是X_TABLESIZE,預掃描始終會進行完整的擴展。

參數(shù)中使用的宏,其擴展名包含未屏蔽的逗號。

這可能導致使用錯誤數(shù)量的參數(shù)調(diào)用在第二次掃描時擴展的宏。舉個例子

#define?foo??a,b
#define?bar(x)?lose(x)
#define?lose(x)?(1?+?(x))

我們預期的結(jié)果是bar(foo)變成(1 +(foo)),然后變成(1 +(a,b))。

然而bar(foo)擴展為loss(a,b)會出錯,因為Los需要一個參數(shù)。在這種情況下,該問題可以通過使用相同的括號輕松解決,該括號應用于防止算術(shù)運算的錯誤嵌套:

#define?foo?(a,b)
or
#define?bar(x)?lose((x))

多余的一對括號可防止foo定義中的逗號被解釋為參數(shù)分隔符。

參數(shù)中的換行符

類似函數(shù)的宏的調(diào)用可以擴展到許多邏輯行,但是在本實施方式中,整個擴展是一行完成的。

因此,由編譯器或調(diào)試器發(fā)出的行號是指調(diào)用在其上開始的行,這可能與包含導致問題的參數(shù)的行不同,例如:

#define?ignore_second_arg(a,b,c)?a;?c

ignore_second_arg?(foo?(),
???????????????????ignored?(),
???????????????????syntax?error);

Syntax error on tokens觸發(fā)的語法錯誤會導致錯誤消息引用第三行(ignore_second_arg行),即使有問題的代碼來自第五行。

參考資料:

http://gcc.gnu.org/onlinedocs/cpp/Macros.html



-END-


來源 | 技術(shù)讓夢想更偉大

作者 | 李肖遙


|?整理文章為傳播相關(guān)技術(shù),版權(quán)歸原作者所有?|

|?如有侵權(quán),請聯(lián)系刪除?|


【1】嵌入式研發(fā)10多年,工程師悟出這些道理

【2】當談起嵌入式工程師,究竟在談些什么

【3】嵌入式工程師出路之我見:就業(yè),技術(shù),行業(yè)...

【4】為什么嵌入式工程師會對8位MCU有誤解?

【5】嵌入式工程師結(jié)合經(jīng)歷聊硬件工程師和軟件工程師哪個更有前途?



免責聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺僅提供信息存儲服務(wù)。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!

嵌入式ARM

掃描二維碼,關(guān)注更多精彩內(nèi)容

本站聲明: 本文章由作者或相關(guān)機構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫毥谦F公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉(zhuǎn)型技術(shù)解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...

關(guān)鍵字: AWS AN BSP 數(shù)字化

倫敦2024年8月29日 /美通社/ -- 英國汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動 BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運行,同時企業(yè)卻面臨越來越多業(yè)務(wù)中斷的風險,如企業(yè)系統(tǒng)復雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報道,騰訊和網(wǎng)易近期正在縮減他們對日本游戲市場的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會開幕式在貴陽舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導體

8月28日消息,在2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機 衛(wèi)星通信

要點: 有效應對環(huán)境變化,經(jīng)營業(yè)績穩(wěn)中有升 落實提質(zhì)增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競爭力 堅持高質(zhì)量發(fā)展策略,塑強核心競爭優(yōu)勢...

關(guān)鍵字: 通信 BSP 電信運營商 數(shù)字經(jīng)濟

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺與中國電影電視技術(shù)學會聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會上宣布正式成立。 活動現(xiàn)場 NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會上,軟通動力信息技術(shù)(集團)股份有限公司(以下簡稱"軟通動力")與長三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉