1. 程式人生 > >C++深度解析 行內函數分析 inline和#define(5)

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只是一種請求,編譯器不一定允許這種請求

行內函數省去了函式呼叫時壓棧,跳轉和返回的開銷