關於宏的正確用法和錯誤用法

來源:互聯網
上載者:User

來源於microchip資深工程師的網路研討會

     關於C的預先處理和它對宏定義的預先處理,我們來看一些宏的正確和錯誤的拓展,以及怎樣拓展他們,所有的規範都是建立在ANSI C編譯器基礎上的。

1.    簡單的宏處理:

     #define  name  “replacement text”

     樣本用法:

     #define START  20

        foobar = START;

“replacement text”可以被其他的宏定義展開

例如:   

         #define LIMIT  100

         #define PREF   LIMIT

         setting = PREF;

這種宏展開可以拓展至沒有宏定義為止


2.    防止重複定義的宏

例如:

#ifndef LCDH
// header file contents go here
// now stop this being included again
#define LCDH
#endif

此種使用的作用範圍是從定義到模組結束,或者遇到#undef


3.帶參數的宏定義


#define  name(argument-list)  replacement text


參數會替換掉“replacement text”裡的對應項

樣本用法:

#define DIFF(a, b)  ((a)>=(b) ? (a)-(b) : -1)

result = DIFF(input, 6);

展開後是:

result = ((input)>=(6) ? (input)-(6) : -1);

前置處理器能夠替換一個或者更多的變數參數,實現更加複雜的替換。為了防止產生非預料的結果,每個替換的變數都要加上括弧。

4.字串替換

例如:

#define PRODUCT(year)  "Matrix" #year

const char * productName = PRODUCT(2000);

展開後:

const char * productName = "Matrix" "2000";

最終變成:

const char * productName = "Matrix2000";

當宏定義中有變數參數時,你就能夠使用一些特殊的操作符。其中一個就是#操作符,它可以把變數轉換成為c字串。在這個例子裡,經過預先處理,變數productName將會指向字串“Matrix2000”,在擴充中,兩個符號#year被變數替換,並且產生的字串和“Matrix”串連。

5.串連變數

例如:

#define BLATCH(bit)   LATB##bit
BLATCH(4) = 1;

替換成為:

LATB4 = 1;

另一個宏操作符就是“##”,可以讓你串連任意的兩個符號,而不僅僅是字串。在這個例子中,操作符##用來串連符號LATB和bit變數。當產生替換時,它就會擴充成LATB4,這就是PIC處理器的latch B的第四位連接埠。

這個功能很有用,但是在變數本身是其他需要前置處理器擴充的宏時,會有一個潛在的問題,我們看下面:

#define BLATCH(bit)   LATB##bit

#define MOTOR  4

BLATCH(MOTOR) = 1;

經過前置處理器

會擴充成為:LATBMOTOR = 1;

這顯然不是我門想要的結果

問題的根源是,當宏變數進行替換時,宏變數不會被優先擴充。前置處理器的定義是,當只有宏變數不在#,##之前或者不在##之後的時候才能被擴充,所以當“MOTOR插入到“BLATCH”中時,他不會被進一步擴充,因為它臨近著##操作符。

幸運的時,我們找到了一個方法實現這種擴充:

可以分兩步,來進行:

#define PASTE(a, b)    a##b

#define BLATCH(bit)   PASTE(LATB, bit)


#define MOTOR  4


BLATCH(MOTOR) = 1;

經過前置處理器:

LATB4 = 1;

第二個宏實現變數的擴充,第二個宏實現了串連,這就實現了我們要的功能。


我們來看最後一個複雜一些的例子:

#define PASTE2(a,b)   a##b

#define PASTE(a,b)    PASTE2(a,b)


#ifdef  PIC18  // set for PIC18 devices
#define IO            LAT
#else
#define IO            PORT
#endif
#define PIN           4
#define RDPORT        PASTE(PORT, PIN)
#define RDLATCH       PASTE(IO, PIN)     
#define IOREAD(loc)   PASTE(RD, loc)


使用:

result = IOREAD(LATCH);

當我們看懂這個例子的時候,會覺得很有成就感吧,但當你認為這段代碼會工作的很順利時,你將會發現編譯器錯誤或者其他的不良後果。

   最後的結果是:result = PASTE(LAT, 4);

並不是我們想要的,究其原因就是每個宏擴充在一個運算式裡只能實現一次,具體過程是這樣的:


IOREAD(LATCH)
  -> PASTE(RD, LATCH)
    -> RDLATCH
      -> PASTE(IO, PIN)
        -> PASTE(LAT, 4); ✘

在進行PASTE第二次擴充時,前置處理器不會進行擴充。


解決辦法就是另外再寫一個,和PASTE同樣功能,但是名字不一養的宏來使用。





聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.