2013-07-03 33 views
3

如果我嘗試編譯下面的C代碼沒有聲明的功能的靜態,我得到一個鏈接錯誤:爲什麼內聯函數需要static關鍵字?

undefined reference to '_fun' 

,但如果我不讓它靜它的工作原理。在C++中,沒有關鍵字static就可以正常工作。

// Doesn't work 
inline int fun() 
{ 
    return 3; 
} 

// Works 
static inline int fun() 
{ 
    return 3; 
} 

int main(int argc, const char * argv[]) 
{ 
    printf("%i", fun()); 
} 
+3

什麼是編譯器? –

+0

在gcc 4.7版上工作得很好。 – unxnut

+0

什麼是錯誤信息? (它適用於我與海灣合作委員會,但不與「gcc -std = c99 -pedantic」。) –

回答

3

C中inline的要求由ISO C標準的6.7.4節定義。從N1256

Any function with internal linkage can be an inline function. For a function with external linkage, the following restrictions apply: If a function is declared with an inline function specifier, then it shall also be defined in the same translation unit. If all of the file scope declarations for a function in a translation unit include the inline function specifier without extern, then the definition in that translation unit is an inline definition. An inline definition does not provide an external definition for the function, and does not forbid an external definition in another translation unit. An inline definition provides an alternative to an external definition, which a translator may use to implement any call to the function in the same translation unit. It is unspecified whether a call to the function uses the inline definition or the external definition.

引用本節據我所知,你的定義滿足所有這些要求。這:

inline int fun() 
{ 
    return 3; 
} 

既是聲明定義fun。沒有inline關鍵字,它會有外部鏈接。

棘手的部分是最後一句:

It is unspecified whether a call to the function uses the inline definition or the external definition.

在這種情況下,沒有外部的定義。你沒有說你正在使用什麼編譯器,但默認情況下,gcc -std=c99 -pedantic顯然選擇使用外部定義,並且因爲沒有一個,你會得到一個鏈接器錯誤。 (沒有-std=c99 -pedantic,沒有鏈接器錯誤,但這是因爲gcc還實現了inline作爲C90之上的擴展。)

如果您只打算在該源文件中使用該函數,那麼也可能無論如何,添加static關鍵字,使其內部鏈接。

實驗表明,如果我使用-O1,-O2-O3中的任何一種進行優化編譯,程序將編譯,鏈接並正確運行。

C標準中的腳註似乎暗示gcc的行爲是正確的。在相同的部分的示例具有類似的非staticinline函數定義:

inline double cels(double t) 
{ 
     return (5.0 * (t - 32.0))/9.0; 
} 

後跟:

Because cels has external linkage and is referenced, an external definition has to appear in another translation unit (see 6.9); the inline definition and the external definition are distinct and either may be used for the call.

標準的意圖似乎是,如果一個inline函數具有內部連接,它應該在使用它的源文件中只定義一次,但如果它具有外部鏈接,則內聯定義是一個替代到必須出現在別處的非內聯定義。是否調用外部函數或擴展內聯定義的選擇由編譯器決定。

幾點不是直接到你的問題相關:

int fun()也許應該int fun(void)。空括號是合法的,但它們表明該函數採用未指定數量和類型的參數。 void指定它不需要參數,這就是你想要的。

如果您打算致電print f,則需要#include <stdio.h>;這不是可選的。

您不希望constargv的聲明中。對於這個問題,既然你沒有引用命令行參數,你可以寫int main(void)

+0

如果空的parens是一部分的定義,即'int fun(){...}'是完全正確的;個人而言,我爲了一致性而添加了'void' ... – Christoph

+0

@Christoph:無論是否定義,空'()'都不會爲函數引入原型。出於這個原因,即使在定義時使用'(void)'也是一個好主意。它在這方面確實有所作爲。 – AnT

+0

@AndreyT:這並不是我讀過標準的印象,但gcc同意你的看法(clang至少提供了警告,但沒有錯誤);相關標準引用:*函數原型是一個函數聲明,聲明瞭它的參數類型*(C99 6.2.1§2),* void類型的一個未命名參數的特殊情況,列表指定該函數沒有參數*(C99 6.7.5.3§10),*函數聲明器中的空列表是該函數定義的一部分,指定函數沒有參數*(§14) – Christoph

0

C99 inline語義很微妙 - 事實上,語言的整個部分(存儲持續時間與鏈接,暫定和內聯定義)是一團糟。

儘管inline在包含存儲類說明符(staticextern)的定義中充當編譯器提示,並且基本上可以忽略,但如果不存在說明符,則語義會更改。

inline int fun(void) { ... }的定義做兩兩件事:

首先,它聲明與外部鏈接的標識符,但不提供相應的外部定義。這意味着這種定義必須由不同的翻譯單位提供,否則我們最終會出現未定義的行爲(可能表現爲未能鏈接)。

其次,它提供了一個內聯定義,它是外部替代的替代。由於函數體在當前的翻譯單元中可見,因此編譯器可以使用它來內聯函數或類型專門化。

爲了獲得外部定義,直到fairly recently,我認爲有必要在另一個翻譯單元(或假的,與4行預處理器代碼)重複函數定義。

然而,這不是必要的:一個單一的代碼線 - 其包括extern說明符的功能的重新聲明 - 是足以使直列式定義到外部的一個。

在你的榜樣,這將意味着將

inline int foo(void) 
{ 
    return 42; 
} 

成一個頭文件foo.h並提供與內容

#include "foo.h" 

// force definition to be external instead of inline 
// I believe inline could be omitted, but it doesn't hurt 
extern inline foo(void); 

爲什麼這是有用的源文件foo.c?由於C缺乏模板,所以通用代碼通常會帶來性能損失,因爲您需要使用void*和函數指針(或更復雜的情況下,vtables)來僞造泛型。但是,只有當相關函數定義在當前翻譯單元中可見時,足夠智能的優化程序才能獲得模板的大多數(可能全部)性能優勢,但是(在沒有鏈接時優化的情況下)。

儘管可以通過在頭文件中添加static定義來實現此目的,但這可能會使代碼大小增加到與C++模板相同的不可接受的級別。

相比之下,使用C99 inline函數,編譯器可以自由地忽略內聯定義而偏向外部定義,甚至可以駐留在共享庫中。

功能的一個很好的例子是qsort()stdlib.h的內聯定義和libc.so的外聯定義。 qsort()沒有先驗原因比std::sort()慢。

相關問題