C++深度解析 内聯函數分析(5)
宏
#define
宏定義會經過預處理器,隻是進行文本替換,缺點在于不會進行文法和語義檢查的,僅僅是複制、粘貼的過程,編譯器根本不知道宏的存在。
是以,C++中,當需要某個類型的常量時,可以使用const常量 來代替 宏常數,如:
const int A = 3 <<<-------替代------->>> #define A 3
内聯函數 inline
使用内聯函數 替代 宏代碼片段。
使用inline關鍵字聲明内聯函數。
内聯函數沒有普通函數調用的時候的額外開銷(參數的入棧,函數的傳回,跳轉)。
内聯函數會被編譯器優化,編譯器直接将内聯函數的函數體進行擴充,擴充到調用内聯函數的地方。
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;
//FUNC(++a, b)會被預處理器展開為:((++a) < (b) ? (++a) : (b))
int c = FUNC(++a, b); //((++a) < (b) ? (++a) : (b));
printf("a = %d\n", a); // 3
printf("b = %d\n", b); // 3
printf("c = %d\n", c); // 3
return 0;
}
結果如下:

因為FUNC是一個宏定義,文本替換。比較時候++a:a=2。如果條件成立++a:a=3。(進而a被加了兩次)由此看來,使用宏定義是有副作用的。
示例代碼二:(内聯函數)
#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); // 2
printf("b = %d\n", b); // 3
printf("c = %d\n", c); // 2
return 0;
}
結果如下:
内聯函數直接将函數體插入到調用的地方。示例中,int c = func(++a, b)調用了内聯函數,是以把函數體直接擴充到func(++a, b)。
内聯函數總結:
内聯函數具有普通函數的特征(參數檢查,傳回類型等)
函數的内聯請求可能被編譯器拒絕(通過配置的方式,讓編譯器接受内聯請求)
函數被内聯編譯後,函數體直接擴充到調用的地方。
(宏代碼片段由預處理器處理,進行簡單的文本替換,沒有任何編譯過程,是以可能出現副作用)
是以,在C++程式設計中,首選内聯函數,而不是宏代碼片段。
對函數進行強制内聯
g++:__attribute__((always_inline))屬性,當一個函數擁有這個屬性後,這個函數就會被g++編譯器強制内聯
MSVC:__forceinline
示例代碼一:
#include <stdio.h>
//__forceinline //MSVC
__attribute__((always_inline)) // g++
//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;
}
注意:C++中inline内聯編譯的限制:
總的來說,函數體不能過于複雜。
- 不能存在任何形式的循環語句
- 不能存在過多的條件判斷語句
- 函數體不能過于龐大
- 不能對函數進行取址操作
- 函數内聯聲明必須在調用語句之前
内聯函數和普通函數的差別:
- 普通函數:每次調用前,CPU都會儲存現場(入棧),調用完後還要恢複現場(出棧)等額外開銷。
- 内聯函數:就會在每次調用的地方,将内聯函數裡的代碼段“内聯地”展開,是以省去了額外的開銷
小結:
C++中可以通過inline聲明内聯函數
編譯器直接将内聯函數擴充到函數調用的地方
inline隻是一種請求,編譯器不一定允許這種請求
内聯函數省去了函數調用時壓棧,跳轉和傳回的開銷