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

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

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

當(dāng)使用參數(shù)調(diào)用宏時(shí),會(huì)將參數(shù)替換為宏主體

Q4MP_gh_c472c21 ? 來(lái)源:技術(shù)讓夢(mèng)想更偉大 ? 作者:李肖遙 ? 2020-11-16 16:41 ? 次閱讀

語(yǔ)法錯(cuò)誤

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

#definetwice(x)(2*(x)) #definecall_with_1(x)x(1) call_with_1(twice) //x=1 →twice(1) →(2*(1))

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

#definestrange(file)fprintf(file,"%s%d", … strange(stderr)p,35) →fprintf(stderr,"%s%d",p,35)

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

運(yùn)算符優(yōu)先級(jí)問(wèn)題

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

#defineceil_div(x,y)(x+y-1)/y

假定其用法如下:

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

拓展開是

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

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

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

如果我們將宏定義為

#defineceil_div(x,y)((x)+(y)-1)/(y)

可能導(dǎo)致另一種情況,sizeof ceil_div(1,2)是一個(gè)C表達(dá)式,可以計(jì)算ceil_div(1,2)類型的大小,它擴(kuò)展為:

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

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

#defineceil_div(x,y)((((x)+(y)-1)/(y))

吞噬分號(hào)

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

#defineSKIP_SPACES(p,limit) {char*lim=(limit); while(p

該宏定義必須是單個(gè)邏輯行,嚴(yán)格來(lái)說(shuō),該調(diào)用擴(kuò)展為復(fù)合語(yǔ)句,這是一個(gè)完整的語(yǔ)句,不需要用分號(hào)結(jié)束。

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

這可能會(huì)在else語(yǔ)句之前出問(wèn)題,因?yàn)榉痔?hào)實(shí)際上是空語(yǔ)句。假設(shè)你寫

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

在if條件和else條件之間存在兩個(gè)語(yǔ)句(復(fù)合語(yǔ)句和null語(yǔ)句)使C代碼無(wú)效。

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

#defineSKIP_SPACES(p,limit) do{char*lim=(limit); while(p

SKIP_SPACES (p, lim);擴(kuò)展為

do{…}while(0);

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

重復(fù)調(diào)用

我們常見(jiàn)的“最小”定義一個(gè)宏min,如下所示:

#definemin(X,Y)((X)

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

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

它擴(kuò)展如下:

next=((x+y)

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

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

解決此問(wèn)題的最佳方法是以僅計(jì)算一次foo(z)值的方式定義min。C語(yǔ)言沒(méi)有提供執(zhí)行此操作的標(biāo)準(zhǔn)方法,但是可以使用GNU擴(kuò)展來(lái)完成此操作,如下所示:

#definemin(X,Y) ({typeof(X)x_=(X); typeof(Y)y_=(Y); (x_

“({{…})”符號(hào)產(chǎn)生一個(gè)復(fù)合表達(dá)式,它的值是其最后一條語(yǔ)句的值。

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

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

自引用宏

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

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

#definefoo(4+foo)

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

對(duì)foo的每個(gè)引用都將擴(kuò)展為(4 + foo);

然后將對(duì)其進(jìn)行重新掃描,并將其擴(kuò)展為(4 +(4 + foo));

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

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

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

#defineEPERMEPERM

然后宏EPERM擴(kuò)展為EPERM。實(shí)際上,每當(dāng)在運(yùn)行文本中使用預(yù)處理器時(shí),預(yù)處理器都會(huì)將其單獨(dú)保留。

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

#definex(4+y) #definey(2*x)

然后x和y擴(kuò)展如下:

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

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

參數(shù)預(yù)掃描處理

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

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

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

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

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

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

如果f是期望一個(gè)參數(shù)的宏,則f(f(1))是對(duì)f的嵌套調(diào)用對(duì)。通過(guò)擴(kuò)展f(1)并將其代入f的定義來(lái)進(jìn)行所需的擴(kuò)展。預(yù)掃描會(huì)導(dǎo)致發(fā)生預(yù)期的結(jié)果。

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

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

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

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

#defineAFTERX(x)X_##x #defineXAFTERX(x)AFTERX(x) #defineTABLESIZE1024 #defineBUFSIZETABLESIZE

然后AFTERX(BUFSIZE)擴(kuò)展為X_BUFSIZE,而XAFTERX(BUFSIZE)擴(kuò)展為X_1024而不是X_TABLESIZE,預(yù)掃描始終會(huì)進(jìn)行完整的擴(kuò)展。

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

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

#definefooa,b #definebar(x)lose(x) #definelose(x)(1+(x))

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

然而bar(foo)擴(kuò)展為loss(a,b)會(huì)出錯(cuò),因?yàn)長(zhǎng)os需要一個(gè)參數(shù)。在這種情況下,該問(wèn)題可以通過(guò)使用相同的括號(hào)輕松解決,該括號(hào)應(yīng)用于防止算術(shù)運(yùn)算的錯(cuò)誤嵌套:

#definefoo(a,b) or #definebar(x)lose((x))

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

參數(shù)中的換行符

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

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

#defineignore_second_arg(a,b,c)a;c ignore_second_arg(foo(), ignored(), syntaxerror);

由Syntax error on tokens觸發(fā)的語(yǔ)法錯(cuò)誤會(huì)導(dǎo)致錯(cuò)誤消息引用第三行(ignore_second_arg行),即使有問(wèn)題的代碼來(lái)自第五行。

責(zé)任編輯:lq

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

    關(guān)注

    30

    文章

    4788

    瀏覽量

    68628
  • 變量
    +關(guān)注

    關(guān)注

    0

    文章

    613

    瀏覽量

    28373
  • 語(yǔ)法
    +關(guān)注

    關(guān)注

    0

    文章

    44

    瀏覽量

    9818

原文標(biāo)題:避免這7個(gè)誤區(qū),才能讓【宏】削鐵如泥

文章出處:【微信號(hào):gh_c472c2199c88,微信公眾號(hào):嵌入式微處理器】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    景智駕總部落地衢州

    近日,景智駕衢州總部落地儀式在南孔圣地衢州東方橋庵里大酒店盛大舉行!衢州智造新城黨工委副書記、管委會(huì)常務(wù)副主任巫建民與景智駕創(chuàng)始人及CEO劉飛龍博士共同為落地儀式剪彩。
    的頭像 發(fā)表于 12-24 17:10 ?319次閱讀

    李彥宣布:百度文心大模型日調(diào)用量超15億

    在百度世界2024大會(huì)上,百度公司創(chuàng)始人李彥宣布了一項(xiàng)令人矚目的數(shù)據(jù):百度文心大模型的日調(diào)用量已經(jīng)超過(guò)15億次。這一數(shù)據(jù)不僅彰顯了百度在人工智能領(lǐng)域的領(lǐng)先地位,也預(yù)示著人工智能技術(shù)的廣泛應(yīng)用
    的頭像 發(fā)表于 11-12 10:40 ?211次閱讀

    SV中define定義的用法

    SV中使用預(yù)處理指令`define來(lái)定義,可以用來(lái)創(chuàng)建文本替換。根據(jù)場(chǎng)景不同,`define主要用來(lái)定義常量、簡(jiǎn)化復(fù)雜的表達(dá)式或代碼段以及提高代碼的可移植性。其基本語(yǔ)法為:
    的頭像 發(fā)表于 10-21 14:22 ?583次閱讀

    科技與德賽電池簽約儲(chǔ)能電站合作項(xiàng)目

    科技和德賽電池儲(chǔ)能電站合作項(xiàng)目簽約儀式在勝科技隆重舉行。勝科技董事長(zhǎng)陳濤、惠州德賽電池董事長(zhǎng)曾劍云出席簽約儀式。勝科技CEO趙啟祥、惠州德賽電池總裁程月帆代表雙方簽約。
    的頭像 發(fā)表于 09-27 17:15 ?828次閱讀

    行業(yè)領(lǐng)導(dǎo)蒞臨景智駕考察交流

    近日,中國(guó)人才研究會(huì)汽車人才專業(yè)委員會(huì)理事長(zhǎng) 朱明榮先生、同濟(jì)科技園常務(wù)副主任榮文偉先生一行蒞臨景智駕上海嘉定技術(shù)中心進(jìn)行考察交流。 在深入了解景智駕公司發(fā)展情況后,各位領(lǐng)導(dǎo)對(duì)景智駕目前情況及未來(lái)發(fā)展給予了高度評(píng)價(jià),并就
    的頭像 發(fā)表于 09-03 09:37 ?458次閱讀

    TINA仿真運(yùn)放電路時(shí),運(yùn)放的參數(shù)中是否可以增加溫度參數(shù)呢?

    我們目前在使用TINA仿真運(yùn)放電路的參數(shù),從模型中找到了外圍電阻是有溫度參數(shù)可以設(shè)定的,包括線性溫度系數(shù),二次溫度系數(shù),指數(shù)溫度系數(shù)。但是運(yùn)放本身的參數(shù)中目前沒(méi)有找到與溫度相關(guān)的參數(shù)
    發(fā)表于 08-14 07:31

    科技擬收購(gòu)APCB 100%股權(quán)

    科技近期發(fā)布重要公告,宣布其計(jì)劃通過(guò)全資子公司新加坡勝及PSL,以不超過(guò)2.787億元人民幣的現(xiàn)金,全面收購(gòu)APCB Electronics(Thailand)Co.,Ltd.(簡(jiǎn)稱APCB)的100%股權(quán)。此次收購(gòu)標(biāo)志著勝
    的頭像 發(fā)表于 08-12 15:06 ?557次閱讀

    LF412用PSPICE驗(yàn)證該運(yùn)放的SPICE模型的遇到的疑問(wèn)求解

    關(guān)于LF412,我用PSPICE驗(yàn)證該運(yùn)放的SPICE模型的遇到以下問(wèn)題: 1.該模型缺少電壓噪聲和電流噪聲參數(shù),有更加準(zhǔn)確的SPICE模型嗎; 2.該模型開環(huán)輸出阻抗為純阻性
    發(fā)表于 08-06 06:16

    請(qǐng)問(wèn)如何為idf.py build指定特定參數(shù)

    如何為idf.py build指定特定參數(shù): (1)build目錄(即不是默認(rèn)的build目錄名稱), (2)sdkconfig(即不是默認(rèn)的文件名) 如何將基本的模塊統(tǒng)一編譯,而應(yīng)用模塊獨(dú)立編譯?因?yàn)橛卸鄠€(gè)應(yīng)用模塊(代碼是一套,只是用不同的隔開)
    發(fā)表于 06-12 08:07

    氣體成功斬獲無(wú)錫華潤(rùn)上華電子大宗載氣項(xiàng)目,進(jìn)軍存量市場(chǎng)

    此次中標(biāo)被視為氣體領(lǐng)域重大突破,標(biāo)志著成熟晶圓產(chǎn)線電子大宗載氣存量業(yè)務(wù)的替換成為可能,從而開拓金電子大宗載氣業(yè)務(wù)新的增長(zhǎng)點(diǎn)。
    的頭像 發(fā)表于 04-19 15:27 ?417次閱讀

    證監(jiān)會(huì)同意鑫科技創(chuàng)業(yè)板IPO注冊(cè)

    證監(jiān)會(huì)發(fā)布關(guān)于同意浙江鑫科技股份有限公司首次公開發(fā)行股票注冊(cè)的批復(fù),這標(biāo)志著鑫科技在深交所創(chuàng)業(yè)板上市的腳步愈發(fā)臨近。作為國(guó)內(nèi)較早開始應(yīng)用鍛造工藝生產(chǎn)汽車鋁合金車輪的高新技術(shù)企業(yè),鑫科技在行業(yè)內(nèi)具有顯著的技術(shù)和市場(chǎng)優(yōu)勢(shì)。
    的頭像 發(fā)表于 03-11 15:31 ?561次閱讀

    鑫科技即將掛牌上市

    浙江鑫科技股份有限公司(以下簡(jiǎn)稱“鑫科技”)成功獲得證監(jiān)會(huì)的批文,即將掛牌上市,這標(biāo)志著鑫科技將成為資本市場(chǎng)鍛造鋁合金車輪第一股,迎來(lái)產(chǎn)業(yè)經(jīng)營(yíng)和資本運(yùn)營(yíng)雙輪驅(qū)動(dòng)的全新發(fā)展階段。
    的頭像 發(fā)表于 03-11 15:30 ?787次閱讀

    CubeMx生成的stm32f013vet6設(shè)備,對(duì)于SDIOCLK頻率設(shè)置的定義與手冊(cè)里面對(duì)不上是為什么?

    在手冊(cè)里面有SDIO adapter clock (SDIOCLK = HCLK) 當(dāng)HCLK設(shè)置為72MHz的時(shí)候進(jìn)行配置SDIO_CK, 通過(guò)寄存器SDIO_CLKCR的位7:0(CLKDIV
    發(fā)表于 03-08 08:29

    如何利用Rust過(guò)程實(shí)現(xiàn)derive-with庫(kù)呢?

    通過(guò)派生 #[derive(With)] 給結(jié)構(gòu)體字段生成 with_xxx 方法,通過(guò)鏈?zhǔn)?b class='flag-5'>調(diào)用 with_xxx 方法來(lái)構(gòu)造結(jié)構(gòu)體。
    的頭像 發(fā)表于 01-25 09:51 ?304次閱讀

    EG網(wǎng)關(guān)串口連接永PLC應(yīng)用案例

    EG網(wǎng)關(guān)串口連接永PLC應(yīng)用案例 前言:永PLC是一款國(guó)產(chǎn)優(yōu)秀的可編程控制器,廣泛應(yīng)于工業(yè)控制領(lǐng)域,是一款性能高,運(yùn)行穩(wěn)定的控制器。此次我們要把永FBs/B1系列PLC通過(guò)Modbus協(xié)議連接
    的頭像 發(fā)表于 01-03 14:45 ?611次閱讀
    EG網(wǎng)關(guān)串口連接永<b class='flag-5'>宏</b>PLC應(yīng)用案例