大牛深入浅出讲解C语言#define宏定义应用及使用方法
在C語(yǔ)言中,我們使用#define來(lái)定義宏。在C程序編譯的預(yù)處理階段,預(yù)處理器會(huì)把宏定義的符號(hào)替換成指定的文本。
不帶參數(shù)的宏
關(guān)于宏最常見的就是用來(lái)定義數(shù)值常量的名稱,即沒(méi)有參數(shù)的宏定義,采用如下形式:
例如:
1#define?ARRAY_SIZE????10 2int?data[ARRAY_SIZE];當(dāng)程序需要修改數(shù)組長(zhǎng)度時(shí),只需要修改宏定義即可,無(wú)需對(duì)程序中每一處用到數(shù)組長(zhǎng)度的地方進(jìn)行修改。
帶參數(shù)的宏
你可以定義具有形參的宏,然后預(yù)處理器展開這類宏時(shí),會(huì)將宏指定的實(shí)參替換文本中對(duì)應(yīng)的形參。這有點(diǎn)像函數(shù),故也叫做函數(shù)式宏定義、類函數(shù)宏,形式如下:
1#define?宏名稱([行參列表])?替換文本 2#define?宏名稱([行參列表,]...)????替換文本當(dāng)宏被調(diào)用時(shí),替換文本中的每個(gè)值都與形參列表相對(duì)應(yīng)。另外C99標(biāo)準(zhǔn)允許定義有參略號(hào)的宏,省略號(hào)必須放在參數(shù)列表的后面,以表示可選參數(shù)。
當(dāng)調(diào)用有可選參數(shù)的宏時(shí),預(yù)處理器會(huì)將所有可選參數(shù)連同分隔它們的逗號(hào)打包在一起作為一個(gè)參數(shù)。在替換文本中,標(biāo)識(shí)符?__VA_ARGS__對(duì)應(yīng)前面打包的可選參數(shù)。
1//假設(shè)有個(gè)已經(jīng)打開的日志文件,準(zhǔn)備采用文件指針fp_log對(duì)其進(jìn)行寫入 2#define?printLog(...)?fprintf(fp_log,?__VA_ARGS__) 3//使用printLog 4printLog("%s:?intVar=%d\r\n",?__func__,?intVar);預(yù)處理器把最后一行的宏調(diào)用替換成下面一行代碼:
1fprintf(fp_log,?"%s:?intVar=%d\r\n",?__func__,?intVar);帶參宏的一些問(wèn)題
1#include?<stdio.h> 2#define?SQUARE(x)???x?*?x 3int?main(int?argc,?char?const?*argv[]) 4{ 5????int?a?=?5; 6????printf("%d\r\n",?SQUARE(5)); 7????printf("%d\r\n",?SQUARE(a+1)); 8????return?0; 9}程序運(yùn)行,第一條打印顯而易見為25,第二條打印為多少呢?不是36而是11。我們通過(guò)"gcc -E a.c >a.txt",將預(yù)處理后的文件重定向到a.txt,來(lái)觀察被替換的宏文本。
1printf("%d\r\n",?5?*?5); 2printf("%d\r\n",?a+1?*?a+1);可見替換產(chǎn)生的表達(dá)式并沒(méi)有按照預(yù)想的次序進(jìn)行求值。可能大家會(huì)說(shuō),加上兩個(gè)括號(hào)就解決了嘛:
1#define?SQUARE(x)??(x)?*?(x)那么,為每個(gè)出現(xiàn)在替換文本中的參數(shù)加上括號(hào)就一定沒(méi)問(wèn)題了嗎?看下面例子:
1#include?<stdio.h> 2#define?ADD(x)????(x)?+?(x) 3int?main(int?argc,?char?const?*argv[]) 4{ 5????int?a?=?5; 6????printf("%d\r\n",?10?*?ADD(5)); 7????return?0; 8}看似輸出100,實(shí)際輸出55。我們看下替換后的文本:
1printf("%d\r\n",?10?*?(5)?+?(5));乘法運(yùn)算在加法運(yùn)算之前執(zhí)行,所以結(jié)果為55。這個(gè)錯(cuò)誤很容易修正:整個(gè)表達(dá)式加上括號(hào):
1#define?ADD(x)????((x)?+?(x))所有對(duì)數(shù)值表達(dá)式進(jìn)行求值的宏定義,為避免參數(shù)中操作符或鄰近的操作符之間不可預(yù)料的互相作用,應(yīng)對(duì)每個(gè)參數(shù)加括號(hào),整個(gè)表達(dá)式也要加括號(hào)。
宏與函數(shù)
盡可能多的加括號(hào)就絕對(duì)不會(huì)有問(wèn)題了嗎?看下面例子:
1#include?<stdio.h>2#define?MAX(a,?b)????((a)?>?(b)???(a)?:?(b))3int?main(int?argc,?char?const?*argv[])4{5????int?a?=?5;6????int?b?=?5;7????int?m?=?MAX(++a,?b);8????printf("%d\r\n",?m);9????return?0; 10}m的值是多少?是不是相當(dāng)于執(zhí)行MAX(6, 5),然后輸出6呢?答案是7,看下替換的文本:
1int?m?=?((++a)?>?(b)???(++a)?:?(b));這里就是C語(yǔ)言中函數(shù)式宏定義的陷阱,傳遞參數(shù)++a會(huì)被展開到替換文本的每一個(gè)a處。所以,再多的括號(hào)也不可能確保萬(wàn)無(wú)一失。
那么,函數(shù)式宏定義較函數(shù)有什么優(yōu)點(diǎn)嗎?
相較于函數(shù)的分配和釋放棧、傳參、傳返回值等一系列操作,函數(shù)式宏定義的代碼執(zhí)行效率高。
函數(shù)的參數(shù)必須聲明為一種特定的類型,而宏是與類型無(wú)關(guān)的。以比較大小為例,如果我們需要比較整形、長(zhǎng)整型、浮點(diǎn)型數(shù)值大小,由于C語(yǔ)言不支持函數(shù)重載,我們需要為每一種數(shù)據(jù)類型實(shí)現(xiàn)一個(gè)max()函數(shù),顯然使用宏定義要來(lái)的方便了。當(dāng)然宏定義不檢測(cè)參數(shù)類型也是把雙刃劍,可能導(dǎo)致程序不安全,需要特別注意。
還有一些函數(shù)無(wú)法實(shí)現(xiàn)的任務(wù),比如說(shuō),如何將數(shù)據(jù)類型作為參數(shù)傳遞給函數(shù)?看宏定義怎么破:
替換后的文本:
1p?=?((int?*)malloc((20)?*?sizeof(int)));你點(diǎn)的每個(gè)贊,我都當(dāng)成喜歡
《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的大牛深入浅出讲解C语言#define宏定义应用及使用方法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 【PC工具】复制翻译神器!有了这个开源免
- 下一篇: 【PC工具】github项目辅助下载工具