中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么理解C/C++中的宏

發布時間:2021-11-20 14:43:38 來源:億速云 閱讀:184 作者:iii 欄目:編程語言

這篇文章主要講解了“怎么理解C/C++中的宏”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“怎么理解C/C++中的宏”吧!

宏的定義

通過 #define 指令定義一個宏。

#define NAME_OF_MACRO value

比如,以下代碼定義了一個名為 BUFFER_SIZE 的宏,指代 1024 這個數字。

#define BUFFER_SIZE 1024

使用時,

foo = (char *) malloc (BUFFER_SIZE);

使用預處理器編譯:

$ gcc -E test.c

編譯結果:

foo = (char *) malloc (1024);

多行

宏的定義是跟隨 #define 在一同一行內的,但可通過 反斜杠 \ 實現換行從而定義出多行的宏。

多行的宏經過編譯后會還原到一行中。

test.c

#include <stdio.h>#define GREETING_STR \ "hello \world"int main() { printf(GREETING_STR); }

編譯后:

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

宏展開時的順序

宏的展開是在處理源碼時按照其出現位置進行的,如果宏定義有嵌套關系,也是層層進行展開,比如:

#include <stdio.h>#define GREETING_NAME "wayou"#define GREETING "hello," GREETING_NAMEint main() { printf(GREETING); return 0;}

首先遇到 GREETING,將其展開成 GREETING_NAME "wayou",然后發現另一個宏 GREETING_NAME,將其展開最后得到 "hello," "wayou"。所以編譯后的代碼為:

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

其展開的順序并不是宏定義時的順序,為了驗證,可將上面示例代碼中兩個宏的定義調換一下,得到:

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

再次編譯查看產出,會發現沒有區別,也不會報 GREETING 中所依賴的 GREETING_NAME 找不到的錯。其實 #define 只是告訴編譯器定義了這么個宏,而具體的求值,則是使用宏的地方才開始的。

像下面這樣,當宏存在覆蓋時,會以新的為準,其結果為 37。

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

Object-like 宏

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

Function-like 宏

也可定義出使用時像是方法調用一樣的宏,這便是 Function-like 類型的宏。

#define lang_init() c_init()lang_init()// 編譯后c_init()

函數類型的宏只在以方法調用形式使用時才會被展開,即名稱后加括號,否則會被忽略。當宏名和函數名重名時,這一策略就會顯得有用了,比如:

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

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

函數類型的宏在定義時需注意,宏名與后面括號不能有空格,否則就是普通的 Object-like 類型對象。

#define lang_init ()  c_init()lang_init()// 編譯后:() c_init()()

宏的參數

函數類型的宏,可以像正常函數一樣指定入參,入參需為逗號分隔合法的 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));

入參中的括號

入參中只需要括號對稱,但不要求方括號或花括號成對出現,所以下面的代碼:

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

其入參實際為 array[x = y 和 x + 1]。

入參的展開

入參本質上也是宏,對象類型的宏,在函數宏展示時,這些參數也被展示到了函數宏的函數體里。

min (min (a, b), c)

首先被展開成:

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

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

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

參數的缺省

函數宏在使用時其入參可缺省,但不能全部缺省,至少提供一個入參。

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

如果函數宏中入參在字符串中,是不會被展開的,它就是普通的字符串字面量,這樣的結果是符合預期的。

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

但如果確實想將入參展開成字符串,可在使用入參時,加上 # 前綴。

#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 在字符串中會被正確展開。What's more, 如果這里的 x 也是宏,那只會在 if 語句中進行展開。

拼接

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

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

考察下面這個場景,其中命令名重復出現:

struct command{ char *name; void (*function) (void);};struct command commands[] ={ { "quit", quit_command }, { "help", help_command }, …};

通過定義宏配合拼接,可達到精簡代碼的目的:

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

不定參數

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

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

調用時,命名參數后面,包括逗號都會進入到 __VA_ARGS__ 關鍵字當中。但 C++ 中還支持對這些參數命名從而不用 __VA_ARGS__。

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

C++ 中可這么寫:

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

不定參數與命名參數混合的情況

不定參數為命名參數后面省略的部分。

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

預設的宏

標準庫及編譯器中預設了一些有用的宏,可以在這里 查閱。

取消和重置宏

當某個宏不再使用時,可通過 #undef 將取注銷掉。#undef 后緊跟宏名,后面不要跟其他東西,即使是函數類型的宏。

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

兩個宏相似的定義

滿足以下條件時,我們認為兩者是相似的:

類型相同,比如同為對象類型,或函數類型的宏  展開后各位置的符號(token)相同  如果是函數宏,入參相同  空白的不限但出現的位置相同

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

#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) // 宏的內容不一樣#define FOUR(score,and,seven,years,ago) (2 + 2) // 入參不一樣

宏重復定義時的表現

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

而如果說一個已經存在的宏,并沒有注銷,重復定義時,如果相似,則新的定義會忽略,如果不相似,編譯器會報警告同時使用新定義的宏。這允許在多個文件中定義同一個宏。

感謝各位的閱讀,以上就是“怎么理解C/C++中的宏”的內容了,經過本文的學習后,相信大家對怎么理解C/C++中的宏這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

c++
AI

华容县| 乐亭县| 工布江达县| 长寿区| 射洪县| 南部县| 休宁县| 出国| 民乐县| 云安县| 蓝山县| 连云港市| 泰宁县| 塔河县| 甘德县| 桐城市| 丹巴县| 丘北县| 海晏县| 克什克腾旗| 扶绥县| 玛沁县| 和田市| 通辽市| 镶黄旗| 汉沽区| 松桃| 金沙县| 榕江县| 长寿区| 新余市| 南和县| 沿河| 东兴市| 睢宁县| 合肥市| 吴桥县| 凤凰县| 丰台区| 沈丘县| 静宁县|