C語言行內函式

2020-07-16 10:04:27
一般來說,呼叫一個函數流程為:當前呼叫命令的地址被儲存下來,程式流跳轉到所呼叫的函數並執行該函數,最後跳轉回之前所儲存的命令地址。

對於需要經常呼叫的小函數來說,這大大降低了程式執行效率。所以,C99 新增了行內函式(inline function)

關鍵字 inline 告訴編譯器,任何地方只要呼叫行內函式,就直接把該函數的機器碼插入到呼叫它的地方。這樣程式執行更有效率,就好像將行內函式中的語句直接插入到了原始碼檔案中需要呼叫該函數的地方一樣。

要將一個函數定義為行內函式,需要在函數定義時加上 inline 函數修飾符。例 1 中,swapf()被定義為行內函式,用來交換兩個浮點變數的值,函數 selection_sortf()會呼叫行內函式 swapf()。

【例1】函數 swapf()
// 函數swapf()交換兩個浮點變數的值
// 引數:兩個指向float的指標
// 返回值:無
inline void swapf( float *p1, float *p2 )       // 一個行內函式
{
   float tmp = *p1; *p1 = *p2; *p2 = tmp;
}
// 函數selection_sortf() 使用 selection-sort演算法
// 對float陣列進行排序
// 引數:一個float陣列,以及其長度
// 返回值:無
void selection_sortf( float a[], int n )        // 對長度為n的陣列進行排序
{
  register int i, j, mini;                      // 3個索引變數
  for ( i = 0; i < n - 1; ++i )
  {
    mini = i;                                   // 從索引i開始,尋找最小值
    for ( j = i+1; j < n; ++j )
      if ( a[j] < a[mini] )
        mini = j;
      swapf( a+i, a+mini);                      // 交換最小值元素和索引i元素的值
  }
}

一般來說,不建議把將包含迴圈的函數定義成行內函式,例如函數 selection_sortf()。例 1 在 for 迴圈中使用行內函式來加速執行效率。

inline 修飾符並非強制性的:編譯器有可能會置之不理。例如,遞回函數通常不會被編譯成行內函式。編譯器有權自行決定是否要將有 inline 修飾符的函數編譯成行內函式。

和其他函數不同的是,在每個用到行內函式的翻譯單元中,都必須重複定義這個行內函式。編譯器必須時刻準備好該函數定義,以便在呼叫它時及時插入內聯程式碼。因此,經常在標頭檔案中定義行內函式

如果某個翻譯單元內的某個函數的所有宣告都具有 inline 修飾符,而沒有 extern 修飾符,那麼該函數具有內聯定義(inline definition)。

內聯定義只針對翻譯單元,它不構成外部定義,因此別的翻譯單元可以包含該函數的外部定義。如果有外部定義附加到內聯定義中,那麼編譯器可以自由選擇要使用哪一種定義。

如果使用儲存類修飾符 extern 來宣告一個採用 inline 定義的函數,那麼該函數的定義就會是外部的(external)。例如,下面的宣告與 swapf()的定義如果放在例 1 的同一個翻譯單元中,那麼 swapf()就具有 extern 定義:
extern void swapf( float *p1, float *p2 );

一旦函數 swapf()具有外部的定義,其他翻譯單元只需要採用普通的函數宣告,就可以呼叫它。然而,從別的翻譯單元呼叫函數,將不會被編譯成行內函式。

行內函式其實就是普通函數,只不過它們在呼叫時採用機器碼形式。和普通函數一樣,行內函式具有自己的地址。如果行內函式使用到宏,前處理器就會展開宏,展開時所用的宏值,取該行內函式在原始碼中定義所在位置的宏值。然而,在沒被宣告為 static 的行內函式中,不應該以靜態儲存週期的方式來定義可修改的物件。