您好,登錄后才能下訂單哦!
這篇文章主要講解了“怎么理解C/C++中的宏”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“怎么理解C/C++中的宏”吧!
宏的定義
通過(guò) #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)的,但可通過(guò) 反斜杠 \ 實(shí)現(xiàn)換行從而定義出多行的宏。
多行的宏經(jīng)過(guò)編譯后會(huì)還原到一行中。
test.c
#include <stdio.h>#define GREETING_STR \ "hello \world"int main() { printf(GREETING_STR); }
編譯后:
int main() { printf("hello world"); return 0;}
宏展開(kāi)時(shí)的順序
宏的展開(kāi)是在處理源碼時(shí)按照其出現(xiàn)位置進(jìn)行的,如果宏定義有嵌套關(guān)系,也是層層進(jìn)行展開(kāi),比如:
#include <stdio.h>#define GREETING_NAME "wayou"#define GREETING "hello," GREETING_NAMEint main() { printf(GREETING); return 0;}
首先遇到 GREETING,將其展開(kāi)成 GREETING_NAME "wayou",然后發(fā)現(xiàn)另一個(gè)宏 GREETING_NAME,將其展開(kāi)最后得到 "hello," "wayou"。所以編譯后的代碼為:
int main() { printf("hello," "wayou"); return 0;}
其展開(kāi)的順序并不是宏定義時(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)沒(méi)有區(qū)別,也不會(huì)報(bào) GREETING 中所依賴的 GREETING_NAME 找不到的錯(cuò)。其實(shí) #define 只是告訴編譯器定義了這么個(gè)宏,而具體的求值,則是使用宏的地方才開(kāi)始的。
像下面這樣,當(dāng)宏存在覆蓋時(shí),會(huì)以新的為準(zhǔn),其結(jié)果為 37。
#define BUFSIZE 1020#define TABLESIZE BUFSIZE#undef BUFSIZE#define BUFSIZE 37
Object-like 宏
Object-like 類型的宏看起來(lái)就像普通的數(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ì)被展開(kāi),即名稱后加括號(hào),否則會(huì)被忽略。當(dāng)宏名和函數(shù)名重名時(shí),這一策略就會(huì)顯得有用了,比如:
extern void foo(void);#define foo() /* optimized inline version */… foo(); funcptr = foo;
這里 foo() 的調(diào)用會(huì)來(lái)自宏里面定義的那個(gè)函數(shù),而 funcptr 會(huì)正確地指向函數(shù)地址,如果后者也被宏展開(kāi),則成了 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)⒌恼归_(kāi)
入?yún)⒈举|(zhì)上也是宏,對(duì)象類型的宏,在函數(shù)宏展示時(shí),這些參數(shù)也被展示到了函數(shù)宏的函數(shù)體里。
min (min (a, b), c)
首先被展開(kāi)成:
min (((a) < (b) ? (a) : (b)), (c))
然后進(jìn)一步展開(kāi)成(此處換行為方便閱讀,實(shí)際編譯后沒(méi)有):
((((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 givenmin(,,) error→ macro "min" passed 3 arguments, but takes just 2
字符化/Stringizing
如果函數(shù)宏中入?yún)⒃谧址?,是不?huì)被展開(kāi)的,它就是普通的字符串字面量,這樣的結(jié)果是符合預(yù)期的。
#define foo(x) x, "x"foo(bar) → bar, "x"
但如果確實(shí)想將入?yún)⒄归_(kāi)成字符串,可在使用入?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ì)被正確展開(kāi)。What's more, 如果這里的 x 也是宏,那只會(huì)在 if 語(yǔ)句中進(jìn)行展開(kāi)。
拼接
通過(guò) ## 可將兩個(gè)宏展開(kāi)成一個(gè),即將兩者進(jìn)行了拼接,這種操作叫 "token pasting",或 "token concatenation",就是拼接嘛。
宏拼接一般用在需要拼接的宏是來(lái)自宏參數(shù)的情況,其他情況,大可直接將兩個(gè)宏寫在一起即可,用不著 ## 指令。
考察下面這個(gè)場(chǎng)景,其中命令名重復(fù)出現(xiàn):
struct command{ char *name; void (*function) (void);};struct command commands[] ={ { "quit", quit_command }, { "help", help_command }, …};
通過(guò)定義宏配合拼接,可達(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í),可通過(guò) #undef 將取注銷掉。#undef 后緊跟宏名,后面不要跟其他東西,即使是函數(shù)類型的宏。
#define FOO 4x = FOO; → x = 4;#undef FOOx = FOO; → x = FOO;
兩個(gè)宏相似的定義
滿足以下條件時(shí),我們認(rèn)為兩者是相似的:
類型相同,比如同為對(duì)象類型,或函數(shù)類型的宏 展開(kāi)后各位置的符號(hào)(token)相同 如果是函數(shù)宏,入?yún)⑾嗤? 空白的不限但出現(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)⒉灰粯?/p>
宏重復(fù)定義時(shí)的表現(xiàn)
對(duì)于使用了 #undef 注銷過(guò)的宏,再次定義同名的宏時(shí),要求新定義的宏不與老的相似。
而如果說(shuō)一個(gè)已經(jīng)存在的宏,并沒(méi)有注銷,重復(fù)定義時(shí),如果相似,則新的定義會(huì)忽略,如果不相似,編譯器會(huì)報(bào)警告同時(shí)使用新定義的宏。這允許在多個(gè)文件中定義同一個(gè)宏。
感謝各位的閱讀,以上就是“怎么理解C/C++中的宏”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)怎么理解C/C++中的宏這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。