2014-10-22 94 views
7

如果我有:C,内联函数和GCC

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

int main(void) 
{ 
    foo(); 
} 

与海湾合作委员会的文件编译良好,但链接器返回undefined reference to foo

相反,如果我删除inline该接头用快乐!

看起来外部定义的标识符对链接器是可见的,但 内嵌定义的标识符不是。

此外,如果我编译与-O3标志链接器看到内联定义的标识符。

有什么问题?

+3

https://gcc.gnu.org/onlinedocs/gcc/Inline.html – Ashalynd 2014-10-22 08:34:16

+0

@Ashalynd答案可能在某处,但它是什么? – harold 2014-10-22 08:36:06

+0

我想知道'extern'是否需要GCC或C标准的一部分。我相信C99中增加了“内联”,但我不知道它是如何影响链接的。 – 2014-10-22 08:38:27

回答

4

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

  • 假设编译器不希望实际直列代码,它必须知道该函数的放置位置,尤其是在多个翻译单元中使用时。

  • 传统方法是创建多个副本,每个翻译单元一个(或至少对于使用它的单元)。

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

为了解决这个问题(和其它问题,我可能没有在这里列出),他们已经想到了一些实际上是相当整齐(虽然 - 如前所述 - 在我看来误导)解决方案:

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

所以,在你的榜样,你会保持你的功能,就是,把它放在一个头文件(因此它被称为地方它会被使用):

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

另外,告诉编译器在哪里放置非内嵌版本,你必须在一个转换单元中添加更多的“前进”的声明:

extern inline int foo(void); 

所以整个概念相比,经典的职能基本上是相反的:把实施在头文件中,然后在一个文件中进行简短的声明。

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

+0

或者如果我把静态代码运行良好,但我不不明白这是如何影响链接器的... – xdevel2000 2014-10-22 10:15:40

+0

标记它'static'已经告诉编译器“这里和只有这里”,所以它再次知道在哪里放置函数。 – Mario 2014-10-22 10:16:51

+0

是的,我可以理解,但你总是从编译器视图中解释这一点。但是static是一个链接关键字,它对链接器有意义。所以,再次抱歉,但这并不能解释为什么报告错误的链接程序... – xdevel2000 2014-10-22 10:33:11

2

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

gcc main。 -o main -std = gnu11

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

+1

谨慎解释这背后的原因?我不明白为什么最初的代码不能用默认参数编译。 – Mario 2014-10-22 08:52:09

+0

你可以看看这个[post](http://gustedt.wordpress.com/2010/11/29/myth-and-reality-about-inline-in-c99/) – VivienG 2014-10-22 08:57:09

+0

我想我已经明白了上面给出的代码执行失败的原因失败了,但是你的链接并没有解释为什么这个行为再次被* gnu11 *改变。这不是在网页上,或者我错过了吗? – Mario 2014-10-22 09:23:49

1

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

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

+0

这应该备份与标准引用 – 2014-10-22 09:40:47

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):

因此,如果在 中未使用通过外部链接声明的标识符,则表示离子,它不需要外部定义。

在文件范围的函数有外部链接,但声明为inline而不extern关键字的功能是一个直列定义。在§6.9/ 5,它说:

一种外部定义是外部声明,这也是一个功能(比内联定义其他)或 对象的 定义。如果在 表达式中使用外部链接声明的标识符(除了作为sizeof或_Alignof运算符的操作数的一部分,该运算符的结果是一个整数常量),整个程序中的某处应该只有一个外部定义: 标识符;否则,不得超过 之一。 161)

4 [...]如在6.7讨论的,声明也导致存储要被保留用于一个对象或一个由标识符命名的函数是一个定义。

即,使用存储类说明符staticextern

+0

因此,一个外部定义也作为声明作为声明,并且链接器工作正常...但内联定义不... – xdevel2000 2014-10-22 10:16:59