1. 程式人生 > >行內函數:static inline 和 extern inline 的含義

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

定義

  行內函數從原始碼層看,有函式的結構,而在編譯後,卻不具備函式的性質。編譯時,類似巨集替換,使用函式體替換呼叫處的函式名。一般在程式碼中用inline修飾,但是否能形成行內函數,需要看編譯器對該函式定義的具體處理。

動機

  內聯擴充套件是用來消除函式呼叫時的時間開銷。它通常用於頻繁執行的函式。 一個小記憶體空間的函式非常受益。   如果沒有行內函數,編譯器可以決定哪些函式內聯 。 程式設計師很少或沒有控制哪些職能是內聯的,哪些不是。 給這種控制程度,作用是程式設計師可以選擇內聯的特定應用 。

函式內聯問題

  除了 ​​相關的問題, 內聯擴充套件一般,語言功能作為一個行內函數可能不被視為有價值的,因為它們出現的原因,對於一個數字:   通常,一個編譯器是在一個比人類更有利的地位來決定某一特定功能是否應該被內聯。 有時,編譯器可能無法儘可能多的功能內嵌作為程式設計師表示。   一個重要的一點需要注意的是程式碼(行內函數)得到暴露其客戶端(呼叫函式)。   隨著功能的演變,它們有可能成為合適的內聯,他們不前,或不再在他們面前的內聯合適。 而內聯或取消行內函數比從巨集轉換為更容易,但仍需要額外的維修,一般產量相對較少的利益。   用於本機C型編譯系統的擴散可以增加編譯時間,因為他們的身體的中間表示是到每個呼叫點,他們都是內聯複製行內函數。在程式碼大小可能增加是由在編譯時間可能增加映象。   C99中內嵌的規範要求只有一個額外在另一個編譯單元,功能的外部定義時,相應的內聯定義,可以發生在不同的編譯單元多次,如果該函式用於地方。這很容易導致聯結器,因為這樣的定義不是由程式設計師提供的錯誤。 出於這個原因,往往是在C99內聯一起使用靜態的,也給出了函式的內部聯絡。   在C + +,有必要定義一個在每一個模組(編譯單元)行內函數使用一個普通的功能,而必須在只有一個模組中定義它。否則,就不可能編制的所有其他模組一個模組獨立。   對於功能問題與優化本身,而不是語言,請參閱使用內聯擴充套件問題 。

行情

  “一個函式宣告[。。。]說明符宣告一個內聯與行內函數。內聯說明符指示的實現,行內函數體替代了在呼叫點是首選通常的函式呼叫機制。一個實現不要求在呼叫執行此點內聯替代,但是,即使這個內嵌替代省略,由7.1.2行內函數定義的其他規則,仍應得到尊重“。   - 國際標準化組織14882:1998(E)的,目前的C + +標準,第7.1.2   “的函式說明符宣告的行內函數是一個行內函數。[。。。]製作一個行內函數的函式表明該函式被調用盡可能快。在何種程度上這些建議是有效的,是實現定義( 注:例如,一個實施內聯替換可能不會執行,或者可能只執行替換內聯在宣告中要求的範圍內聯的)。   “[。。。]內聯定義不提供外部定義的功能,並且不禁止的定義,還有一個是外部的翻譯單位。一個內聯定義提供了任何其他的外部定義,翻譯可能用來實現呼籲在相同的翻譯單元的功能。沒有指定是否呼叫該函式內聯定義或使用外部定義。“   - 國際標準化組織9899:1999(E)的C99標準,第6.7.4

巨集比較

  行內函數的功能和預處理巨集的功能相似。相信大家都用過預處理巨集,我們會經常定義一些巨集,如   #define TABLE_COMP(x) ((x)>0?(x):0)   就定義了一個巨集。   為什麼要使用巨集呢?因為函式的呼叫必須要將程式執行的順序轉移到函式   所存放在記憶體中的某個地址,將函式的程式內容執行完後,再返回到轉去執行   該函式前的地方。這種轉移操作要求在轉去執行前要儲存現場並記憶執行的地   址,轉回後要恢復現場,並按原來儲存地址繼續執行。因此,函式呼叫要有一   定的時間和空間方面的開銷,於是將影響其效率。而巨集只是在預處理的地方把   程式碼展開,不需要額外的空間和時間方面的開銷,所以呼叫一個巨集比呼叫一個   函式更有效率。   但是巨集也有很多的不盡人意的地方。   1、.巨集不能訪問物件的私有成員。   2、.巨集的定義很容易產生二意性。   我們舉個例子:   #define TABLE_MULTI(x) (x*x)   我們用一個數字去呼叫它,TABLE_MULTI(10),這樣看上去沒有什麼錯誤,   結果返回100,是正確的,但是如果我們用TABLE_MULTI(10+10)去呼叫的話,   我們期望的結果是400,而巨集的呼叫結果是(10+10*10+10),結果是120,這顯   然不是我們要得到的結果。避免這些錯誤的方法,一是給巨集的引數都加上括號。   #define TABLE_MULTI(x) ((x)*(x))   這樣可以確保不會出錯,但是,即使使用了這種定義,這個巨集依然有可能   出錯,例如使用TABLE_MULTI(a++)呼叫它,他們本意是希望得到(a+1)*(a+1)的   結果,而實際上呢?我們可以看看巨集的展開結果: (a++)*(a++),如果a的值是   4,我們得到的結果是4*4 = 16,a = 6。而我們期望的結果是5*5=25,這又出現了問題。   事實上,在一些C的庫函式中也有這些問題。例如:Toupper(*pChar++)就會對   pChar執行兩次++操作,因為Toupper實際上也是一個巨集。   我們可以看到巨集有一些難以避免的問題,怎麼解決呢?   下面就是用我要介紹的行內函數來解決這些問題,我們可以使用行內函數   來取代巨集的定義。而且事實上我們可以用行內函數完全取代預處理巨集。   行內函數和巨集的區別在於,巨集是由前處理器對巨集進行替代,而行內函數是   通過編譯器控制來實現的。而且行內函數是真正的函式,只是在需要用到的時   候,行內函數像巨集一樣的展開,所以取消了函式的引數壓棧,減少了呼叫的開   銷。你可以象呼叫函式一樣來呼叫行內函數,而不必擔心會產生於處理巨集的一   些問題。   我們可以用Inline來定義行內函數,不過,任何在類的說明部分定義的函   數都會被自動的認為是行內函數。   下面我們來介紹一下行內函數的用法。   行內函數必須是和函式體申明在一起,才有效。像這樣的申明   Inline Tablefunction(int I)是沒有效果的,編譯器只是把函式作為普通的函   數申明,我們必須定義函式體。   Inline tablefunction(int I) {return I*I};   這樣我們才算定義了一個行內函數。我們可以把它作為一般的函式一樣調   用。但是執行速度確比一般函式的執行速度要快。   我們也可以將定義在類的外部的函式定義為行內函數,比如:   Class TableClass{   Private:   Int I,j;   Public:   Int add() { return I+j;};   Inline int dec() { return I-j;}   Int GetNum();   }   inline int tableclass::GetNum(){   return I;   }   上面申明的三個函式都是行內函數。在C++中,在類的內部定義了函式體的   函式,被預設為是行內函數。而不管你是否有inline關鍵字。   行內函數在C++類中,應用最廣的,應該是用來定義存取函式。我們定義的   類中一般會把資料成員定義成私有的或者保護的,這樣,外界就不能直接讀寫我   們類成員的資料了。   對於私有或者保護成員的讀寫就必須使用成員介面函式來進行。如果我們把   這些讀寫成員函式定義成行內函數的話,將會獲得比較好的效率。   Class sample{   Private:   Int nTest;   Public:   Int readtest(){ return nTest;}   Void settest(int I) {nTest=I;}   }   當然,行內函數也有一定的侷限性。就是函式中的執行程式碼不能太多了,如   果,行內函數的函式體過大,一般的編譯器會放棄內聯方式,而採用普通的方式   呼叫函式。這樣,行內函數就和普通函式執行效率一樣了。

注意事項

  使用行內函數應注意的事項   行內函數具有一般函式的特性,它與一般函式所不同之處只在於函式呼叫的處理。一般函式進行呼叫時,要將程式執行權轉到被呼叫函式中,然後再返回到呼叫它的函式中;而行內函數在呼叫時,是將呼叫表示式用行內函數體來替換。在使用行內函數時,應注意如下幾點: 1.在行內函數內不允許用迴圈語句和開關語句。 如果行內函數有這些語句,則編譯將該函式視同普通函式那樣產生函式呼叫程式碼,遞迴函式(自己呼叫自己的函式)是不能被用來做行內函數的。行內函數只適合於只有1~5行的小函式。對一個含有許多語句的大函式,函式呼叫和返回的開銷相對來說微不足道,所以也沒有必要用行內函數實現。 2.行內函數的定義必須出現在行內函數第一次被呼叫之前。 3.本欄目講到的類結構中所有在類說明內部定義的函式是行內函數。