C++ inline內(nèi)聯(lián)函數(shù)詳解

函數(shù)是一個(gè)可以重復(fù)使用的代碼塊,CPU 會(huì)一條一條地挨著執(zhí)行其中的代碼。CPU 在執(zhí)行主調(diào)函數(shù)代碼時(shí)如果遇到了被調(diào)函數(shù),主調(diào)函數(shù)就會(huì)暫停,CPU 轉(zhuǎn)而執(zhí)行被調(diào)函數(shù)的代碼;被調(diào)函數(shù)執(zhí)行完畢后再返回到主調(diào)函數(shù),主調(diào)函數(shù)根據(jù)剛才的狀態(tài)繼續(xù)往下執(zhí)行。
一個(gè) C/C++程序的執(zhí)行過(guò)程可以認(rèn)為是多個(gè)函數(shù)之間的相互調(diào)用過(guò)程,它們形成了一個(gè)或簡(jiǎn)單或復(fù)雜的調(diào)用鏈條,這個(gè)鏈條的起點(diǎn)是 main(),終點(diǎn)也是 main()。當(dāng) main() 調(diào)用完了所有的函數(shù),它會(huì)返回一個(gè)值(例如return 0;
)來(lái)結(jié)束自己的生命,從而結(jié)束整個(gè)程序。
函數(shù)調(diào)用是有時(shí)間和空間開(kāi)銷(xiāo)的。程序在執(zhí)行一個(gè)函數(shù)之前需要做一些準(zhǔn)備工作,要將實(shí)參、局部變量、返回地址以及若干寄存器都?jí)喝霔V?,然后才能?zhí)行函數(shù)體中的代碼;函數(shù)體中的代碼執(zhí)行完畢后還要清理現(xiàn)場(chǎng),將之前壓入棧中的數(shù)據(jù)都出棧,才能接著執(zhí)行函數(shù)調(diào)用位置以后的代碼。關(guān)于函數(shù)調(diào)用的細(xì)節(jié),我們已經(jīng)在《C語(yǔ)言?xún)?nèi)存精講》一章中的《一個(gè)函數(shù)在棧上到底是怎樣的》《用一個(gè)實(shí)例來(lái)深入剖析函數(shù)進(jìn)棧出棧的過(guò)程》兩節(jié)中講到。
如果函數(shù)體代碼比較多,需要較長(zhǎng)的執(zhí)行時(shí)間,那么函數(shù)調(diào)用機(jī)制占用的時(shí)間可以忽略;如果函數(shù)只有一兩條語(yǔ)句,那么大部分的時(shí)間都會(huì)花費(fèi)在函數(shù)調(diào)用機(jī)制上,這種時(shí)間開(kāi)銷(xiāo)就就不容忽視。
為了消除函數(shù)調(diào)用的時(shí)空開(kāi)銷(xiāo),C++ 提供一種提高效率的方法,即在編譯時(shí)將函數(shù)調(diào)用處用函數(shù)體替換,類(lèi)似于C語(yǔ)言中的宏展開(kāi)。這種在函數(shù)調(diào)用處直接嵌入函數(shù)體的函數(shù)稱(chēng)為內(nèi)聯(lián)函數(shù)(Inline Function),又稱(chēng)內(nèi)嵌函數(shù)或者內(nèi)置函數(shù)。
指定內(nèi)聯(lián)函數(shù)的方法很簡(jiǎn)單,只需要在函數(shù)定義處增加inline關(guān)鍵字。請(qǐng)看下面的例子:
運(yùn)行結(jié)果: 45 99↙ 45, 99 99, 45
注意,要在函數(shù)定義處添加 inline 關(guān)鍵字,在函數(shù)聲明處添加 inline 關(guān)鍵字雖然沒(méi)有錯(cuò),但這種做法是無(wú)效的,編譯器會(huì)忽略函數(shù)聲明處的 inline 關(guān)鍵字。
當(dāng)編譯器遇到函數(shù)調(diào)用swap(&m, &n)時(shí),會(huì)用 swap() 函數(shù)的代碼替換swap(&m, &n),同時(shí)用實(shí)參代替形參。這樣,程序第 16 行就被置換成:
int temp;
temp = *(&m);
*(&m) = *(&n);
*(&n) = temp;
編譯器可能會(huì)將 *(&m)、*(&n) 分別優(yōu)化為 m、n。
當(dāng)函數(shù)比較復(fù)雜時(shí),函數(shù)調(diào)用的時(shí)空開(kāi)銷(xiāo)可以忽略,大部分的 CPU 時(shí)間都會(huì)花費(fèi)在執(zhí)行函數(shù)體代碼上,所以我們一般是將非常短小的函數(shù)聲明為內(nèi)聯(lián)函數(shù)。
由于內(nèi)聯(lián)函數(shù)比較短小,我們通常的做法是省略函數(shù)原型,將整個(gè)函數(shù)定義(包括函數(shù)頭和函數(shù)體)放在本應(yīng)該提供函數(shù)原型的地方。下面的例子是一個(gè)反面教材,這樣的寫(xiě)法是不被推薦的:
使用內(nèi)聯(lián)函數(shù)的缺點(diǎn)也是非常明顯的,編譯后的程序會(huì)存在多份相同的函數(shù)拷貝,如果被聲明為內(nèi)聯(lián)函數(shù)的函數(shù)體非常大,那么編譯后的程序體積也將會(huì)變得很大,所以再次強(qiáng)調(diào),一般只將那些短小的、頻繁調(diào)用的函數(shù)聲明為內(nèi)聯(lián)函數(shù)。
內(nèi)聯(lián)函數(shù)看起來(lái)簡(jiǎn)單,但是有很多細(xì)節(jié)要引起重視,我們將在后續(xù)兩節(jié)《C++內(nèi)聯(lián)函數(shù)也可以用來(lái)代替宏》《如何規(guī)范地使用C++內(nèi)聯(lián)函數(shù)》對(duì)內(nèi)聯(lián)函數(shù)的本質(zhì)以及使用規(guī)范進(jìn)行深入的講解。
最后需要說(shuō)明的是,對(duì)函數(shù)作 inline 聲明只是程序員對(duì)編譯器提出的一個(gè)建議,而不是強(qiáng)制性的,并非一經(jīng)指定為 inline 編譯器就必須這樣做。編譯器有自己的判斷能力,它會(huì)根據(jù)具體情況決定是否這樣做。