C++
中的const
常量能夠替代宏常數定義:c++
#define A 3 const int A = 3;
咱們還能夠利用宏來定義宏代碼片斷:函數
#define FUNC(a, b) ((a) < (b) ? (a) : (b))
可是宏代碼塊不是函數, 常帶有反作用,爲消除反作用,用函數來替代,可是函數在調用的是由有參數的入棧,函數的返回,棧變量的銷燬等等內存的開銷,宏代碼塊則是徹底沒有的,爲綜合二者的優勢,C++出現了內聯函數。優化
宏代碼塊是由預處理處理,進行簡單的文本替換,沒有通過任何編譯過程,所以可能出現反作用,而內聯函數則是在編譯階段進行處理,具備函數的特徵(參數檢查、返回類型等)。spa
因爲宏使用簡單的文本替換,對於有些狀況,在同一個做用域中同一個宏使用兩次會出現重定義錯誤。code
#define SWAP(a,b)\ int tmp = a; \ a = b; \ b = tmp; int main() { int x = 10; int y = 5; SWAP(x, y); SWAP(x, y);//此處會出錯 system("pause"); return 0; }
C++中推薦使用內聯函數替代宏代碼片斷,基本形式以下:圖片
inline int func(int a, int b) { return a < b ? a : b; }
inline
關鍵字聲明能夠將一個函數聲明爲內聯函數,可是這種聲明不是絕對的,inline
是對編譯器的一種請求,請求編譯器將對應函數進行內聯編譯,編譯器是能夠拒絕的,是否能夠內聯成功,要看編譯器。內存
inline
內聯函數聲明時,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; int c = FUNC(++a, b); // ((++a) < (b) ? (++a) : (b)) // 2 < 3 ? 3 : 3 // 宏代碼塊的缺陷 int d = func(++a, b); printf("a = %d\n", a); printf("b = %d\n", b); printf("c = %d\n", c); printf("d = %d\n", d); return 0; }
查看反彙編進行分析編譯器
inline int func(int a, int b) ; func函數在這裏被編譯 { 00EA17B0 push ebp 00EA17B1 mov ebp,esp 00EA17B3 sub esp,0C4h 00EA17B9 push ebx 00EA17BA push esi 00EA17BB push edi 00EA17BC lea edi,[ebp-0C4h] 00EA17C2 mov ecx,31h 00EA17C7 mov eax,0CCCCCCCCh 00EA17CC rep stos dword ptr es:[edi] return a < b ? a : b; 00EA17CE mov eax,dword ptr [a] 00EA17D1 cmp eax,dword ptr [b] 00EA17D4 jge func+31h (0EA17E1h) 00EA17D6 mov ecx,dword ptr [a] 00EA17D9 mov dword ptr [ebp-0C4h],ecx 00EA17DF jmp func+3Ah (0EA17EAh) 00EA17E1 mov edx,dword ptr [b] 00EA17E4 mov dword ptr [ebp-0C4h],edx 00EA17EA mov eax,dword ptr [ebp-0C4h] } ... ... ... int d = func(++a, b); 調用函數 01001873 mov eax,dword ptr [a] 01001876 add eax,1 01001879 mov dword ptr [a],eax 0100187C mov ecx,dword ptr [b] 0100187F push ecx 01001880 mov edx,dword ptr [a] 01001883 push edx 01001884 call func (01001177h) ; 這裏就是func的函數調用 01001889 add esp,8 0100188C mov dword ptr [d],eax
發現編譯器並無內聯成功,仍是普通的函數調用it
對編譯器進行設置
再看反彙編代碼
inline int func(int a, int b) ; 內聯函數在這裏不會被編譯 { return a < b ? a : b; } ... ... ... int d = func(++a, b) ; 函數調用的時候,直接將整個函數體插入調用的地方進行編譯 00BC4EC3 mov eax,dword ptr [a] 00BC4EC6 add eax,1 00BC4EC9 mov dword ptr [a],eax 00BC4ECC mov ecx,dword ptr [a] 00BC4ECF cmp ecx,dword ptr [b] 00BC4ED2 jge main+7Fh (0BC4EDFh) 00BC4ED4 mov edx,dword ptr [a] 00BC4ED7 mov dword ptr [ebp-0F4h],edx 00BC4EDD jmp main+88h (0BC4EE8h) 00BC4EDF mov eax,dword ptr [b] 00BC4EE2 mov dword ptr [ebp-0F4h],eax 00BC4EE8 mov ecx,dword ptr [ebp-0F4h] 00BC4EEE mov dword ptr [d],ecx
沒有了函數調用的代碼,直接是將內聯函數塊編譯進來了,省去了函數調用的開銷,內聯請求成功。
在Linux系統中用g++編譯器編譯,也是同樣的狀況,能夠經過配置g++ 編譯器達到內聯效果
inline
聲明,也可能被內聯編譯一些現代C++編譯器提供了擴展語法,可以對函數進行強制內聯,如:
g++ : __attribute__((always_inline)) MSVS: __forceinline
強制內聯
#include <stdio.h> //__forceinline //__attribute__((always_inline)) 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
內聯編譯的限制:
C++中能夠經過inline
聲明內聯函數編譯器直接將內聯函數體擴展到函數調用的地方
inline
只是一種請求,編譯器不必定容許這種請求內聯函數省去了函數調用時壓棧、跳轉和返回等開銷