编译器何时对函数进行内联处理?

26
在C++中,只有在函数被显式声明为inline(或在头文件中定义)时,函数才会被内联,还是编译器可以根据需要自行内联函数?

3
注:inline 只是编译器的提示,编译器不一定需要将其内联。 - Martin York
@Martin York:不幸的是,你是对的。在99.9%的情况下,最好让编译器在最后一刻决定。但在某些情况下(像图像处理等性能关键程序),拥有一个终极内联强制开关会很好。但我可以理解编译器程序员。因为一旦这个开关存在,许多人会在许多不适当的情况下使用它,无论文档如何坚决警告只在罕见情况下使用它... - mmmmmmmm
1
更准确地说,在C/C++代码中,“inline”具有不同于描述优化的含义。在C/C++代码中,“inline”仅表示函数可以存在多个定义,并且链接器应该将它们合并在一起。这意味着对该函数的调用应该被内联。它们是两个完全独立的概念。 - jalf
@rstevens: 你已经有了那个,它的拼写是#define - MSalters
11个回答

46
inline关键字实际上只是告诉链接器(或者编译器要告诉链接器),同一函数的多个相同定义不是错误。如果您想在标头中定义一个函数,则需要使用它,否则如果标头包含在多个编译单元中,则链接器将会出现“多重定义”错误。
选择inline作为关键字的理由似乎是,唯一想在标头中定义非模板函数的原因是让编译器对其进行内联。除非有完整定义,否则编译器无法内联函数调用。如果函数未在标头中定义,则编译器仅具有声明,并且即使希望这样做,也无法内联函数。
现在,据我所知,不仅编译器优化代码,链接器也可以这样做。链接器可以(如果他们尚未这样做),即使该函数未在同一编译单元中定义,也可以内联函数调用。
如果可能的话,最好不要在标头中定义大于一行的函数(对于编译时间不利,如果大型函数被内联,可能会导致程序膨胀和性能下降)。

1
这是inline的真正重要方面。 - Jason S

32
是的,即使没有显式声明为inline,编译器也可以内联代码。
基本上,只要语义没有改变,编译器就可以对生成的代码进行任何虚拟操作。标准对生成的代码没有强制性要求。

5
既然编译器已经为我们完成了所有工作,那么我们为什么需要内联提示呢? - Baiyan Huang
2
除了UncleBens下面提到的微妙之处外,我认为我们没有。需要注意的是,编译器从一开始就不那么聪明,我们有点被语言的旧特性所束缚。 - Mehrdad Afshari
1
还有一件事:它还允许您在多个编译单元中定义函数。 - Baiyan Huang
1
@lz_prgmr:正如所指出的,它会影响编译器的“抱怨”行为,但这并不意味着编译器被强制内联或者在任何方面内联。编译器可以自由地内联任何它喜欢的东西,并且即使你使用 inline 修饰一个函数,也可以选择不内联。 - Mehrdad Afshari
这使得使用GDB/WinDBG进行调试变得困难。 - Jayanth
显示剩余2条评论

5

编译器可能会内联任何函数,也可能不会内联。他们可以使用 inline 修饰符作为这个决定的提示,但也可以忽略它。

另外请注意,如果类成员函数在类定义中直接被定义,那么它们具有隐式的 inline 修饰符。


4

如果我没记错的话,当优化被打开时,编译器会内联任何适合的例程或方法。


1
“打开优化”不是一个标准术语。您正在描述具有该开关的实际实现。在这种情况下,您不需要使用假设的“可能”:编译器将内联适当的函数。 - MSalters

4
编译器可能会忽略您的内联声明。它基本上被编译器用作提示,以决定是否这样做。编译器没有义务将标记为内联的内容内联,也没有义务不将未标记为内联的内容内联。基本上,您取决于您的编译器和您选择的优化级别。

3

来自IBM信息中心的文本:

使用内联说明符只是向编译器建议可以执行内联扩展;编译器可以忽略该建议。

C语言除main函数外,任何函数都可以使用内联函数说明符声明或定义为内联函数。静态局部变量不允许在内联函数体内定义。

C++类声明内实现的函数会自动定义为内联函数。常规C++函数和类声明外的成员函数(除main外)可以使用内联函数说明符声明或定义为内联函数。在内联函数体内定义的静态局部变量和字符串字面值在翻译单元之间被视为同一对象;


3

你的编译器文档应该告诉你这是实现相关的。例如,根据其手册,GCC从不内联任何代码,除非应用了优化。

如果编译器没有内联代码,则inline关键字将具有与static相同的效果,并且调用代码的每个编译单元都将拥有自己的副本。聪明的链接器可以将它们减少到一个副本。


1
编译器可以内联任何它想要的内容,只要内联不违反代码语义并且可以访问函数代码。它还可以有选择地进行内联 - 当感觉到内联是一个好主意时进行内联,而当感觉到不是一个好主意或者会违反代码语义时则不进行内联。
一些编译器甚至可以在另一个翻译单元中使用函数时进行内联 - 这被称为链接时代码生成。
典型的内联违反代码语义的情况包括虚拟调用和将函数地址传递到另一个函数或存储它的情况。

1

编译器会进行优化,除非你明确指定相反的操作。


0

内联扩展可能无法使用的一些情况包括:

  1. 对于返回值的函数,如果存在循环、开关或goto
  2. 对于不返回值的函数,如果存在return语句;
  3. 如果函数包含静态变量
  4. 如果内联函数是递归的。

内联扩展使程序运行更快,因为消除了函数调用和返回语句的开销。然而,它使程序占用更多的内存,因为定义内联函数的语句在每个调用该函数的点上都会被复制。因此,需要进行权衡。

(如我所读的一本面向对象编程书中所述)


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