2013-07-03 34 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()慢。

相关问题