如果我有:
inline int foo(void)
{
return 10 + 3;
}
int main(void)
{
foo();
}
使用GCC,文件编译得很好,但链接器返回undefined reference to foo
相反,如果我删除inline
链接器很高兴!
似乎链接器可以看到外部定义的标识符 内联定义的标识符不是。
此外,如果我使用-O3
标志进行编译,则链接器会看到内联定义的标识符。
问题是什么?
答案 0 :(得分:5)
好的,所以在阅读VivienG's link后,我想我已经理解了这条错误信息背后的确切原因。它令人困惑和误导(至少对我来说;如果你只有一个翻译单元就不会发生这种情况),但有可能解释:
假设编译器不想实际内联代码,它必须知道放置该函数的位置,特别是当它在多个翻译单元中使用时。
经典方法是创建多个副本,每个副本对应一个翻译单元(至少对于那些使用它的单位)。
这可能会导致问题,例如当试图做一些函数指针比较时(尽管如此,仍然留下问题为什么)。
为了解决这个问题(以及我可能没有在此列出的其他问题),他们认为其中一些实际上非常整洁(尽管 - 如我所说 - 在我看来是误导性的)解决方案:
您以您知道的方式将函数声明为inline
,但同时您告诉编译器将非内联版本放在extern
关键字的哪个位置。
因此,在您的示例中,您将按原样保留函数并将其放在头文件中(因此可以知道它将在何处使用):
inline int foo(void)
{
return 10 + 3;
}
此外,要告诉编译器在哪里放置非内联版本,您必须在一个翻译单元中再添加一个“前向”声明:
extern inline int foo(void);
因此,与经典函数相比,整个概念基本上是颠倒过来的:将实现放在标题中,然后在一个文件中进行简短声明。
如上所述,在使用-O3
参数时,所有标有inline
的代码实际上都是内联的,这不会导致问题发生。
答案 1 :(得分:2)
答案 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中所讨论的那样,也会导致为对象或由标识符命名的函数保留存储的声明是一个定义。
即使用存储类说明符static
或extern
。