为什么内联函数需要静态关键字?

时间:2013-07-03 01:52:56

标签: c static inline

如果我尝试编译以下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());
}

2 个答案:

答案 0 :(得分:3)

C中inline的要求由ISO C标准的第6.7.4节定义。从N1256

引用此部分
  

任何具有内部链接的函数都可以是内联函数。为一个   具有外部链接的功能,以下限制适用:如果a   函数是用 内联 函数说明符声明的,然后是它   也应在同一翻译单元中定义。如果所有的文件   翻译单元中函数的范围声明包括   没有 extern 内嵌 功能说明符,然后是其中的定义   翻译单元是内联定义。一个   内联定义不提供外部定义   功能,并不禁止另一个外部定义   翻译单位。内联定义提供了替代   外部定义,翻译人员可以用来实现任何调用   到同一翻译单元的功能。它没有具体说明   是否对函数的调用使用内联定义或   外部定义。

据我所知,您的定义满足所有这些要求。这样:

inline int fun()
{
    return 3;
}

既是声明又是fun定义。如果没有inline关键字,它将具有外部链接。

棘手的部分是最后一句:

  

未指定对函数的调用是否使用内联   定义或外部定义。

在这种情况下,没有外部定义。您没有说明您正在使用哪种编译器,但默认情况下gcc -std=c99 -pedantic显然选择使用外部定义,并且由于没有一个,您会收到链接器错误。 (没有-std=c99 -pedantic,没有链接器错误,但这是因为gcc还将inline实现为C90之上的扩展。)

如果您只是在一个源文件中使用该函数,那么您最好还是添加static关键字,以便为其提供内部链接。

实验表明,如果我使用-O1-O2-O3中的任何一个进行优化编译,您的程序将正确编译,链接和运行。

C标准中的脚注似乎暗示gcc表现正常。同一部分中的示例具有类似的非static inline函数定义:

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

接下来是:

  

因为 cels 具有外部链接并被引用,所以外部   定义必须出现在另一个翻译单元中(见6.9);   内联定义和外部定义是截然不同的   要么可以用于通话。

标准的意图似乎是如果inline函数具有内部链接,则应该在使用它的源文件中仅定义一次,但如果它具有外部链接,则内联定义为替代到必须出现在别处的非内联定义。是否调用外部函数或扩展内联定义的选择留给了编译器的一时兴起。

有些问题与您的问题没有直接关系:

int fun()应该是int fun(void)。空括号是合法的,但它们表明该函数采用了未指定的数字和类型的参数。 void指定它不需要参数,这就是你想要的。

如果您打算致电#include <stdio.h> f,则需要print;这不是可选的。

const声明中您不希望argv。就此而言,由于您没有引用命令行参数,因此可以编写int main(void)

答案 1 :(得分:0)

C99 inline语义是微妙的 - 事实上,语言的整个部分(存储持续时间与链接,暂定和内联定义)是一团糟。

虽然inline在包含存储类说明符(staticextern)的定义中充当编译器提示,并且基本上可以忽略,但如果不存在说明符,则语义会发生变化。

inline int fun(void) { ... }这样的定义做了两件事:

首先,它声明了一个带有外部链接的标识符,但没有提供相应的外部定义。这意味着这样的定义必须由不同的翻译单元提供,否则我们最终会得到未定义的行为(可能表现为链接失败)。

其次,它提供了内联定义,它是外部定义的替代。由于函数体在当前转换单元中可见,编译器可以使用它来内联函数或类型特化。

要获得外部定义,直到fairly recently,我认为有必要在另一个翻译单元中重复该函数定义(或伪造4行预处理器代码)。

但是,这不是必需的:单行代码 - 包含extern说明符的函数的重新声明 - 足以使内联定义成为外部定义。

在你的例子中,这意味着要放

inline int foo(void)
{
    return 42;
}

到头文件foo.h并提供内容

的源文件foo.c
#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);

为什么这有用?由于C缺少模板,通用代码通常会带来性能损失,因为您需要伪造带有void*和函数指针的泛型(或者,在更复杂的情况下,使用vtable)。

但是,只有在当前翻译单元中可以看到相关的函数定义时,足够智能的优化器才能获得模板的大部分(可能是所有)性能优势,但(在没有链接时优化的情况下)。 / p>

虽然可以通过在标头中添加static定义来实现这一点,但这可能会将代码大小增加到不可接受的水平,就像C ++模板一样。

相比之下,使用C99 inline函数,编译器可以自由地忽略内联定义,而不是外部定义,它甚至可以驻留在共享库中。

可以从中受益的函数的一个很好的例子是qsort()stdlib.h中的内联定义和libc.so中的外部定义。 qsort()没有先验原因要比std::sort()慢。