是否必须将标题中的内联函数插入到调用它的所有位置?

时间:2016-05-10 06:37:41

标签: c compilation

我总是认为编译器可以选择按原样插入函数还是将其视为常规函数。

但是,如果内联函数在头文件中定义(并且可以包含在多个源文件中),我认为不可能。

这是对的吗?

由于

3 个答案:

答案 0 :(得分:1)

是的,如果他真的内联inline函数,编译器可以完全自由选择。实际上他可以自由地内联任何函数,他已经看到了这个定义。

inline作为关键字被引入以避免"多个符号定义"问题。因此,只有在头文件中有定义才有意义。

通常的方案是有一个像

这样的定义
inline void toto(void) {
  ...
}

在几个TU包含的头文件中。然后在恰好一个TU中你有一个没有inline

的声明
void toto(void);

确保符号由此TU导出。

答案 1 :(得分:0)

在ISO C99和ISO C11中,在头文件中有:

inline void f(void) { ..... }

意味着编译器确实可以在内联函数或将其视为常规函数之间进行选择。

但是,当作为常规函数处理时,编译器的行为与您编写void f(void);时的行为类似,即它将在其他地方链接到f的主体。

因此,您需要准确地输入一个翻译单元:

extern inline void f(void);

导致编译器以该单位发出正文。

但是,在GNU C中,使用inline void f(void) { ... }会导致未定义的行为。编译器仍然可以选择,但是当它选择将其视为常规函数时,它的行为就像您编写了void f(void) { ... }一样,即它在每个使用它的翻译单元中发出函数体。

有些人在标题中将它们的函数写为static inline void f(void) { .... },它在ISO C和GNU C中都有效,但可能使链接器更难将所有副本合并为一个。

See this thread for more detail

答案 2 :(得分:0)

假设在标题中定义了内联函数,如: inline void f(void) { ... } ......我可以在你的信息中看到两个问题:

  1. 您是否必须在每个包含函数定义的标头中包含该标头 翻译单元(TU),您将在其中使用此功能?
  2. 编译器可以自由地在某些TU中内联函数,而不是 在同一个项目中的其他TU中内联它?
  3. Ad.1:简答:是的。
    答案很长:
    对于编译,inline函数的定义(不仅仅是声明!)必须在每个TU中都可见,您将在其中调用它。在每个TU中包括标题是最好的解决方案。
    通过省略标题中函数的存储类说明符(如上面的代码片段,这里可以,请参阅下面有关使用externstatic的简短讨论),您将使用默认值,内联函数的含义:inline关键字在每个包含此标题的TU中处于活动状态。该函数保留默认的外部链接(!),因此它也可以从其他TU访问(即在不包括此标题的TU中),但关键字inline在不包含函数内联定义的TU中不活动。
    现在链接器必须将一个版本的函数放在一些目标文件中,因此该函数可以从TU生成的目标文件链接,这些目标文件不包含此函数的“内联”定义(或者编译器决定不包含的TU)尽管使用inline关键字,但内联。) 通过在包含的头文件之后添加,通过重新声明函数为非内联来指示单个TU: void f(void); 如果在标题中的static函数定义之前添加inline,则每个TU将生成自己的副本,并且可执行代码可能会占用更多空间(受内联影响代码大小的数量因素影响)。但是你不需要在任何TU中重新声明该函数为非内联 如果在标题中将extern添加到inline函数定义中,那么您将获得'未定义的引用'链接器错误,因为没有任何TU会为它生成可执行代码,尽管拥有函数的定义(=知道它的正文) )。为避免这种情况,至少有一个TU必须另外包含此函数的非静态重新声明: inline void f(void); 请注意,GCC在C程序中实现了三种不同的内联语义(GNU C90,gnu_inline,以及一种用于例如-std = c99),但据我所知,它们都不符合上述条件。)<登记/>
    Ad2:是。即使在单个TU中,编译器也可以自由内联inline函数的一部分调用,或者内联所有调用,或者不调用。说实话,这同样适用于没有inline关键字定义的函数,因为大多数编译器都会尝试'内联',作为优化的一部分,即使不使用关键字也是如此。毕竟,关键字只是对编译器的一个提示,因为其他人已在他们的评论/回复中突出显示。