天天看點

【C++深度解析】4、内聯函數分析

文章目錄

    • 1 内聯函數
    • 2 深入内聯函數
    • 3 小結

C++ 中的 const 常量可以替代宏常數定義,那麼 C++ 中是否有解決方案替代代碼片呢?

  • C++ 中使用 inline 關鍵字聲明内聯函數,推薦使用内聯函數代替宏代碼片段
【C++深度解析】4、内聯函數分析
  • C++ 編譯器直接将内聯函數體插入函數調用的地方
  • 内聯函數沒有普通函數調用時的額外開銷(壓棧,跳轉,傳回)
  • C++ 編譯器不一定滿足函數的内聯請求

執行個體分析:内聯函數初探

// 4-1.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;
    int c = func(++a, b);
    printf("a = %d\n", a);
    printf("b = %d\n", b);
    printf("c = %d\n", c);
    return 0;
}
           

代碼很簡單,就是求兩數的最小值,編譯運作:

$ g++ 4-1.c -o 4-1
$ ./4-1
a = 2
b = 3
c = 2
           

将第 12 行改為 int c = FUNC(++a, b); 再次編譯運作:

$ g++ 4-1.c -o 4-1
$ ./4-1
a = 3
b = 3
c = 3
           

為什麼會出現這種情況呢,因為宏定義是直接展開,展開後如下:

int c = ((++a) < (b) ? (++a) : (b));
           

是以 ++a 操作計算了兩次,a,b,c 的值都是 3。

  • 用 VS2013 反彙編,如下圖所示
【C++深度解析】4、内聯函數分析

可以看出,雖然函數 func() 聲明為 inline,但是這裡并沒有展開,還是進行了函數調用。inline 隻是一個請求,C++ 編譯器不一定滿足内聯請求。

  • 内聯函數具有普通函數的特性(參數檢查,傳回類型等)
  • 函數的内聯請求可能被編譯器拒絕
  • 函數被内聯函數編譯器後,函數體直接擴充到調用的地方
  • 宏代碼片由預處理器處理,進行簡單的文本替換,沒有任何編譯過程

  • 現代 C++ 編譯器能夠進行編譯優化,一些函數即使沒有 inline 聲明,也可能被内聯編譯
  • 一些現代 C++ 編譯器擴充了文法,能夠對函數進行強制内聯,如:
    • g++: __attribute__((always_inline))
    • MSVC: __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;
}
           

上面的代碼中 inline 是 C++ 中的标準,向編譯器申請将函數設為内聯函數,編譯器不一定滿足内斂要求;__forceinline 是 vs 編譯器的強制内聯标示符,将 inline 換為__forceinline,強制函數為内聯類型;

__attribute__((always_inline)) 是 g++ 編譯器的标示符,将 inline 換為__attribute__ 強制函數為内聯類型。

  • 不能存在任何形式的循環語句
  • 不能存在過多的條件判斷語句
  • 函數體不能過于龐大
  • 不能對函數進行取址操作
  • 函數内聯聲明必須在調用語句之前

繼續閱讀