天天看點

C++深度解析 内聯函數分析 内聯inline和宏#define(5)宏内聯函數 inline内聯函數總結:對函數進行強制内聯小結:

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;
}           

結果如下:

C++深度解析 内聯函數分析 内聯inline和宏#define(5)宏内聯函數 inline内聯函數總結:對函數進行強制内聯小結:

因為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;
}           

結果如下:

C++深度解析 内聯函數分析 内聯inline和宏#define(5)宏内聯函數 inline内聯函數總結:對函數進行強制内聯小結:

内聯函數直接将函數體插入到調用的地方。示例中,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隻是一種請求,編譯器不一定允許這種請求

内聯函數省去了函數調用時壓棧,跳轉和傳回的開銷

繼續閱讀