內聯函數

維基百科,自由的百科全書

電腦科學中,內聯函數(有時稱作在線函數編譯時期展開函數)是一種編程語言結構,用來建議編譯器對一些特殊函數進行內聯擴展(有時稱作在線擴展);也就是說建議編譯器將指定的函數體插入並取代每一處調用該函數的地方(上下文),從而節省了每次調用函數帶來的額外時間開支。但在選擇使用內聯函數時,必須在程序佔用空間和程序執行效率之間進行權衡,因為過多的比較複雜的函數進行內聯擴展將帶來很大的存儲資源開支。另外還需要特別注意的是對遞歸函數的內聯擴展可能引起部分編譯器的無窮編譯。

設計內聯函數的動機

內聯擴展是一種特別的用於消除調用函數時所造成的固有的時間消耗方法。一般用於能夠快速執行的函數,因為在這種情況下函數調用的時間消耗顯得更為突出。這種方法對於很小的函數也有空間上的益處,並且它也使得一些其他的優化成為可能。

沒有了內聯函式,程式員難以控制哪些函數內聯哪些不內聯;由編譯器自行決定是否內聯。加上這種控制維度准許特定於應用的知識,諸如執行函式的頻繁程度,被利用於選擇哪些函數要內聯。

此外,在一些語言中,內聯函數與編譯模型聯繫緊密:如在C++中,有必要在每個使用它的模塊中定義一個內聯函數;與之相對應的,普通函數必須定義在單個模塊中。這使得模塊編譯獨立於其他的模塊。

巨集的比較

通常,在C語言中,內聯展開的功能由帶參巨集(Macros)在源碼級實現。內聯提供了幾個更好的方法:

  • 巨集調用並不執行類型檢查,甚至連正常參數也不檢查,但是函數調用卻要檢查。
  • C語言的巨集使用的是文本替換,可能導致無法預料的後果,因為需要重新計算參數和操作順序
  • 在巨集中的編譯錯誤很難發現,因為它們引用的是擴展的程式碼,而不是程式設計師鍵入的。
  • 許多結構體使用巨集或者使用不同的語法來表達很難理解。內聯函數使用與普通函數相同的語言,可以隨意的內聯和不內聯。
  • 內聯程式碼的調試信息通常比擴展的巨集程式碼更有用。

語言支持

C++C99C11GNU C都支持內聯函數,最被廣泛使用的C標準1989 ANSI C則不支持。在Ada中,關鍵字「pragma」可以用來聲明內聯。其他的大部分程式語言,包括Java和函數式語言,不支持內聯函數,但他們的編譯器常常進行強制性的內聯擴展。不同的編譯器在內聯擴展上有處理不同複雜程度函數的能力。主流的C++編譯器如Visual C++GCC提供了一個選項來自動內聯任何一個合適的函數,即使它們沒有被聲明為內聯函數。

對於C++,inlineextern inline同義:內聯函數在各個編譯單元都可能會生成一份,要求各處定義完全一致。內聯函數在C++中的寫法如下:

inline int max (int a, int b) {
    if (a > b) 
    return a; 
    else 
    return b; 
}
a = max (x, y); // 等价于 "a = (x > y ? x : y);"

對於C99,inline為編譯單元內部可見,extern inline為編譯單元外部也可見。

內聯函數的不足

除了通常使用內聯擴展可能帶來的問題,作為一種程式語言特性的內聯函數也可能並沒有看起來那麼有效,原因如下:

  • 通常,編譯器比程序設計者更清楚對於一個特定的函數是否合適進行內聯擴展;一些情況下,對於程式設計師指定的某些內聯函數,編譯器可能更傾向於不使用內聯甚至根本無法完成內聯。
  • 對於一些開發中的函數,它們可能從原來的不適合內聯擴展變得適合或者倒過來。儘管內聯函數或者非內聯函數的轉換易於巨集的轉換,但增加的維護開支還是使得它的優點顯得更不突出了。
  • 對於基於C的編譯系統,內聯函數的使用可能大大增加編譯時間,因為每個調用該函數的地方都需要替換成函數體,程式碼量的增加也同時帶來了潛在的編譯時間的增加。

參見

外部連結