欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C/C++中宏/Macro的深入講解

 更新時(shí)間:2019年06月23日 14:24:09   作者:劉哇勇  
這篇文章主要給大家介紹了關(guān)于C/C++中宏/Macro的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用C/C++具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧

前言

宏(Macro)本質(zhì)上就是代碼片段,通過別名來使用。在編譯前的預(yù)處理中,宏會(huì)被替換為真實(shí)所指代的代碼片段,即下圖中 Preprocessor 處理的部分。

C/C++ 代碼編譯過程 - 圖片來自 ntu.edu.sg

根據(jù)用法的不同,分兩種,Object-like 和 Function-like。前者用于 Object 對(duì)象,后者用于函數(shù)方法。

C/C++ 代碼編譯過程中,可通過相應(yīng)參數(shù)來獲取到各編譯步驟中的產(chǎn)出,比如想看被預(yù)處理編譯之后的宏,使用 gcc 使加上 -E 參數(shù)。

$ gcc -E macro.c

宏的定義

通過 #define 指令定義一個(gè)宏。

#define NAME_OF_MACRO value

比如,以下代碼定義了一個(gè)名為 BUFFER_SIZE 的宏,指代 1024 這個(gè)數(shù)字。

#define BUFFER_SIZE 1024

使用時(shí),

foo = (char *) malloc (BUFFER_SIZE);

使用預(yù)處理器編譯:

$ gcc -E test.c

編譯結(jié)果:

foo = (char *) malloc (1024);

多行

宏的定義是跟隨 #define 在一同一行內(nèi)的,但可通過 反斜杠 \ 實(shí)現(xiàn)換行從而定義出多行的宏。


多行的宏經(jīng)過編譯后會(huì)還原到一行中。

test.c

#include <stdio.h>
#define GREETING_STR \
 "hello \
world"

int main() { printf(GREETING_STR); }

編譯后:

int main() {
 printf("hello world");
 return 0;
}

宏展開時(shí)的順序

宏的展開是在處理源碼時(shí)按照其出現(xiàn)位置進(jìn)行的,如果宏定義有嵌套關(guān)系,也是層層進(jìn)行展開,比如:

#include <stdio.h>

#define GREETING_NAME "wayou"
#define GREETING "hello," GREETING_NAME

int main() {
 printf(GREETING);
 return 0;
}

首先遇到 GREETING,將其展開成 GREETING_NAME "wayou",然后發(fā)現(xiàn)另一個(gè)宏 GREETING_NAME,將其展開最后得到 "hello," "wayou"。所以編譯后的代碼為:

int main() {
 printf("hello," "wayou");
 return 0;
}

其展開的順序并不是宏定義時(shí)的順序,為了驗(yàn)證,可將上面示例代碼中兩個(gè)宏的定義調(diào)換一下,得到:

-#define GREETING_NAME "wayou"
#define GREETING "hello," GREETING_NAME
+#define GREETING_NAME "wayou"

再次編譯查看產(chǎn)出,會(huì)發(fā)現(xiàn)沒有區(qū)別,也不會(huì)報(bào) GREETING 中所依賴的 GREETING_NAME 找不到的錯(cuò)。其實(shí) #define 只是告訴編譯器定義了這么個(gè)宏,而具體的求值,則是使用宏的地方才開始的。

像下面這樣,當(dāng)宏存在覆蓋時(shí),會(huì)以新的為準(zhǔn),其結(jié)果為 37。

#define BUFSIZE 1020
#define TABLESIZE BUFSIZE
#undef BUFSIZE
#define BUFSIZE 37

Object-like 宏

Object-like 類型的宏看起來就像普通的數(shù)據(jù)對(duì)象,故名。多用于數(shù)字常量的情形下。且宏名一般使用全大寫形式方便識(shí)別。像上面示例中,都是 Object-like 的。

Function-like 宏

也可定義出使用時(shí)像是方法調(diào)用一樣的宏,這便是 Function-like 類型的宏。

#define lang_init() c_init()
lang_init()

// 編譯后
c_init()

函數(shù)類型的宏只在以方法調(diào)用形式使用時(shí)才會(huì)被展開,即名稱后加括號(hào),否則會(huì)被忽略。當(dāng)宏名和函數(shù)名重名時(shí),這一策略就會(huì)顯得有用了,比如:

extern void foo(void);
#define foo() /* optimized inline version */
…
 foo();
 funcptr = foo;

這里 foo() 的調(diào)用會(huì)來自宏里面定義的那個(gè)函數(shù),而 funcptr 會(huì)正確地指向函數(shù)地址,如果后者也被宏展開,則成了 funptr=foo() 顯然就不對(duì)了。

函數(shù)類型的宏在定義時(shí)需注意,宏名與后面括號(hào)不能有空格,否則就是普通的 Object-like 類型對(duì)象。

#define lang_init ()  c_init()
lang_init()

// 編譯后:
() c_init()()

宏的參數(shù)

函數(shù)類型的宏,可以像正常函數(shù)一樣指定入?yún)?,入?yún)⑿铻槎禾?hào)分隔合法的 C 字面量。

#define min(X, Y) ((X) < (Y) ? (X) : (Y))
 x = min(a, b);     → x = ((a) < (b) ? (a) : (b));
 y = min(1, 2);     → y = ((1) < (2) ? (1) : (2));
 z = min(a + 28, *p);  → z = ((a + 28) < (*p) ? (a + 28) : (*p));

入?yún)⒅械睦ㄌ?hào)

入?yún)⒅兄恍枰ㄌ?hào)對(duì)稱,但不要求方括號(hào)或花括號(hào)成對(duì)出現(xiàn),所以下面的代碼:

macro (array[x = y, x + 1])

其入?yún)?shí)際為 array[x = y 和 x + 1]。

入?yún)⒌恼归_

入?yún)⒈举|(zhì)上也是宏,對(duì)象類型的宏,在函數(shù)宏展示時(shí),這些參數(shù)也被展示到了函數(shù)宏的函數(shù)體里。

 min (min (a, b), c)

首先被展開成:

min (((a) < (b) ? (a) : (b)), (c))

然后進(jìn)一步展開成(此處換行為方便閱讀,實(shí)際編譯后沒有):

((((a) < (b) ? (a) : (b))) < (c)
 ? (((a) < (b) ? (a) : (b)))
 : (c))

參數(shù)的缺省

函數(shù)宏在使用時(shí)其入?yún)⒖扇笔?,但不能全部缺省,至少提供一個(gè)入?yún)ⅰ?/p>

min(, b)    → ((  ) < (b) ? (  ) : (b))
min(a, )    → ((a ) < ( ) ? (a ) : ( ))
min(,)     → ((  ) < ( ) ? (  ) : ( ))
min((,),)    → (((,)) < ( ) ? ((,)) : ( ))

min()   error→ macro "min" requires 2 arguments, but only 1 given
min(,,)  error→ macro "min" passed 3 arguments, but takes just 2

字符化/Stringizing

如果函數(shù)宏中入?yún)⒃谧址?,是不?huì)被展開的,它就是普通的字符串字面量,這樣的結(jié)果是符合預(yù)期的。

#define foo(x) x, "x"
foo(bar)    → bar, "x"

但如果確實(shí)想將入?yún)⒄归_成字符串,可在使用入?yún)r(shí),加上 # 前綴。

#define WARN_IF(EXP) \
do { if (EXP) \
    fprintf (stderr, "Warning: " #EXP "\n"); } \
while (0)
WARN_IF (x == 0);
   → do { if (x == 0)
      fprintf (stderr, "Warning: " "x == 0" "\n"); } while (0);

此處 #EXP 在字符串中會(huì)被正確展開。What's more, 如果這里的 x 也是宏,那只會(huì)在 if 語句中進(jìn)行展開。

拼接

通過 ## 可將兩個(gè)宏展開成一個(gè),即將兩者進(jìn)行了拼接,這種操作叫 "token pasting",或 "token concatenation",就是拼接嘛。

宏拼接一般用在需要拼接的宏是來自宏參數(shù)的情況,其他情況,大可直接將兩個(gè)宏寫在一起即可,用不著 ## 指令。

考察下面這個(gè)場(chǎng)景,其中命令名重復(fù)出現(xiàn):

struct command
{
 char *name;
 void (*function) (void);
};

struct command commands[] =
{
 { "quit", quit_command },
 { "help", help_command },
 …
};

通過定義宏配合拼接,可達(dá)到精簡(jiǎn)代碼的目的:

#define COMMAND(NAME) { #NAME, NAME ## _command }

struct command commands[] =
{
 COMMAND (quit),
 COMMAND (help),
 …
};

不定參數(shù)

像普通函數(shù)一樣,函數(shù)類型的宏也可定義接收不定參數(shù)。

#define eprintf(…) fprintf (stderr, __VA_ARGS__)

調(diào)用時(shí),命名參數(shù)后面,包括逗號(hào)都會(huì)進(jìn)入到 __VA_ARGS__ 關(guān)鍵字當(dāng)中。但 C++ 中還支持對(duì)這些參數(shù)命名從而不用 __VA_ARGS__。

eprintf ("%s:%d: ", input_file, lineno)

// 編譯后:
fprintf (stderr, "%s:%d: ", input_file, lineno)

C++ 中可這么寫:

#define eprintf(args…) fprintf (stderr, args)

不定參數(shù)與命名參數(shù)混合的情況

不定參數(shù)為命名參數(shù)后面省略的部分。

#define eprintf(format, …) fprintf (stderr, format, __VA_ARGS__)

預(yù)設(shè)的宏

標(biāo)準(zhǔn)庫(kù)及編譯器中預(yù)設(shè)了一些有用的宏,可以在這里 查閱。

取消和重置宏

當(dāng)某個(gè)宏不再使用時(shí),可通過 #undef 將取注銷掉。#undef 后緊跟宏名,后面不要跟其他東西,即使是函數(shù)類型的宏。

#define FOO 4
x = FOO;    → x = 4;
#undef FOO
x = FOO;    → x = FOO;

兩個(gè)宏相似的定義

滿足以下條件時(shí),我們認(rèn)為兩者是相似的:

  • 類型相同,比如同為對(duì)象類型,或函數(shù)類型的宏
  • 展開后各位置的符號(hào)(token)相同
  • 如果是函數(shù)宏,入?yún)⑾嗤?/li>
  • 空白的不限但出現(xiàn)的位置相同

比如,下面這些是相似的:

#define FOUR (2 + 2)
#define FOUR     (2  +  2)
#define FOUR (2 /* two */ + 2)

而下面這些則不然:

#define FOUR (2 + 2)
#define FOUR ( 2+2 ) // 空白位置不一樣 
#define FOUR (2 * 2) // 宏的內(nèi)容不一樣
#define FOUR(score,and,seven,years,ago) (2 + 2) // 入?yún)⒉灰粯?/pre>

宏重復(fù)定義時(shí)的表現(xiàn)

對(duì)于使用了 #undef 注銷過的宏,再次定義同名的宏時(shí),要求新定義的宏不與老的相似。

而如果說一個(gè)已經(jīng)存在的宏,并沒有注銷,重復(fù)定義時(shí),如果相似,則新的定義會(huì)忽略,如果不相似,編譯器會(huì)報(bào)警告同時(shí)使用新定義的宏。這允許在多個(gè)文件中定義同一個(gè)宏。

相關(guān)資源

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對(duì)腳本之家的支持。

相關(guān)文章

  • C++實(shí)現(xiàn)停車場(chǎng)管理系統(tǒng)的示例代碼

    C++實(shí)現(xiàn)停車場(chǎng)管理系統(tǒng)的示例代碼

    停車場(chǎng)管理系統(tǒng)就是模擬停車場(chǎng)進(jìn)行車輛管理的系統(tǒng),該系統(tǒng)分為汽車信息模塊,用戶使用模塊和管理員用戶模塊,本文將用C++實(shí)現(xiàn)這一簡(jiǎn)單的系統(tǒng),希望對(duì)大家有所幫助
    2023-04-04
  • C++?多態(tài)虛函數(shù)的底層原理深入理解

    C++?多態(tài)虛函數(shù)的底層原理深入理解

    這篇文章主要介紹了C++?多態(tài)虛函數(shù)的底層原理深入理解,多態(tài)是在不同繼承關(guān)系的類對(duì)象,去調(diào)用同一函數(shù),產(chǎn)生了不同的行為,通常是父類調(diào)用子類的重寫函數(shù),在C++中就是?父類指針指向子類對(duì)象,此時(shí)父類指針的向下引用就可以實(shí)現(xiàn)多態(tài)
    2022-08-08
  • C++獲取zip文件列表方法

    C++獲取zip文件列表方法

    本文將介紹獲取zip文件列表的方法,有些新手的朋友可以參考下
    2012-12-12
  • C語言中遞歸的實(shí)際應(yīng)用與經(jīng)典問題

    C語言中遞歸的實(shí)際應(yīng)用與經(jīng)典問題

    函數(shù)以及函數(shù)的遞歸調(diào)用是學(xué)習(xí)C語言必須要掌握的內(nèi)容,且遞歸作為經(jīng)典的算法思想被廣泛應(yīng)用于程序設(shè)計(jì)中,下面這篇文章主要給大家介紹了關(guān)于C語言中遞歸的實(shí)際應(yīng)用與經(jīng)典問題的相關(guān)資料,需要的朋友可以參考下
    2021-09-09
  • C語言實(shí)現(xiàn)文件讀寫

    C語言實(shí)現(xiàn)文件讀寫

    這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)文件讀寫,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-12-12
  • C++中成員函數(shù)和友元函數(shù)的使用及區(qū)別詳解

    C++中成員函數(shù)和友元函數(shù)的使用及區(qū)別詳解

    大家好,本篇文章主要講的是C++中成員函數(shù)和友元函數(shù)的使用及區(qū)別詳解,感興趣的同學(xué)趕快來看一看吧,對(duì)你有幫助的話記得收藏一下
    2022-01-01
  • 用C++實(shí)現(xiàn)隊(duì)列的程序代碼

    用C++實(shí)現(xiàn)隊(duì)列的程序代碼

    本篇文章是對(duì)使用C++實(shí)現(xiàn)隊(duì)列的程序代碼進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-05-05
  • C語言實(shí)現(xiàn)靜態(tài)版通訊錄的示例代碼

    C語言實(shí)現(xiàn)靜態(tài)版通訊錄的示例代碼

    這篇文章主要為大家詳細(xì)介紹了如何利用C語言實(shí)現(xiàn)一個(gè)簡(jiǎn)單的靜態(tài)版通訊錄,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)C語言有一定幫助,需要的可以參考一下
    2022-08-08
  • C++之vector內(nèi)存釋放原理

    C++之vector內(nèi)存釋放原理

    這篇文章主要介紹了C++之vector內(nèi)存釋放原理,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-08-08
  • C++實(shí)例輸入多行數(shù)字到數(shù)組

    C++實(shí)例輸入多行數(shù)字到數(shù)組

    這篇文章主要介紹了C++實(shí)例輸入多行數(shù)字到數(shù)組的相關(guān)資料,這里提供實(shí)例代碼幫助大家學(xué)習(xí)理解,需要的朋友可以參考下
    2016-12-12

最新評(píng)論