1. 程式人生 > >static inline行內函數

static inline行內函數

行內函數有些類似於巨集。行內函數的程式碼會被直接嵌入在它被呼叫的地方,呼叫幾次就嵌入幾次,沒有使用call指令。這樣省去了函式呼叫時的一些額外開銷,比如儲存和恢復函式返回地址等,可以加快速度。不過呼叫次數多的話,會使可執行檔案變大,這樣會降低速度。相比起巨集來說,核心開發者一般更喜歡使用行內函數。因為行內函數沒有長度限制,格式限制。編譯器還可以檢查函式呼叫方式,以防止其被誤用。static inline的行內函數,一般情況下不會產生函式本身的程式碼,而是全部被嵌入在被呼叫的地方。如果不加static,則表示該函式有可能會被其他編譯單元所呼叫,所以一定會產生函式本身的程式碼。所以加了static,一般可令可執行檔案變小。核心裡一般見不到只用inline的情況,而都是使用static inline。 inline關鍵字inline表明要優化函式的可執行程式碼,這可以通過將函式的程式碼合併到呼叫程式的程式碼中來實現。Linux核心使用的inline函式大多被定義為static 型別。一個"static inline"函式促使編譯程式嘗試著將其程式碼插入到所有呼叫它的程式中。這一合併能夠免除函式呼叫的任何開銷,#define語句也可以排除額外的函式呼叫。另外,使用inline會增加二進位制映像的大小,而這會降低訪問CPU快取記憶體的速度,所以不能在所有的函式定義中使用它。

問:

首先,關於inline就夠煩人了,有的書上說inline關鍵字要加在定義前,宣告時可以省略,有的說宣告時加上inline函式就變成內聯型,有的說宣告和定義形式要保持一致。在一個類中宣告一個函式,函式的實現在外部,無論是僅僅在內部宣告處加inline,還是在外部實現處加inline,或是兩個地方都加,編譯均能通過,而且也無法通過除錯的辦法看出對程式到底有啥影響。搞不清到底要怎麼寫這個inline才比較好,不過可以肯定的是,inline函式的定義部分要放在標頭檔案裡,宣告和定義分開放會編譯出錯。

而且inline還可以和extern關鍵字、static關鍵字合用,在網上搜了一下,linux之父linus說過 "static inline" means "we have to have this function, if you use it, but don't inline it, then make a static version of it in this compilation unit". "extern inline" means "I actually _have_ an extern for this function, but if you want to inline it, here's the inline-version".
這話說的雲裡霧裡的,誰能解釋一下,說說你對static inline 和 extern inline用法的理解。

答:

extern inline表示該函式是已宣告過的了.由於函式本身可以宣告多次,所以extern對函式的影響僅僅把函式的隱藏屬性顯式化了.
extern 對於非函式的物件是有用的,因為物件宣告時會帶來記憶體的分配,而用 extern就表示該物件已經宣告過了,不用再分配記憶體.
static是以前C的用法.目的是讓該關鍵字標識的函式只在本地檔案可見,同一個程式的其它檔案是不可見該函式的.換句話說,就算你其它檔案裡包含了同名同參數表的函式定義的話,也是不會引起函式重複定義的錯誤的.因為static是僅在當前檔案可見.

關於inline函式,你說的大部分的都 是對的.我來給你總結一下吧.
inline函式僅僅是一個建議,對編譯器的建議,所以最後能否真正內聯,看編譯器的意思,它如果認為你的函式不復雜,能在呼叫點展開,就會真正內聯,並不是說聲明瞭內聯就會內聯,你宣告內聯只是一個建議而已.
其次,因為行內函數要在呼叫點展開,所以編譯器必須隨處可見行內函數的定義,要不然,就成了非行內函數的呼叫了.所以,這要求你的每個呼叫了行內函數的檔案都出現了該行內函數的定義,因此,將行內函數放在標頭檔案裡實現是合適的,省卻你為每個檔案實現一次的麻煩.而你所以宣告跟定義要一致,其實是指,如果你在每個檔案裡都實現一次該行內函數的話,那麼,你最好保證每個定義都是一樣的,否則,將會引起未定義的行為,即是說,如果不是每個檔案裡的定義都一樣,那麼,編譯器展開的是哪一個,那要看具體的編譯器而定.所以,最好將行內函數定義放在標頭檔案中.
而類中的成員函式預設都是內聯的,如果你在類定義時就在類內給出函式,那當然最好.如果你在類中未給出成員函式定義,而你又想內聯該函式的話,那在類外要加上inline,否則就認為不是內聯的.而且剛說了,行內函數最好放在標頭檔案內,所以最好在類定義的標頭檔案裡把類的行內函數都實現了.
而你說的將宣告與實現分開,其實是不會編譯出錯的,反正我寫那麼多程式都沒試過.將宣告與定義分開的話,這樣的後果會帶來編譯器並不隨處可見該函式定義,所以,只能在你實現定義的那個檔案裡,將該函式看成內聯(如果可以內聯的話),在其它檔案,仍看成是普通函式.
看到這裡,我想你應該明白了.那麼宣告時加inline,實現時要不要加inline呢?呵呵,留給 lz 思考吧.
extern inline表示該函式是已宣告過的了.由於函式本身可以宣告多次,所以extern對函式的影響僅僅把函式的隱藏屬性顯式化了.
extern 對於非函式的物件是有用的,因為物件宣告時會帶來記憶體的分配,而用 extern就表示該物件已經宣告過了,不用再分配記憶體.
static是以前C的用法.目的是讓該關鍵字標識的函式只在本地檔案可見,同一個程式的其它檔案是不可見該函式的.換句話說,就算你其它檔案裡包含了同名同參數表的函式定義的話,也是不會引起函式重複定義的錯誤的.因為static是僅在當前檔案可見.

關於inline函式,你說的大部分的都 是對的.我來給你總結一下吧.
inline函式僅僅是一個建議,對編譯器的建議,所以最後能否真正內聯,看編譯器的意思,它如果認為你的函式不復雜,能在呼叫點展開,就會真正內聯,並不是說聲明瞭內聯就會內聯,你宣告內聯只是一個建議而已.
其次,因為行內函數要在呼叫點展開,所以編譯器必須隨處可見行內函數的定義,要不然,就成了非行內函數的呼叫了.所以,這要求你的每個呼叫了行內函數的檔案都出現了該行內函數的定義,因此,將行內函數放在標頭檔案裡實現是合適的,省卻你為每個檔案實現一次的麻煩.而你所以宣告跟定義要一致,其實是指,如果你在每個檔案裡都實現一次該行內函數的話,那麼,你最好保證每個定義都是一樣的,否則,將會引起未定義的行為,即是說,如果不是每個檔案裡的定義都一樣,那麼,編譯器展開的是哪一個,那要看具體的編譯器而定.所以,最好將行內函數定義放在標頭檔案中.
而類中的成員函式預設都是內聯的,如果你在類定義時就在類內給出函式,那當然最好.如果你在類中未給出成員函式定義,而你又想內聯該函式的話,那在類外要加上inline,否則就認為不是內聯的.而且剛說了,行內函數最好放在標頭檔案內,所以最好在類定義的標頭檔案裡把類的行內函數都實現了.
而你說的將宣告與實現分開,其實是不會編譯出錯的,反正我寫那麼多程式都沒試過.將宣告與定義分開的話,這樣的後果會帶來編譯器並不隨處可見該函式定義,所以,只能在你實現定義的那個檔案裡,將該函式看成內聯(如果可以內聯的話),在其它檔案,仍看成是普通函式.
看到這裡,我想你應該明白了.那麼宣告時加inline,實現時要不要加inline呢?呵呵,留給 lz 思考吧.