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

What is the use of the `inline` keyword in C?

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

  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); 的实际作用是什么?

我从来没有真正在我的 C 程序中发现 inline 关键字的用法,当我在其他人的代码中看到这个关键字时,它几乎总是 static inline,我看不出有什么区别只有 static.

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

在这个例子中,所有的声明和定义都使用内联而不是外部:

// a declaration mentioning inline     
inline int max(int a, int b);

// a definition mentioning inline  
inline int max(int a, int b) {  
  return a > b ? a : b;  
}

Here 是一个参考,可以让您更清楚地了解 C 中的内联函数以及内联和外部的用法。

来自 C11 规范中的 6.7.4 函数说明符

6 A function declared with an inline function specifier is an inline function. Making a function an inline function suggests that calls to the function be as fast as possible.138)The extent to which such suggestions are effective is implementation-defined.139)

138) By using, for example, an alternative to the usual function call mechanism, such as inline substitution. Inline substitution is not textual substitution, nor does it create a new function. Therefore, for example, the expansion of a macro used within the body of the function uses the definition it had at the point the function body appears, and not where the function is called; and identifiers refer to the declarations in scope where the body occurs. Likewise, the function has a single address, regardless of the number of inline definitions that occur in addition to the external definition.

139) For example, an implementation might never perform inline substitution, or might only perform inline substitutions to calls in the scope of an inline declaration.

提示编译器该函数被广泛使用,要求调用该函数时速度优先。但是对于现代智能编译器,这可能或多或少是无关紧要的,因为编译器可以决定一个函数是否应该内联,并且可以忽略用户的内联请求,因为现代编译器可以非常有效地决定如何调用函数。

static inline void f(void) {} has no practical difference with static void f(void) {}.

大多数情况下现代编译器都是如此 none。对于任何编译器,没有 实际/可观察到的输出差异。

inline void f(void) {} in C doesn't work as the C++ way. How does it work in C?

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

What actually does extern inline void f(void); do?

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

一句话"Inline"说"In""Line",在function中加上这个关键字会影响程序运行时,程序编译的时候把function里面写的代码贴到下面函数调用,因为函数调用比内联代码成本更高,所以这优化了代码。 所以, static inline void f(void) {} 和 static void f(void) {} ,在此 inline 关键字中,do 在运行时有所不同。但是当函数的代码行太多时,它不会影响运行时。 如果在函数前加上static,函数的生命周期就是整个程序的生命周期。并且该功能的使用仅限于该文件。 要了解 extern 你可以参考 - Effects of the extern keyword on C functions

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

内联函数:

gcc.gnu.org 说,

By declaring a function inline, you can direct GCC to make calls to that function faster. One way GCC can achieve this is to integrate that function's code into the code for its callers. This makes execution faster by eliminating the function-call overhead; in addition, if any of the actual argument values are constant, their known values may permit simplifications at compile time so that not all of the inline function's code needs to be included. The effect on code size is less predictable; object code may be larger or smaller with function inlining, depending on the particular case.

因此,它告诉编译器将函数构建到使用它的代码中,以缩短执行时间。

如果你声明像 setting/clearing 这样的小函数,一个标志或一些重复执行的位切换,inline,它可以在时间上产生很大的性能差异,但代价是代码大小。


非静态内联和静态内联

再次参考gcc.gnu.org,

When an inline function is not static, then the compiler must assume that there may be calls from other source files; since a global symbol can be defined only once in any program, the function must not be defined in the other source files, so the calls therein cannot be integrated. Therefore, a non-static inline function is always compiled on its own in the usual fashion.


外部内联?

再说一遍,gcc.gnu.org,说明一切:

If you specify both inline and extern in the function definition, then the definition is used only for inlining. In no case is the function compiled on its own, not even if you refer to its address explicitly. Such an address becomes an external reference, as if you had only declared the function, and had not defined it.

这种inline和extern的结合,几乎可以达到宏的效果。使用它的方法是将一个函数定义放在带有这些关键字的头文件中,并将定义的另一个副本(缺少 inline 和 extern)放在库文件中。头文件中的定义导致对函数的大多数调用被内联。如果该函数的任何用途仍然存在,它们指的是库中的单个副本。


总结一下:

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

注意:当我在这个答案中谈论 .c 文件和 .h 文件时,我假设你已经正确地布置了你的代码,即 .c 文件只包括 .h 文件。不同之处在于,一个 .h 文件可能包含在多个翻译单元中。

static inline void f(void) {} has no practical difference with static void f(void) {}.

在 ISO C 中,这是正确的。它们在行为上是相同的(假设您当然没有在同一个 TU 中以不同方式重新声明它们!)唯一的实际影响可能是导致编译器进行不同的优化。

inline void f(void) {} in C doesn't work as the C++ way. How does it work in C? What actually does extern inline void f(void); do?

this answer and also this thread对此进行了解释。

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

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

ISO C 中 .c 文件中的

extern inline void f(void); 与头文件中 inline void f(void) {} 的使用配对。它导致函数的 外部定义 在该翻译单元中发出。如果你不这样做,那么就没有外部定义,所以你可能会得到一个 link 错误(未指定 f links 的任何特定调用是否对外部定义或不)。

换句话说,在 ISO C 中,您可以手动 select 外部定义所在的位置;或者通过在任何地方使用 static inline 来完全抑制外部定义;但在 ISO C++ 中,编译器会选择是否以及在何处进行外部定义。

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

更复杂的是,GNU C++ 允许您在 C++ 代码中编写 static inlineextern inline...我不想猜测它到底做了什么

I never really found a use of the inline keyword in my C programs, and when I see this keyword in other people's code, it's almost always static inline

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

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

因此,如果编码人员想要在 GNU C 中提供 inline 优化提示,则需要 static inline。由于 static inline 在 ISO C 和 GNU C 中都可以工作,因此人们最终接受它并看到它似乎可以正常工作而没有出错是很自然的。

, in which I see no difference with just static.

区别仅在于向编译器提供速度超过大小的优化提示。对于现代编译器,这是多余的。