在C中使用`inline`关键字有什么用?

时间:2015-06-29 05:08:26

标签: c inline c99

我在stackoverflow中读了几个关于C中inline的问题,但仍不清楚。

  1. static inline void f(void) {}static void f(void) {}没有实际区别。
  2. C中的
  3. inline void f(void) {}并不像C ++那样工作。它在C中是如何工作的?
  4. extern inline void f(void);实际上做了什么?
  5. 我从未真正在我的C程序中使用inline关键字,当我在其他人的代码中看到此关键字时,它几乎总是static inline,我认为只与static没有区别。

6 个答案:

答案 0 :(得分:21)

可以通过两种方式优化C代码:代码大小和执行时间。

内联函数:

gcc.gnu.org说,

  

通过声明内联函数,您可以指示GCC更快地调用该函数。 GCC可以实现这一目标的一种方法是将该功能的代码集成到其呼叫者的代码中。这通过消除函数调用开销使执行更快;此外,如果任何实际参数值是常量,则它们的已知值可能允许在编译时进行简化,因此不需要包括所有内联函数的代码。对代码大小的影响不太可预测;根据具体情况,目标代码可能更大或更小,具有函数内联。

因此,它告诉编译器将函数构建到代码中,以便改进执行时间。

如果声明像重复执行的设置/清除标志或某些位切换这样的小功能inline,它可以在时间方面产生很大的性能差异,但代价是代码大小。< / p>

非静态内联和静态内联

再次提及gcc.gnu.org

  

当内联函数不是静态函数时,编译器必须假定可能存在来自其他源文件的调用;由于全局符号只能在任何程序中定义一次,因此不能在其他源文件中定义该函数,因此无法集成其中的调用。因此,非静态内联函数总是以通常的方式自行编译。

extern inline?

再次,gcc.gnu.org,说明了一切:

  

如果在函数定义中同时指定了inline和extern,则该定义仅用于内联。在任何情况下,函数都不会自行编译,即使您明确地引用其地址也是如此。这样的地址成为外部引用,就像你只声明了函数一样,并且没有定义它。

内联和外部的这种组合几乎具有宏的效果。使用它的方法是将函数定义放在带有这些关键字的头文件中,并将定义的另一个副本(缺少内联和外部)放在库文件中。头文件中的定义导致对函数的大多数调用都被内联。如果函数的任何使用仍然存在,则它们引用库中的单个副本。

总结一下:

  1. inline void f(void){}inline定义仅在当前翻译单元中有效。
  2. static inline void f(void) {} 由于存储类为static,因此标识符具有内部链接,inline定义在其他翻译单元中不可见。
  3. extern inline void f(void); 由于存储类为extern,因此标识符具有外部链接,内联定义也提供外部定义。

答案 1 :(得分:18)

注意:当我在本回答中谈论.c个文件和.h个文件时,我认为您已正确布置了代码,即.c个文件仅包含.h文件。区别在于.h文件可能包含在多个翻译单元中。

  

static inline void f(void) {}static void f(void) {}没有实际区别。

在ISO C中,这是正确的。它们的行为是相同的(假设您当然不会在相同的TU中重新声明它们!)唯一的实际效果可能是使编译器以不同方式进行优化。

  C中的

inline void f(void) {}不能像C ++那样工作。它在C中如何工作? extern inline void f(void);实际做了什么?

this answerthis thread解释了这一点。

在ISO C和C ++中,您可以在头文件中自由使用inline void f(void) {} - 尽管出于不同的原因!

在ISO C中,它根本不提供外部定义。在ISO C ++中,它确实提供了外部定义;但是C ++有一个额外的规则(C没有),如果inline函数有多个外部定义,那么编译器会将其排序并选择其中一个。

ISO C中的extern inline void f(void);文件中的

.c旨在与头文件中inline void f(void) {}的使用配对。它导致函数的外部定义在该转换单元中发出。如果您不这样做,那么就没有外部定义,因此您可能会收到链接错误(未指定f的任何特定调用是否链接到外部定义)。

换句话说,在ISO C中,您可以手动选择外部定义的位置;或者在任何地方使用static inline完全取消外部定义;但是在ISO C ++中,编译器会选择外部定义的位置和位置。

在GNU C中,情况有所不同(下面有更多内容)。

为了使事情进一步复杂化,GNU C ++允许您在C ++代码中编写static inlineextern inline ...我不想猜测它到底是什么

  

我从来没有真正在我的C程序中使用inline关键字,当我在其他人的代码中看到这个关键字时,它几乎总是静态内联

许多程序员不知道他们在做什么,只是把看起来有用的东西放在一起。另一个因素是您正在查看的代码可能是为GNU C编写的,而不是ISO C.

GNU C中,普通inline的行为与ISO C不同。它实际上会发出一个外部可见的定义,因此从.h文件中包含一个普通的inline函数两个翻译单元导致未定义的行为。

因此,如果编码器想要在GNU C中提供inline优化提示,则需要static inline。由于static inline在ISO C和GNU C中都有效,因此人们最终解决这个问题并且看到它似乎工作而不会出错。很自然。

  

,其中我认为只有静态没有区别。

不同之处仅在于为编译器提供速度超过大小的优化提示。使用现代编译器,这是多余的。

答案 2 :(得分:4)

来自C11规范

中的 6.7.4函数说明符
  

6使用内联函数说明符声明的函数是内联函数   功能。使函数成为内联函数表明调用   功能尽可能快。 138)程度   这样的建议是有效的   的实现定义即可。 139)

     

138)例如,通过使用常规函数调用的替代方法   机制,例如内联替换。内联替换不是   文本替换,也不创建新功能。因此,   例如,在体内使用的宏的扩展   函数使用函数体所具有的定义   出现,而不是调用函数的位置;和标识符   在身体发生的范围内的声明。同样地,   无论内联数量多少,函数都有一个地址   除外部之外发生的定义   定义

     

139)例如,实现可能   从不执行内联替换,或者可能只执行内联   在内联声明范围内对调用进行替换。

它建议编译器广泛使用此函数并请求在此函数的调用中更喜欢速度。但是使用现代智能编译器,这可能或多或少无关紧要,因为编译器可以决定是否应该内联函数并且可以忽略来自用户的内联请求,因为现代编译器可以非常有效地决定如何调用函数。

  

static inline void f(void) {}static void f(void) {}没有实际区别。

所以,现代编译器大多数时候都没有。对于任何编译器,没有实际/可观察的输出差异。

  C中的

inline void f(void) {}并不像C ++那样工作。怎么做的   在C?工作?

在任何地方内联的函数必须在C ++中的任何地方内联,并且链接器不会抱怨多个定义错误(定义必须相同)。

  

实际上extern inline void f(void);办?

这将提供与f的外部链接。因为f可能存在于其他编译单元中,所以编译器可以选择不同的调用机制来加速调用,或者可以完全忽略inline

答案 3 :(得分:2)

一个函数,其中所有声明(包括定义)都提到内联而不是外部 同一翻译单元中必须有一个定义。标准将此称为内联定义 不会发出独立的目标代码,因此无法从另一个翻译单元调用此定义。

在此示例中,所有声明和定义都使用内联但不是extern:

<form id="msform" method="POST" action="index.php">

Here是一个参考,可以让您更清楚地了解C&amp; C中的内联函数。还有关于内联和使用的内容外部量。

答案 4 :(得分:0)

C内联不同于C++ inline

内联是向编译器提示的一个可能内联函数,无论是否进行内联(实际上inline从未内联于-O0的函数,但它们始终内联于-Ofast的内联翻译单位),它提供以下保证:

  • C99 inline / GNU89 extern inline:此内联定义未发出任何外部可见函数,但可能需要一个函数,因此它必须存在。仅提供内联定义,当编译器决定内联函数时,它将用作替代。这样就可以分别定义同一个符号的内联定义和离线功能,一个具有内联功能,另一个具有脱机功能,但不在同一翻译单元中。内联定义只在本地对编译器可见,并且每个翻译单元可以有自己的内联定义。内联定义不能导出到其他文件,因为内联定义未达到链接阶段。为了在编译时实现此目的,可以将内联定义放在头文件中并包含在每个转换单元中。这意味着使用inline是编译器指令,并且extern / static是为链接器生成的离线版本。
  • C99 extern inline / GNU89 inline:为此内联定义发出了外部可见函数,这意味着该说明符只能在其中一个翻译单元中使用,其余必须在外部定义。从直觉上讲,这与“外部”相反。
  • C99 / GNU89 static inline:为此编译器内联定义的链接器发出本地可见的离线功能 非静态内联函数不应包含非const静态存储持续时间变量或访问静态文件作用域变量,这将产生编译器警告。这是因为,如果从不同的翻译单元提供了离线版本,则函数的嵌入式和离线版本将具有不同的静态变量。因此,它提醒程序员,从逻辑上讲,它应该是const,因为修改和读取静态内容将导致未定义的行为。如果编译器内联该函数,它将读取一个新的静态值,而不是先前的脱机调用中写入的值。将其设置为静态可确保不会从外部提供内联定义,而是从内联定义提供内联定义,这有点像常规函数定义,只是防止其他转换单元使用函数定义。可能会出现访问静态文件作用域变量的警告,因为内联将访问另一个文件中不同行的定义(如果它们都包含一个内联定义,然后在另一个文件中使用extern const char *saddr(void);转换单元;要么找不到符号,要么需要在该文件中声明一个不同的静态变量,或者使用不同的extern定义。int x;允许访问,因为它将被链接到外部引用中-line函数在另一个文件中。 如果该函数未在翻译单元中定义,则无法内联,因为它留给了链接器。如果已定义函数但未内联,则编译器决定内联时将使用此版本
  • 在非内联定义之前/之后使用inline / extern inline原型会覆盖原型,就好像它不存在一样;内联原型与常规原型相同。 在内联定义之前使用内联原型是如何在没有副作用的情况下原型化内联函数;之后,它像普通的原型一样没有用,将被忽略。
  • 在内联定义之前/之后使用extern inline / extern /常规原型与外部内联定义相同;它使用内联定义提供函数的外部外联定义。
  • __attribute__((always_inline))始终使用此定义内联翻译单元中的功能符号。如果用于内联函数,则将不再提供外部定义。它只能用于定义。
  • 适用static的常规规则,然后如果定义位于另一个文件中,则不同于其定义的原型也要遵循相同的规则。如果文件不包含内联定义,则可以提供完全不同的脱机定义,而与其他文件中的任何原型或内联定义无关。

答案 5 :(得分:-3)

一句话&#34;内联&#34;说&#34; In&#34; &#34; Line&#34;,将此关键字添加到函数中会影响运行时的程序,当编译程序时,编写内部函数的代码会粘贴在函数调用下,因为函数调用比内联代码更昂贵,所以这个优化代码。 所以, static inline void f(void){}和static void f(void){},在这个内联关键字中确实在运行时有所不同。但是当函数有太多代码行时,它就不会影响运行时。 如果在功能之前添加静态功能,则功能的使用寿命是整个程序的生命周期。并且该功能仅限于该文件。 要了解extern,您可以参考 - Effects of the extern keyword on C functions