目錄
1.#define定義標(biāo)識符
1.1賦值
1.2? ?定義關(guān)鍵字
1.3用更形象的符號來替換一種實(shí)現(xiàn)
1.4? ?加續(xù)行符換行
1.5#define定義宏
1.6? #define替換的規(guī)則
注意事項(xiàng)
2.#和##
3.帶有副作用的宏參數(shù)
4.函數(shù)和宏的對比
1.#define定義標(biāo)識符
#define定義標(biāo)識符的用法非常簡單
語法:
#define name stuff
name可以由自己來命名,盡量取一些有意義的名字
stuff是名字所對應(yīng)的內(nèi)容
舉幾個(gè)例子:
1.1賦值
1.#define MAX 1000
第一個(gè)意思就是給MAX賦值為1000
可以用代碼使用宏定義,同樣也可以定義數(shù)組的大小
#define MAX 1000
#include<stdio.h>
int main()
{
printf("%d\n", MAX);
int arr[MAX];
return 0;
}
?我們在編譯器中可以看到左邊的MAX都被替換成了1000,也就是說在以后的代碼中可以用到1000的地方,都可以使用MAX來表示。
1.2? ?定義關(guān)鍵字
#define還可以用來定義關(guān)鍵字
#define reg register ?????//為 register這個(gè)關(guān)鍵字,創(chuàng)建一個(gè)簡短的名字
我們?nèi)绻X得register太長了,使用不方便,我們還可以創(chuàng)一個(gè)簡單的名字reg
上面這個(gè)reg的意思就是register
可以看到 reg被替換成了register。
1.3用更形象的符號來替換一種實(shí)現(xiàn)
#define do_forever for(;;)
我們都知道如果for循環(huán)中什么條件都沒有那就是死循環(huán)
所以如果我們想要寫出死循環(huán)的話也可以使用這種方法
?可以看到do_forever就代替了for循環(huán)變成了死循環(huán),這也是一種用法
1.4? ?加續(xù)行符換行
如果我們在#define的宏定義的內(nèi)容過長時(shí),我們的編譯器中一行放不下,我們還可以加入續(xù)行符,也就是'\'來進(jìn)行換行
#define DEBUG_PRINT printf("file:%s\tline:%d\t \
?????????????date:%s\ttime:%s\n" ,\
?????????????__FILE__,__LINE__ , ???\
?????????????__DATE__,__TIME__ )
?那么一定要使用換行符嗎?
是的,如果我們代碼過長而需要換行時(shí),光使用回車鍵的話會報(bào)錯(cuò)(紅色的下劃線)
?希望大家可以多都嘗試。
1.5#define定義宏
我們前面所講到的#define所定義的都是一些常量和符號,而沒有參數(shù), 而接下來要說的是#define定義宏,可以像函數(shù)一樣解決問題,#define 機(jī)制包括了一個(gè)規(guī)定,允許把參數(shù)替換到文本中,
下面是宏的申明方式:
#define name( parament-list ) stuff
其中的 parament-list 是一個(gè)由逗號隔開的符號表,它們可能出現(xiàn)在stuff中。
注意:
參數(shù)列表的左括號必須與name緊鄰。
如果兩者之間有任何空白存在,參數(shù)列表就會被解釋為stuff的一部分。
下面用代簡單舉例
#define SQUARE(x) x*x
int main()
{
printf("%d\n",SQUARE(8));
return 0;
}
?可以看到我用#define 定義了一個(gè)乘方公式,其中參數(shù)是x,他的內(nèi)容是x*x,我們不妨將這段代碼預(yù)處理看一看他的本質(zhì)
?可以看到定義的SQUARE內(nèi)容變成了8*8
當(dāng)我們讓這個(gè)代碼運(yùn)行起來的時(shí)候我們也能看到它輸出了正確的結(jié)果。
但是我們還需要注意的是,使用時(shí)還需要在宏定義的內(nèi)容中帶上括號,這才能保證有些地方不會出錯(cuò)。下面繼續(xù)用代碼舉例;
#define SQUARE(x) x*x
int main()
{
printf("%d\n",SQUARE(1+7));
return 0;
}
如果我們將上面的參數(shù)8換成了1+7,結(jié)果會是怎么樣呢?
?我們會發(fā)現(xiàn)這樣會把參數(shù)的內(nèi)容原原本本的、不加計(jì)算的替換到宏的體內(nèi)去,原本的內(nèi)容被替換成了1+7*1+7,運(yùn)算順序就出了問題,所以運(yùn)算結(jié)果也出了問題
?果然不出所料結(jié)果是15
所以我們在使用宏定義時(shí)不要吝嗇我們的括號,我們只需在宏定義時(shí)給內(nèi)容加上括號,就可以避免這樣的問題
?我們給(x)*(x)的結(jié)果也帶上了括號變成了((x)*(x)),是因?yàn)槿绻覀冞€需要對這個(gè)宏進(jìn)行其他運(yùn)算的話,也不會影響這個(gè)宏的結(jié)果。
同樣的我們也可以使用宏來求兩個(gè)數(shù)較大值?
???????
同樣的,我們不要吝嗇自己的括號,要慷慨且大方,這樣才能保證自己的代碼不會出錯(cuò)?
所以用于對數(shù)值表達(dá)式進(jìn)行求值的宏定義都應(yīng)該用這種方式加上括號,避免在使用宏時(shí)由于參數(shù)中
的操作符或鄰近操作符之間不可預(yù)料的相互作用。
1.6? #define替換的規(guī)則
最后我們不妨再了解一下#define替換的規(guī)則
在程序中擴(kuò)展#define定義符號和宏時(shí),需要涉及幾個(gè)步驟。
1. 在調(diào)用宏時(shí),首先對參數(shù)進(jìn)行檢查,看看是否包含任何由#define定義的符號。如果是,它們首先被替換。
2. 替換文本隨后被插入到程序中原來文本的位置。對于宏,參數(shù)名被他們的值所替換。
3. 最后,再次對結(jié)果文件進(jìn)行掃描,看看它是否包含任何由#define定義的符號。如果是,就重復(fù)上述處理過程。
也就是說#define宏定義會不斷地檢查我們所使用的地方并且不斷地替換宏定義的內(nèi)容,直到所有內(nèi)容都被替換完為止。
注意事項(xiàng)
我們在使用#define定義后不能添加分號,很多人敲代碼時(shí)都會習(xí)慣性的在后面加上分號,當(dāng)然這種習(xí)慣不能用在宏定義中,使用時(shí)請注意細(xì)節(jié)問題。
2.#和##
這個(gè)#并不是#define中的#,而是另一種意義的操作符,把一個(gè)宏參數(shù)變成對應(yīng)的字符串。
關(guān)于它的作用我們直接上代碼演示
int main()
{
int a=10;
printf("the value of a is %d\n",a);
int b=20;
printf("the value of b is %d\n",b);
float f=3.14f;
printf("the value of a is %f\n",f);
}
再這樣的代碼中我們會發(fā)現(xiàn)出現(xiàn)了許多有規(guī)律的,繁榮的相同的代碼,那怎么樣才能使這些代碼能夠簡潔的使用呢?很多人會想到函數(shù),但是我們需要注意的是如果使用函數(shù)的話,代碼中的參數(shù)是不斷變化的,有int類型也有float類型,我們無法確定,所以不能用函數(shù)來解決這樣的問題。
再這之前我們還需要了解一個(gè)關(guān)于字符串的特點(diǎn)
可以看到第一種方式是我們平時(shí)使用的輸出方式,將輸出內(nèi)容放在打印函數(shù)的雙引號內(nèi)即可被輸出;
那第二種方式也可以用來輸出內(nèi)容,可以將使用雙引號引用我們想打印的內(nèi)容,結(jié)果和第一種是相同的,這也是字符串的一個(gè)特點(diǎn)。
我們現(xiàn)在知道了這個(gè)特征,我們就可以對上面那段冗余的代碼進(jìn)行操作了,直接上代碼
#define print_format(num,format) \
printf("the value of "#num" is "format,num)
int main()
{
int a=10;
print_format(a,"%d\n");
int b=20;
print_format(b,"%d\n");
float f=3.14f;
print_format(f,"%f\n");
return 0;
}
?首先我們先用#define定義一個(gè)宏,這個(gè)宏的名字叫做print_format;
其次我們想改變打印函數(shù)中的兩個(gè)變量,我們不妨給我們定義的宏也加入兩個(gè)參數(shù),一個(gè)參數(shù)叫num,一個(gè)參數(shù)叫format;
因?yàn)橐啻未蛴⌒稳鐃he value of num?is format的函數(shù),我們不妨給這個(gè)宏的內(nèi)容寫成如上代碼:
我們根據(jù)上面字符串的特點(diǎn)都知道了其實(shí)字符串可以分開打印,所以我們將"the value of num?is format"中要改成a,b,f的地方用num來替換,將修改數(shù)據(jù)的地方,比如%d,%f這些的地方用format這個(gè)參數(shù)來替換;
最后我們只需要將"the value of num?is format"中,給num前加上#即可,還需要再這個(gè)宏定義的內(nèi)容的最后來用參數(shù)名來聲明。
?這樣我們所得到的結(jié)果就和上面的一樣了,根本不要這么冗長的打印函數(shù)也能完成規(guī)范的操作,這樣就是關(guān)于#的簡單的用法。
接下來是##
##可以把位于它兩邊的符號合成一個(gè)符號,它允許宏定義從分離的文本片段創(chuàng)建標(biāo)識符。
那具體什么意思呢?上代碼給大家演示
int henghengaaa=114514;
#define CAT(x,y) x##y
int main()
{
printf("%d\n",CAT(hengheng,aaa));
return 0;
}
我們定義一個(gè)宏,將宏內(nèi)容使用##符號,意思就是將x和y的內(nèi)容連接在一起
ok我們輸出結(jié)果?
可以看到我們通過#define 的宏定義將字符串的內(nèi)容連接在了一起,并且成功的輸出了我們預(yù)期中的結(jié)果。
當(dāng)然我們合成的前提是我們合成的符號必須是可以用的,如果合成出來的字符串沒有出現(xiàn)過,那也不能正常使用。
3.帶有副作用的宏參數(shù)
當(dāng)宏參數(shù)在宏的定義中出現(xiàn)超過一次的時(shí)候,如果參數(shù)帶有副作用,那么你在使用這個(gè)宏的時(shí)候就可能出現(xiàn)危險(xiǎn),導(dǎo)致不可預(yù)測的后果。副作用就是表達(dá)式求值的時(shí)候出現(xiàn)的永久性效果。
具體是什么意思,我們先從“副作用”這個(gè)詞來解釋
int main()
{
//代碼1
int a = 10;
int b = a + 1;//a=10,b=11
//代碼2
int a = 10;
int b = ++a;//a=11,b=11
//代碼2就是有副作用的
return 0;
}
我們可以看到上述代碼中代碼1中的a值沒有變,而代碼2中的a值變了,我們就說代碼2是有副作用的。
那如果宏里的副作用會影響到我們什么呢?
#define MAX(X,Y) ((X)>(Y)?(X):(Y))
int main()
{
int a=3;
int b=5;
int c=MAX(a++,b++);
//int c=((a++)>(b++)?(a++):(b++));
printf("%d\n",a);
printf("%d\n",b);
printf("%d\n",c);
}
我們還是用宏定義比大小來舉個(gè)例子;
看到上面的代碼,c就是比大小中的較大值,那c的值到底是多少呢?a和b的值又是多少呢?
我們上面講過,預(yù)處理時(shí)我們會將參數(shù)原封不動(dòng)的傳入到宏里面去,所以c就成了這個(gè)樣子
c=((a++)>(b++)?(a++):(b++));
那我們依次對其進(jìn)行運(yùn)算,第一次a++結(jié)果是4,第一次b++結(jié)果是6,第二次a++結(jié)果是5,第二次b++結(jié)果是7
但是按照我們直觀的想法,籠統(tǒng)的,不加深究的眼光去看這串代碼,我們依舊會認(rèn)為是3和5去比大小,我們輸出結(jié)果
?我們就會看到與我們預(yù)期的結(jié)果有些出入。
那我們看到這樣的結(jié)果我們不妨寫一個(gè)函數(shù),來與宏定義來形成的對比
?可以看到結(jié)果和宏定義的結(jié)果就不同了,因?yàn)楹瘮?shù)傳參時(shí)是計(jì)算好結(jié)果再傳參的。
那么這又引出一個(gè)問題,函數(shù)和宏到底哪個(gè)好用呢?
4.函數(shù)和宏的對比
當(dāng)我們求兩個(gè)數(shù)的較大值時(shí)既能寫成宏的形式也能寫成函數(shù)的形式
?
?我們可以對比一下以上兩種方式求較大值
其實(shí)對于當(dāng)前兩個(gè)數(shù)比大小的代碼還是宏定義是更優(yōu)解,原因有二:
1. 用于調(diào)用函數(shù)和從函數(shù)返回的代碼可能比實(shí)際執(zhí)行這個(gè)小型計(jì)算工作所需要的時(shí)間更多。
所以宏比函數(shù)在程序的規(guī)模和速度方面更勝一籌。
當(dāng)我們用vs調(diào)試代碼時(shí),我們觀察反匯編代碼時(shí)發(fā)現(xiàn)在進(jìn)入了函數(shù)時(shí)還有許許多多的指令,在經(jīng)過這些指令時(shí)才能達(dá)到下方比大小的結(jié)果,在下方比大小時(shí)還需要很多的指令,執(zhí)行這些指令都是需要花費(fèi)時(shí)間的,所以函數(shù)的方式比較浪費(fèi)時(shí)間。
因?yàn)樵谡{(diào)用函數(shù)時(shí)有三個(gè)步驟會影響時(shí)間:
①函數(shù)調(diào)用前的準(zhǔn)備(傳參、函數(shù)棧幀空間的維護(hù))
②主要的運(yùn)算
③函數(shù)返回和返回值的處理,和最后函數(shù)棧幀的銷毀
但是我們反觀宏在處理這樣的代碼時(shí)
我們就會看到在傳入?yún)?shù)時(shí)直接就執(zhí)行的是打印函數(shù),因?yàn)樯厦嬷v過,宏可以直接在我們所需要時(shí)將代碼替換掉,沒有函數(shù)那么多的開銷。
2. 更為重要的是函數(shù)的參數(shù)必須聲明為特定的類型。
所以函數(shù)只能在類型合適的表達(dá)式上使用。反之這個(gè)宏怎可以適用于整形、長整型、浮點(diǎn)型等可以
用于>來比較的類型。
宏是類型無關(guān)的,宏是更加靈活的,這也是重要的原因。
當(dāng)然宏也有自己劣勢的地方
①每次使用宏的時(shí)候,一份宏定義的代碼將插入到程序中。除非宏比較短,否則可能大幅度增加程序的長度。也就是說如果我們在宏定義的內(nèi)容中定義的內(nèi)容有十幾二十幾行的代碼時(shí),我們每用一次宏就會增加我們代碼的長度。
②宏是沒有辦法調(diào)試的。在預(yù)處理階段時(shí),宏就會將內(nèi)容全部替換到代碼中所運(yùn)用宏的地方,當(dāng)我們在調(diào)試時(shí)可能會和我們所看到的代碼有所不同。
③宏由于類型無關(guān),也就不夠嚴(yán)謹(jǐn)。
④宏可能會帶來運(yùn)算符優(yōu)先級的問題,導(dǎo)致程容易出現(xiàn)錯(cuò)。上面我們說過,如果在一個(gè)有宏的表達(dá)式中,表達(dá)式對宏的運(yùn)算結(jié)果還有運(yùn)算時(shí),可能就會出現(xiàn)優(yōu)先級的問題從而導(dǎo)致宏的運(yùn)算結(jié)果錯(cuò)誤。
?
宏對比函數(shù)的還有一個(gè)優(yōu)點(diǎn)就是可以將類型當(dāng)成參數(shù)。
我們在使用函數(shù)時(shí)從來都沒見過用函數(shù)的參數(shù)類型給函數(shù)傳參。
用代碼舉例:
?
我們在動(dòng)態(tài)內(nèi)存管理中學(xué)過,需要開辟10個(gè)整形類型的空間需要如上代碼進(jìn)行處理,并且對其進(jìn)行判斷。
但是我想讓他的寫法簡單一點(diǎn)行不行呢?
就比如寫成以下形式
MALLOC(10,int)
?這樣的代碼要開辟十個(gè)類型的整形的空間要簡單得多。? ? ?那怎么使用呢?
#define MALLOC(num,type) (type*)malloc(num *sizeof(type))
int main()
{
int* p2 = MALLOC(10, int);
if (p2 == NULL)
{
//...
}
return 0;
}
?這里用宏定義來解決這樣的問題
我們定義一個(gè)名為MALLOC的宏,并且再定義兩個(gè)參數(shù),其中一個(gè)既然要以類型名,我們不妨就令這個(gè)參數(shù)為type;在后面參數(shù)的內(nèi)容中就用malloc函數(shù)開辟個(gè)數(shù)*一個(gè)類型的大小,最后再用(type*)強(qiáng)制將其類型轉(zhuǎn)化為type類型,這樣就完成了MALLOC的宏定義,使用時(shí)簡單方便,一勞永逸。
相信通過這些可以看出函數(shù)和#define 宏定義的區(qū)別,使用時(shí)可以根據(jù)不同的環(huán)境使用。文章來源:http://www.zghlxwxcb.cn/news/detail-614493.html
以上就是本篇文章的所有內(nèi)容,如果對你有所幫助,還請三聯(lián)支持,感謝您的閱讀。文章來源地址http://www.zghlxwxcb.cn/news/detail-614493.html
到了這里,關(guān)于【C語言】預(yù)處理詳解:#define的各種使用方法的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!