C,内联函数和GCC

时间:2014-10-22 08:29:57

标签: c

如果我有:

inline int foo(void)
{
   return  10 + 3;
}

int main(void)
{
   foo();
}

使用GCC,文件编译得很好,但链接器返回undefined reference to foo

相反,如果我删除inline链接器很高兴!

似乎链接器可以看到外部定义的标识符 内联定义的标识符不是。

此外,如果我使用-O3标志进行编译,则链接器会看到内联定义的标识符。

问题是什么?

4 个答案:

答案 0 :(得分:5)

好的,所以在阅读VivienG's link后,我想我已经理解了这条错误信息背后的确切原因。它令人困惑和误导(至少对我来说;如果你只有一个翻译单元就不会发生这种情况),但有可能解释:

  • 假设编译器不想实际内联代码,它必须知道放置该函数的位置,特别是当它在多个翻译单元中使用时。

  • 经典方法是创建多个副本,每个副本对应一个翻译单元(至少对于那些使用它的单位)。

  • 这可能会导致问题,例如当试图做一些函数指针比较时(尽管如此,仍然留下问题为什么)。

为了解决这个问题(以及我可能没有在此列出的其他问题),他们认为其中一些实际上非常整洁(尽管 - 如我所说 - 在我看来是误导性的)解决方案:

您以您知道的方式将函数声明为inline,但同时您告诉编译器将非内联版本放在extern关键字的哪个位置。

因此,在您的示例中,您将按原样保留函数并将其放在头文件中(因此可以知道它将在何处使用):

inline int foo(void)
{
     return  10 + 3;
}

此外,要告诉编译器在哪里放置非内联版本,您必须在一个翻译单元中再添加一个“前向”声明:

extern inline int foo(void);

因此,与经典函数相比,整个概念基本上是颠倒过来的:将实现放在标题中,然后在一个文件中进行简短声明。

如上所述,在使用-O3参数时,所有标有inline的代码实际上都是内联的,这不会导致问题发生。

答案 1 :(得分:2)

您需要将-std=gnu11传递给编译器。

  

gcc主要。 -o main -std = gnu11

编辑:这post回答了很多问题。

答案 2 :(得分:1)

在C99的C语言中,如果只想在一个编译单元中使用内联函数,则应将其声明为“静态内联”,一切都会好的。否则,在没有“static”的情况下声明的内联函数必须在一个编译单元中声明为“extern inline”。

通常,您将在.c文件中使用“static inline”,或者在.h文件中使用“inline”,并在一个.c文件中将该函数声明为“extern inline”。

答案 3 :(得分:1)

我认为user3629249's comment击中了头部。如果使用-O3编译代码,则会消除对foo()的调用。即装配差异:

使用-O3

main:
.LFB4:
    .cfi_startproc
    xorl    %eax, %eax
    ret
    .cfi_endproc

没有-O3

main:
.LFB1:
    // ...
    call    foo
    // ...

C标准在脚注161(§6.9/ 5)中说:

  

因此,如果未使用通过外部链接声明的标识符   表达式,不需要外部定义。

文件范围内的函数具有外部链接,但是在没有extern关键字的情况下内联声明的函数是内联定义。在§6.9/ 5中它说:

  

外部定义是一个外部声明,也是一个   函数的定义(除了内联定义)或   宾语。如果使用外部链接声明的标识符   表达式(除了作为sizeof或的操作数的一部分)   _Alignof运算符,其结果是一个整数常量),在整个程序的某个地方,应该只有一个外部定义   标识符;否则,不得超过   之一。 161)

     

4 [...]如6.7中所讨论的那样,也会导致为对象或由标识符命名的函数保留存储的声明是一个定义。

即使用存储类说明符staticextern