C++深度解析 行內函數分析 inline和#define(5)
C++深度解析 行內函數分析(5)
巨集定義
#define A 3
巨集定義會經過前處理器,只是進行文字替換,缺點在於不會進行語法和語義檢查的,僅僅是複製、貼上的過程,編譯器根本不知道型別是什麼。
所以,C++中,當需要某個型別的常量時,可以使用const常量來代替巨集常數,如:
const int A = 3 <<<-------替代------->>> #define A 3
行內函數 inline
使用行內函數 替代 巨集程式碼片段
示例程式碼一:(巨集定義呼叫)
#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)。
行內函數
行內函數具有普通函式的特徵(引數檢查,返回型別等)
函式的內聯請求可能被編譯器拒絕(通過配置的方式,讓編譯器接受內聯請求)
函式被內聯編譯後,函式體直接擴充套件到呼叫的地方
(巨集程式碼片段由前處理器處理,進行簡單的文字替換,沒有任何編譯過程,因此可能出現副作用)
對函式進行強制內聯
g++:__attribute__((always_inline))
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只是一種請求,編譯器不一定允許這種請求
行內函數省去了函式呼叫時壓棧,跳轉和返回的開銷