內聯(lián)函數定義
inline關鍵字是C99標準的型關鍵字,其作用是將函數展開,把函數的代碼復制到每一個調用處。這樣調用函數的過程就可以直接執(zhí)行函數代碼,而不發(fā)生跳轉、壓棧等一般性函數操作??梢怨?jié)省時間,也會提高程序的執(zhí)行速度。
為什么需要內聯(lián)函數
在C語言中,如果一些函數被頻繁的調用,不斷地用函數入棧,即函數棧,則會造成棧空間或者棧內存的大量消耗,為了解決這個問題,特別的引入了inline關鍵字,表示為內聯(lián)函數。
棧空間指的是函數內數據的內存空間,在一個系統(tǒng)下,棧空間的資源是有限的,假如頻繁大量的使用就會因??臻g的不足而導致出錯,函數的死循壞遞歸調用的最終結果就是導致棧內存空間的枯竭。
#include?<stdio.h>
//函數定義為inline即:內聯(lián)函數
inline?char*?dbtest(int?a)?{
????return?(i?%?2?>?0)???"奇"?:?"偶";
}?
?
int?main()
{
???int?i?=?0;
???for?(i=1;?i?<?100;?i++)?{
???????printf("i:%d????奇偶性:%s?/n",?i,?dbtest(i));????
???}
}
上面的例子就是標準的內聯(lián)函數的用法,使用inline修飾帶來的好處我們表面看不出來,其實,在內部的工作就是在每個for循環(huán)的內部任何調用dbtest(i)的地方都換成了(i % 2 > 0) ? "奇" : "偶"
,這樣就避免了頻繁調用函數對棧內存重復開辟所帶來的消耗。
內聯(lián)函數注意事項
- 關鍵字inline必須與函數的定義體放在一起,才能使函數成為內聯(lián)函數,僅僅將inline放在函數聲明前面不起作用
如下風格的函數fun則成為內聯(lián)函數:
void?fun(int?x,?int?y);
inline?void?fun(int?x,?int?y)??//inline與函數的定義放在一起
{
}
- 關鍵字inline的使用是有所限制的
inline只適合函數體內代碼比較簡單的函數使用,不能包含復雜的結構控制語句,例如while、switch,并且內聯(lián)函數本身不能是直接遞歸函數(函數內部調用自己的函數)。
- inline僅是一個對編譯器的建議
inline函數僅僅是一個對編譯器的建議,所以最后能否真正內聯(lián),看編譯器的意思,它如果認為函數不復雜,能在調用點展開,就會真正內聯(lián),并不是說聲明了內聯(lián)就會內聯(lián),聲明內聯(lián)只是一個建議而已。
- 建議:inline函數的定義放在頭文件中
其次,因為內聯(lián)函數要在調用點展開,所以編譯器必須隨處可見內聯(lián)函數的定義,要不然就成了非內聯(lián)函數的調用了。所以,這要求每個調用了內聯(lián)函數的文件都出現了該內聯(lián)函數的定義。
因此,將內聯(lián)函數的定義放在頭文件里實現是合適的,省卻你為每個文件實現一次的麻煩。
聲明跟定義要一致:如果在每個文件里都實現一次該內聯(lián)函數的話,那么,最好保證每個定義都是一樣的,否則,將會引起未定義的行為。如果不是每個文件里的定義都一樣,那么,編譯器展開的是哪一個,那要看具體的編譯器而定。所以,最好將內聯(lián)函數定義放在頭文件中。
- static和inline聯(lián)合使用
static是靜態(tài)修飾符,由其關鍵字修飾的變量會保存到全局數據區(qū),對于普通的局部變量或者全局變量,都是由系統(tǒng)自動分配內存的,并且當變量離開作用域的時候釋放掉,而使用static關鍵字來修飾,只有當程序結束時候才會釋放掉,使用static inline修飾時,函數僅在文件內部可見,不會污染命名空間,另外,函數在運行過程中也會分配內存空間,但是由于static的存在,就和修飾變量類似,它只會開辟一塊內存空間。
內聯(lián)函數優(yōu)缺點
普通函數在調用過程中,會對寄存器中內容進行上下文切換(push和pop操作),而內聯(lián)函數則不需要,所以普通函數相比內聯(lián)函數,耗時要多一些。
當函數使用次數比較多的時候,內聯(lián)函數在每個調用的地方都會被展開,所以導致固件大小會變大,同一段代碼會多次重復出現在固件中。而普通函數則沒有此問題,不管調用的函數的次數多少,函數在固件中均只占用一處,空間利用率較高。inline函數其實就是空間換時間
inline 和宏的區(qū)別
雖然inline函數和帶參數的宏很像,但是在使用方法上和宏還是有很大區(qū)別的:
inline()函數 | 帶參數的宏 | |
---|---|---|
展開的時機 | 在編譯的時候展開,因此inline關鍵字是一個編譯關鍵字 | 在預處理時展開,因此#define關鍵字是一個預處理關鍵字 |
參數類型檢查 | inline()函數是一中函數,會進行嚴格的參數類型檢查 | 不會檢查參數類型,只是做簡單的字符串替換,因此在使用帶參數的宏時會有一些副作用,編寫程序是要人為預防 |
是否允許有復雜語句 | 不允許出現復雜語句,如果出現復雜語句,該函數將不會展開,例如遞歸,大型循環(huán)等 | 對此不做要求。宏只是做字符串替換操作,而不了解語句的含義 |
是否一定被展開 | 不一定,是否展開由編譯器決定 | 一定,只要使用了宏就可以保證被展開 |
接口封裝 | 是 | 否 |
是否支持調試 | 是 | 否 |
總結
內聯(lián)函數相比宏函數,會進行語法檢查。宏函數是在預處理階段生效,內聯(lián)函數是在編譯階段進行語法檢查然后替換。
內聯(lián)函數相比普通函數,少了上下文切換的步驟所以執(zhí)行會更快一些。
內聯(lián)函數被多次調用,會使固件大小膨脹,內聯(lián)函數的高速是以空間來換時間。
內聯(lián)函數不可遞歸。
如果函數內容太過于復雜,編譯器會忽略inline關鍵字,把他當成普通函數來處理。
本文參考
https://zhuanlan.zhihu.com/p/448262183
https://zhuanlan.zhihu.com/p/50812510
https://cloud.tencent.com/developer/article/2224955