內聯函數:static inline 和 extern inline 的含義

引入內聯函數的目的是為了解決程序中函數調用的效率問題。

函數是一種更高級的抽象。它的引入使得編程者只關心函數的功能和使用方法,而不必關心函數功能的具體實現;函數的引入可以減少程序的目標代碼,實現程序代碼和數據的共享。但是,函數調用也會帶來降低效率的問題,因為調用函數實際上將程序執行順序轉移到函數所存放在內存中某個地址,將函數的程序內容執行完后,再返回到轉去執行該函數前的地方。這種轉移操作要求在轉去前要保護現場并記憶執行的地址,轉回后先要恢復現場,并按原來保存地址繼續執行。因此,函數調用要有一定的時間和空間方面的開銷,于是將影響其效率。特別是對于一些函數體代碼不是很大,但又頻繁地被調用的函數來講,解決其效率問題更為重要。引入內聯函數實際上就是為了解決這一問題。

在程序編譯時,編譯器將程序中出現的內聯函數的調用表達式用內聯函數的函數體來進行替換。顯然,這種做法不會產生轉去轉回的問題,但是由于在編譯時將函數休中的代碼被替代到程序中,因此會增加目標程序代碼量,進而增加空間開銷,而在時間代銷上不象函數調用時那么大,可見它是以目標代碼的增加為代價來換取時間的節省。
1.內聯函數可減少cpu的系統開銷,并且程序的整體速度將加快,但當內聯函數很大時,會有相反的作用,因此一般比較小的函數才使用內聯函數.
2.有兩種內聯函數的聲明方法,一種是在函數前使用inline關見字,另一種是在類的內部定義函數的代碼,這樣的函數將自動轉換為內聯函數,而且沒必要將inline放在函數前面.
3.內聯是一種對編譯器的請求,下面這些情況會阻止編譯器服從這項請求.
如果函數中包含有循環,switch或goto語句,遞歸函數,含有static的函數.

由此可以看出,內聯函數和成員函數沒什么區別,區別就在于怎樣加快函數的執行速度而已。
內聯函數是浪費空間來節省時間的設置,因為函數的調用是很浪費時間的,寫成內聯函數可以在每次調用時用函數體內容代替函數調用,有點類似一個宏定義。當函數體語句較少,且沒有復雜的循環語句,且調用次數較多時,就可以用內聯函數。

問:

首先,關于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,否則就認為不是內聯的.而且剛說了,內聯函數最好放在頭文件內,所以最好在類定義的頭文件里把類的內聯函數都實現了.
而你說的將聲明與實現分開,其實是不會編譯出錯的,反正我寫那么多程序都沒試過.將聲明與定義分開的話,這樣的后果會帶來編譯器并不隨處可見該函數定義,所以,只能在你實現定義的那個文件里,將該函數看成內聯(如果可以內聯的話),在其它文件,仍看成是普通函數.

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 1 原理 1.1 首先,關于聲明和定義的區別。 這種寫法(函數原型后加;號表示結束的寫法)只能叫函數聲明而不能叫函...
    Pitfalls閱讀 6,586評論 2 12
  • 1.面向對象的程序設計思想是什么? 答:把數據結構和對數據結構進行操作的方法封裝形成一個個的對象。 2.什么是類?...
    少帥yangjie閱讀 5,045評論 0 14
  • 代碼(OC): -(NSUInteger)retainCount{ returnNSExtraRefCount(s...
    nalis風閱讀 1,277評論 0 1
  • 概述:聲明是將一個名稱引入一個程序.定義提供了一個實體在程序中的唯一描述.聲明在單個作用域內可以重復多次(類成員除...
    抓兔子的貓閱讀 644評論 0 3
  • 一、溫故而知新 1. 內存不夠怎么辦 內存簡單分配策略的問題地址空間不隔離內存使用效率低程序運行的地址不確定 關于...
    SeanCST閱讀 7,880評論 0 27