宏在編譯之前,需要進(jìn)行預(yù)處理,將宏直接提換成宏定義的代碼,是直接替換,也就是說,在預(yù)處理之后,你再看代碼,發(fā)現(xiàn)宏定義已經(jīng)被替換過來了,你看到是你定義之后的那一串代碼。
專業(yè)從事企業(yè)網(wǎng)站建設(shè)和網(wǎng)站設(shè)計(jì)服務(wù),包括網(wǎng)站建設(shè)、域名注冊(cè)、雅安服務(wù)器托管、企業(yè)郵箱、微信公眾號(hào)開發(fā)、微信支付寶成都微信小程序、app軟件開發(fā)公司、軟件開發(fā)、等服務(wù)。公司始終通過不懈的努力和以更高的目標(biāo)來要求自己,在不斷完善自身管理模式和提高技術(shù)研發(fā)能力的同時(shí),大力倡導(dǎo)推行新經(jīng)濟(jì)品牌戰(zhàn)略,促進(jìn)互聯(lián)網(wǎng)事業(yè)的發(fā)展。
而函數(shù),在編譯之后,有一系列調(diào)用函數(shù)的過程,比如,傳參,壓棧等,這部分是編譯器所做的。
C 語言編譯鏈接過程:
test.c(原始代碼) -- 預(yù)處理 -- test.i(經(jīng)過預(yù)處理的)-- 編譯 -- test.s(匯編代碼)-- 匯編 -- test.o(目標(biāo)文件,其實(shí)這部分已經(jīng)是單個(gè)文件的完整二進(jìn)制文件了,只是還不能執(zhí)行,如果不懂這句話,可以再問我,其實(shí)這部分知識(shí),平時(shí)也很少遇到) -- 鏈接 -- test (可執(zhí)行文件,比如一個(gè)程序由3個(gè)代碼文件共同生成,那么就會(huì)有3個(gè).o格式目標(biāo)文件,鏈接是把多個(gè)目標(biāo)文件真正的聯(lián)系在一起,比如a.o 中使用了 b.o中的一個(gè)函數(shù),那么它們兩個(gè)之間的地址是如何確定的(同理,可引申到使用函數(shù)庫的問題,使用printf函數(shù),也是需要鏈接器進(jìn)行確定printf函數(shù)地址,才能知道如何調(diào)用。)這個(gè)就是鏈接器的作用)
宏是用于編譯器處理的,他在程序編譯時(shí),會(huì)在對(duì)應(yīng)位置展開成代碼。。,這就相當(dāng)于你在告訴編譯器,我想在這個(gè)位置加一些代碼,代碼的內(nèi)容已在宏中定義,請(qǐng)編譯器自己支找。。。,也就是說程序在運(yùn)行時(shí),早已變成了對(duì)應(yīng)位置上的代碼,此時(shí)已沒有宏的概念了。。。。
而函數(shù)則是運(yùn)行時(shí),調(diào)用。他不會(huì)在編譯時(shí),在對(duì)應(yīng)位置上加上函數(shù)代碼,只是加上一個(gè)函數(shù)入口指針。。。從這個(gè)入口去運(yùn)行一段代碼。。。運(yùn)行完了之后回到當(dāng)前位置繼續(xù)執(zhí)行。。。。
可以簡單的認(rèn)為,宏是在編譯時(shí)上起作用,而函數(shù)是運(yùn)行時(shí)起作用。。。
宏是一種預(yù)處理指令,它提供了一種機(jī)制,可以用來替換源代碼中的字符串。
1、條件編譯:
C語言中,預(yù)處理過程讀入源代碼,檢查包含預(yù)處理指令的語句和宏定義,并對(duì)源代碼進(jìn)行相應(yīng)的轉(zhuǎn)換,預(yù)處理過程還會(huì)刪除程序中的注釋和多余的空白符號(hào)。
預(yù)處理指令是以#開頭的代碼行,#必須是該行除了空白字符外的第一個(gè)字符。#后是指令關(guān)鍵字,在#和指令關(guān)鍵字之間允許存在若干空白字符。
使用宏進(jìn)行條件編譯的用法與使用宏防止多重引用類似。示例如下:
使用條件編譯,方便程序員在調(diào)試程序的過程中,執(zhí)行一些在程序發(fā)布后并不需要執(zhí)行的指令。只要在需要調(diào)試的代碼前加上_DEBUG的定義,就可以在調(diào)試程序的過程中輸出調(diào)試信息。
這樣方便查看程序在運(yùn)行過程中有沒有出現(xiàn)錯(cuò)誤,定位錯(cuò)誤出現(xiàn)的地方。而在程序發(fā)布之前,取消_DEBUG的定義就可以不再執(zhí)行調(diào)試代碼。
2、宏函數(shù):
函數(shù)的調(diào)用是需要一定的時(shí)間和空間代價(jià)的。因?yàn)橄到y(tǒng)在調(diào)用函數(shù)時(shí),需要保留"現(xiàn)場(chǎng)",即將程序要執(zhí)行的指令的下一條指令的位置壓入棧,然后轉(zhuǎn)入調(diào)用函數(shù)去執(zhí)行,調(diào)用完函數(shù)后再返回主調(diào)函數(shù),恢復(fù)"現(xiàn)場(chǎng)",返回到棧里保存的的下一條指令的位置繼續(xù)執(zhí)行。
所以函數(shù)的調(diào)用需要額外的時(shí)間和空間代價(jià)。
而宏函數(shù)則不存在上述問題,宏函數(shù)在預(yù)編譯時(shí),同函數(shù)定義的代碼來替換函數(shù)名,將函數(shù)代碼段嵌入到當(dāng)前程序,不會(huì)產(chǎn)生函數(shù)調(diào)用。
所以會(huì)省去普通函數(shù)保留現(xiàn)場(chǎng)恢復(fù)現(xiàn)場(chǎng)的時(shí)間,但因?yàn)橐獙⒍x的函數(shù)體嵌入到當(dāng)前程序,所以不可避免的會(huì)占用額外的存儲(chǔ)空間。
在頻繁調(diào)用同一個(gè)宏的時(shí)候,該現(xiàn)象尤其明顯。宏函數(shù)的示例定義如下:
#define MAX(a,b) ((a)(b)?(b):(a))
宏函數(shù)的優(yōu)點(diǎn)在于避免函數(shù)調(diào)用,提高程序效率。
同時(shí)需要注意的是inline標(biāo)識(shí)符。inline也將函數(shù)定義為內(nèi)聯(lián)的。但是使用內(nèi)聯(lián)函數(shù)需要注意的是:函數(shù)體必須十分簡單,不能含有循環(huán)、條件、選擇等復(fù)雜結(jié)構(gòu),否則就不能作為內(nèi)聯(lián)函數(shù)了。
事實(shí)上,有時(shí)候即便你沒有將函數(shù)指定為內(nèi)聯(lián)函數(shù),編譯器也會(huì)將一些簡單的函數(shù)作為內(nèi)聯(lián)函數(shù)處理,而對(duì)于一些復(fù)雜的函數(shù),即使聲明為內(nèi)聯(lián)函數(shù),編譯器也不會(huì)理會(huì)的。
inline函數(shù)的瓶頸就在于此,使用inline標(biāo)識(shí)符將函數(shù)聲明為內(nèi)聯(lián)的,但這只是一種提示,到底編譯器有沒有優(yōu)化還依賴于編譯器的實(shí)現(xiàn),而使用宏函數(shù)則完全由代碼本身控制。
但在使用宏函數(shù)的時(shí)候,需要明確的是宏函數(shù)只是簡單的替換,需要注意括號(hào)的使用。
擴(kuò)展資料:
宏的更多規(guī)則特性:
(1)宏名一般用大寫。
(2)使用宏可提高程序的通用性和易讀性,減少不一致性,減少輸入錯(cuò)誤和便于修改。例如:數(shù)組大小常用宏定義。
(3)預(yù)處理是在編譯之前的處理,而編譯工作的任務(wù)之一就是語法檢查,預(yù)處理不做語法檢查。
(4)宏定義末尾不加分號(hào)。
(5)宏定義寫在函數(shù)的花括號(hào)外邊,作用域?yàn)槠浜蟮某绦颍ǔT谖募淖铋_頭。
(6)可以用#undef命令終止宏定義的作用域。
(7)宏定義不可以嵌套。
(8)字符串" "中永遠(yuǎn)不包含宏。
(9)宏定義不分配內(nèi)存,變量定義分配內(nèi)存。
(10)宏定義不存在類型問題,它的參數(shù)也是無類型的。
參考資料:
百度百科--宏定義
宏定義的基礎(chǔ)知識(shí)。引用宏定義時(shí),直接代入進(jìn)行代換。
既然已經(jīng)宏定義SUB(a) (a)-(a),而程序中出現(xiàn)的對(duì)應(yīng)a的是a+b,那么就將a換為a+b代入表達(dá)式:
d=SUB(a+b)*c=(a+b)-(a+b)*c=(2+3)-(2+3)*5
直接用(a+b)-(a+b)代換SUB(a+b)。這一點(diǎn)和數(shù)學(xué)是不同的,不要強(qiáng)行往數(shù)學(xué)上靠。
的確是不可以當(dāng)函數(shù)的參數(shù),但是可以換一個(gè)間接的思路。
將宏定義放在另一個(gè)函數(shù)里,通過取地址的方式來把你原先想去處理的參數(shù)進(jìn)行處理。
示例如下:
#includestdio.h
#define cal(m) {m = m + 10;}
int main(void)
{
int Val_0 = 20;
test(Val_0);
printf("%d", Val_0);
system("pause");
}
int test(int *n)
{
cal(*n);
printf("hello\n");
return 0;
}