C99标准下的.c文件中的内联函数

68

我在.c文件中定义了我的函数(没有头文件声明),如下:

inline int func(int i) {
 return i+1;
}

然后在同一个文件中我在下面使用它:

...
i = func(i);

在链接期间,我收到了“undefined reference to 'func'”的错误。为什么?


3
要么将其设为static,要么提供一个非内联定义,以便链接器可以找到它。 - Daniel Fischer
请参见https://dev59.com/RW015IYBdhLWcg3w7wMW。 - Jens
3个回答

74

C99标准中的inline模型与大多数人想象的有些不同,特别是与C++中使用的不同。

inline只是一种提示,使编译器不会抱怨有重复定义的符号。它不能保证函数被内联,也不能确保需要时实际生成符号。要强制生成符号,您需要在inline定义之后添加某种实例化方式:

int func(int i);
通常情况下,您应该在头文件中定义inline,然后将其包含在多个.c文件(编译单元)中。并且在这些编译单元中只有一处会用到上述行。您可能看到的问题只是因为您没有对编译器进行优化运行。

因此,在.c文件中使用inline并没有太多意义,最好使用static代替,即使额外使用inline也不会带来太大好处。


我的错,一个没有 inlineextern 文件作用域声明确实会强制定义不是内联的。 - Christoph
3
我有一个问题,为什么在关闭优化时会出现这种情况?当我使用gcc -O1选项打开它时,它只是编译,那么在gcc优化中,哪个选项可以使代码以正确的方式编译? - Lazureus
4
@Lazureus,当你使用“-O0”编译时,我认为你的意思是,gcc通常不会内联任何函数。因此,你必须在某个地方定义这些符号,否则它就找不到任何已声明定义的inline函数。 - Jens Gustedt
原型真的需要在定义之后吗?我进行了一些测试,似乎证实了我的信念,即在定义之前的原型也会强制符号生成。只需运行并查看:echo "int f(void); inline int f(void){return 42;}" | cc -x c -Wall -Wextra -Wpedantic -pedantic-errors -Werror -O3 -std=c99 -S - -o -。这是标准的扩展(还是gcc的误解)? - alx - recommends codidact

49

C99的内联语义经常被误解。 inline修饰符有两个作用:

首先,对于static inlineextern inline声明,它作为编译器提示。如果删除修饰符,则语义保持不变。

其次,在没有staticextern的情况下,提供内联定义作为外部定义的替代方案,外部定义必须存在于另一个翻译单元中。不提供外部定义将导致未定义行为,通常表现为链接失败。

如果您想将函数放入共享库中,但同时也要使函数体可用于优化(例如内联或专业化),这是特别有用的。假设编译器足够智能,这允许您恢复 C++ 模板的许多好处,而无需通过预处理程序来实现。

请注意,具体情况比我在此处描述的要复杂一些,因为拥有另一个文件范围的非内联外部声明将触发 Jens' 答案中所述的第一种情况,即使定义本身是 inline 而不是 extern inline。这是有意设计的,以便您可以在头文件中拥有单个内联定义,通过添加单个外部声明行将其包含到提供外部定义的源文件中。


1
这个答案有点过时了 - 我以为 Jens 的回答有误,但事实证明我错了... - Christoph
9
实际上,解释在什么情况下使用“thus”这个词是一个有价值的回答(至少对我来说是这样)。 - Vorac
对于“内联定义作为外部定义的替代品”感到困惑 - 那么当我的代码调用它时,哪个“替代品”会被调用? - wick

3
这是由于 GCC 处理内联函数的方式所致。GCC 作为优化的一部分执行内联替换。
要消除这个错误,请在内联之前使用 static。使用 static 关键字会强制编译器内联此函数,使程序成功编译。
static inline int func(int i) {
 return i+1;
}
...
i = func(i);

但是如何处理成员函数? - Piroxiljin
1
@Piroxiljin:C语言没有成员函数,只有C++才有。根据Jens在这个问题上的回答,inline在C和C++中的含义是不同的。 - Peter Cordes
1
哎呀,这里发生了什么?我一直以为 staticinline 是不相关的。也就是说,static 意味着它必须存在,而 inline(根据定义)则不需要。(所谓“不存在”,是指作为一个定义的符号,因为它将被内联)。 - sherrellbc

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接