C 內聯inline函式

2021-08-30 06:08:31 字數 4577 閱讀 2328

內聯inline函式

在c中保持效率的一種方法是使用巨集,他的行為類似於函式呼叫但卻沒有呼叫的開銷(like a function call without the normal function call overhead.)。

巨集是由由預處理器preprocessor而非編譯器compiler處理的,其直接替換巨集**,沒有引數入棧、函式呼叫及返回等開銷。

但是在c++中使用巨集有兩個問題:

巨集類似於函式呼叫但並非總是如此,其有***;

預處理器不能訪問類的成員變數(preprocessor has no permission to access class member data),因此不能作為成員函式。

為了保持預處理器巨集的效率同時增添真正的函式的安全及類範圍的特性,c++中採用了內聯函式。

預處理器的陷阱

如果你認為編譯器和預處理器的行為相似,那麼你則進入了陷阱,如:

#define floor(x,b) x>=b?0:1

當呼叫形式如下時,

if(floor(a&0x0f,0x07)) // ...

巨集將擴充套件為

if(a&0x0f>=0x07?0:1)

由於&的優先順序最低,因此其行為和預想的不一樣了

因此在使用巨集時,要將引數加上(),以防止改變了表示式的優先順序

#define floor(x,b) ((x)>=(b)?0:1)

但即使如此,巨集仍然有其***,如

#define band(x) (((x)>5 && (x)<10) ? (x) : 0)

//: c09:macrosideeffects.cpp

#include "../require.h"

#include

using namespace std;

#define band(x) (((x)>5 && (x)<10) ? (x) : 0)

int main()

} ///:~

a = 4

band(++a)=0

a = 5

a = 5

band(++a)=8

a = 8

a = 6

band(++a)=9

a = 9

a = 7

band(++a)=10

a = 10

a = 8

band(++a)=0

a = 10

a = 9

band(++a)=0

a = 11

a = 10

band(++a)=0

a = 12

根據a的大小,++a的執行次數不一樣,因此系統的***始終在變,即函式的功能特性不穩定,這是程式設計師所不希望的,其本質原因在於巨集是簡單的文字替換。

巨集和訪問許可權

謹慎的使用巨集可以避免上述問題,但是仍然有乙個不可逾越的障礙是,巨集沒有對於成員訪問範圍的概念。

class x

#define val(x::i) // error

巨集不能訪問類的私有成員,另外不能確定你訪問的是哪個物件。因此很多程式設計師為了效能將某些成員變數屬性更改為public的,但這樣就失去了private的安全效能。

內聯函式

為了解決巨集對於類的私有成員變數的訪問許可權問題,將巨集的概念納入編譯器的控制範圍即可,這就是內聯函式,他具備函式的一切特性,但是沒有函式呼叫的開銷,因為內聯函式象預處理巨集一樣在呼叫處被擴充套件。

類內部定義的函式自動擴充套件為內聯函式,但是你也可以通過inline關鍵字宣告某函式為內聯函式。但是宣告時必須和函式定義放在一起,否則編譯器將其視為普通函式。對於內聯函式,編譯器會進行引數和返回值的型別檢查。

inline int plusone(int x);這樣是不起任何作用的,必須如下方式宣告

inline int plusone(int x)

必須將內聯函式的定義放在標頭檔案中,編譯器將函式型別和函式體放在其符號表中,當遇到相應的呼叫時,就將其替換;標頭檔案中的內聯函式有個特殊狀態,每個檔案中都有內聯函式的實現,但並不會出現重複定義的錯誤,因為內聯函式是在編譯階段替換的,並沒有鏈結過程,不涉及導函式分配的記憶體位址等問題。

內聯函式和編譯器

為了理解內聯函式何時有效,需要了解編譯器如何處理內聯。

編譯器將函式型別包括函式名、引數個數及其型別還有返回值型別儲存在符號表中,當函式體的語法無誤時將其實現也儲存在符號表中,**的形式取決於編譯器。當遇到呼叫內聯函式時,編譯器會分析引數和返回值型別並可能做適當的強制轉換,都沒有問題時就會進行**替換,並可能還有進一步的優化。

什麼時候不能使用內聯

內聯函式有兩種限制,當不能使用內聯時,編譯器將之視為普通函式,為其分配記憶體空間,通常會出現多重定義的錯誤,但是鏈結器被告知忽略這種問題。

當函式的功能過於複雜時,編譯器不會實施內聯,這取決於編譯器,但通常情況下,迴圈或者過多的**不會被內聯,因為此時**執行的時間可能比函式呼叫的時間多很多,內聯失去了意義。

另外一種情況是需要顯式或隱式的得到某函式的位址時,編譯器要產生位址則必須為其分配記憶體空間;而進行內聯替換時只是將其儲存在符號表中,並不為其分配空間。

內聯中的前向引用

當在內聯函式中呼叫了類中還未宣告的函式怎麼辦呢?

這種情況,編譯器仍然可以將其內聯,因為語法規則表明只有到類宣告的「}」處才進行內聯函式的替換。

//: c09:evaluationorder.cpp

// inline evaluation order

class forward

// call to undeclared function:

int f() const

int g() const

};int main() ///:~

構造和析構函式中的隱藏活動

在構造和析構函式中你可能誤認為內聯比實際的效率高,因為構造和析構過程中可能含有隱含活動,如當類中含有子物件時必須呼叫子物件的構造和析構函式。這種子物件可能是成員函式也可能是繼承而來的。成員物件的例子如下:

// hidden activities in inlines

#include

using namespace std;

class member

~member()

};class withmembers // trivial?

~withmembers()

};int main() ///:~

減少clutter

在實際的工程專案中,若在類中定義函式,則會弄亂類的介面並使得類很難使用,因此有些人認為任何成員函式的定義應該放在類的外部實現,以保持類的整潔。如果需要優化的話,則用inline關鍵字。

//: c09:noinsitu.cpp

// removing in situ functions

class rectangle ;

inline rectangle::rectangle(int w, int h)

: width(w), height(h) {}

inline int rectangle::getwidth() const

inline void rectangle::setwidth(int w)

inline int rectangle::getheight() const

inline void rectangle::setheight(int h)

int main() ///:~

inline成員函式應該放在標頭檔案中,而非inline函式應該放在定義檔案中。

使用inline關鍵字的形式宣告還有乙個好處是使得各個成員函式的定義具備統一的風格。

更多的預處理器特性

當需要用到預處理器中的三種特性時,採用巨集而非內聯函式。

分別為字串化(stringizing),字串連線(string concatenation),及符合貼上(token pasting)。字串化即強制將x轉化為字元陣列,通過#實現。當兩個字串之間沒有任何符號時,字串連線將使其合併為乙個字串。這兩個特性在編寫除錯**時特別有用,如:

#define debug(x) cout << #x " = " << x << endl

可以用此技術來跟蹤**的執行,在執行**的同時列印相應資訊。

#define trace(s) cerr << #s << endl; s

這種方法在只有單一語句的for迴圈中可能會出現問題,如

for(int i = 0; i < 100; i++)

trace(f(i));

此時可以將「;」更改為「,」成為逗號表示式。

#define trace(s) cerr << #s << endl, s

或者更改為do while結構,如:

#define trace(s) do while(0)

符號貼上

符號貼上即將兩個符號貼上在一起生成乙個新的符號,通過「##」實現。

#define field(a) char* a##_string; int a##_size

class record ;

上述方式生成乙個識別符號作為字串,另乙個作為串的長度,不僅便於閱讀,同時消除了編碼出錯的可能性,並且便於維護。

在linux的核心**中存在大量這樣資料的定義,尤其是些init初始化階段的資料,或者是某些儲存在特殊段的資料結構

inline函式 C 內聯函式 inline

inline要起作用,必須要與函式定義放在一起,而不是函式的宣告 inline 當編譯器處理呼叫內聯函式的語句時,不會將該語句編譯成函式呼叫的指令,而是直接將整個函式體的 插人呼叫語句處,就像整個函式體在呼叫處被重寫了一遍一樣,在執行時是順序執行,而不會進行跳轉。優點 內聯函式沒有執行函式呼叫的開銷...

C 內聯函式 inline

巨集 就是使用乙個字串來代替乙個表示式 或函式呼叫 編譯之前,預處理器會使用這個巨集字串所代表的表示式 或函式呼叫 來替換所有出現的巨集字串,這樣的話,用巨集表示的函式呼叫 就不用另外開闢函式棧,不用保護和恢復函式呼叫現場,這樣就提高了 的執行效率 所以,呼叫乙個巨集比呼叫乙個函式更有效 但是呼叫巨...

c 內聯函式inline

1.inline原理 在程式編譯時,編譯器將程式中出現的內聯函式的呼叫表示式用內聯函式的函式體來進行替代。採用空間換時間的策略。是以 膨脹 複製 為代價,僅僅省去了函式呼叫的開銷,從而提高函式的執行效率。例如 如果乙個函式被指定為inline函式,則它將在程式中每個呼叫點上被展開。int i 10 ...