內聯函數(五)
C++ 中的 const 常量可以代替宏常數定義,如:const int A = 3; <==> #define A 3;那麽在 C++ 中是否有解決方案代替宏代碼片段呢?在 C++ 中推薦使用內聯函數替代宏代碼片段,使用 inline 關鍵字聲明內聯函數。內聯函數聲明時 inline 關鍵字必須和函數定義結合在一起,否則編譯器會直接忽略內聯請求。
C++ 編譯器可以將一個函數進行內聯編譯,被 C++ 編譯器內聯編譯的函數叫做內聯函數;C++ 編譯器直接將函數插入函數調用的地方,內聯函數沒有普通函數調用時的額外開銷(壓棧,跳轉,返回等)
我們以代碼為例來進行分析
#include <stdio.h> #define FUNC(a, b) ((a) < (b) ? (a) : (b)) inline int func(int a, int b) { return a < b ? a : b; } int main(int argc, char *argv[]) { int a = 1; int b = 3; int c = FUNC(++a, b); printf("a = %d\n", a); printf("b = %d\n", b); printf("c = %d\n", c); return 0; }
我們在第 3 行定義了宏函數,返回兩個數中的較小值;在第 5 行定義函數返回較小值。我們先用宏函數來返回,在第 14 行比較 ++a 和 b 的值,明顯返回的是 2,那麽最後的值就為 a = 2, b = 3, c = 2;我們來看看結果
我們看到的是返回的是 3,仔細看下程序,我們調用的是宏函數,也就是說,這個 ++a 被執行了兩次,所以返回 3 很正常。我們再來把宏函數調用換成函數調用,看看結果
我們看到這次和我們分析的是一致的啦,那麽宏函數和函數還是有區別的,宏函數有弊端。
下來我們來講講內聯函數,內聯函數具有普通函數的特征(參數檢查,返回類型等);函數的內聯請求可能會被編譯器拒絕;函數被內聯編譯後,函數體直接擴展到調用的地方;宏代碼片段由預處理器處理,進行簡單的文本替換,沒有任何的編譯過程,因此可能出現副作用
現代 C++ 編譯器能夠進行編譯優化,一些函數即使沒有 inline 聲明,也可能被內聯編譯;一些現代的 C++ 編譯器通過了擴展語法,能夠對函數進行強制內聯,如:g++:__attribute__((always_inline))屬性; MSVC:__forcrinline
下來我們以代碼為例進行分析
#include <stdio.h> inline int add_inline(int n); int main(int argc, char *argv[]) { int r = add_inline(10); printf(" r = %d\n", r); return 0; } inline int add_inline(int n) { int ret = 0; for(int i=0; i<n; i++) { ret += i; } return ret; }
我們在 VS2010 中通過斷點調試進到反匯編,看看內聯函數生效了沒。在第 8 行通過調用內聯函數 add_inline,我們看到匯編函數如下
我們看到有 call 的調用,證明內聯請求失敗了。還是調用了函數,並沒有在這塊直接把函數體鋪開。這也證明了我們之前所說的不是每一個內聯請求都會得到執行。
在 C++ 中 inline 內聯編譯有這麽幾種條件限制:a> 不能存在任何形式的循環語句;b> 不能存在過多的條件判斷語句;c> 函數體不能過於龐大;d> 不能對函數進行取址操作;e> 函數內聯聲明必須在函數調用語句之前;
通過對內聯函數的學習,總結如下:1、C++ 中可以通過 inline 聲明內聯函數;2、編譯器直接將內聯函數體擴展到函數調用的地方;3、inline 只是一種請求,編譯器不一定允許這種請求;4、內聯函數省去了函數調用時壓棧、跳轉和返回的開銷。
歡迎大家一起來學習 C++ 語言,可以加我QQ:243343083。
內聯函數(五)