在C headers中放置内联函数是错误的吗?
Is it wrong to place inline functions in C headers?
我正在为几个编译器构建一个 C 项目,其中一些是遗留编译器,它们似乎没有 link 时间内联支持,因此直接放置 static inline
函数似乎合乎逻辑在 header 秒内,实际上每个翻译单元都有自己的副本。
此外,我需要确保某些函数是内联的,以便在某些 low-level 中断处理程序中调用时不会调用其他函数(即更改 CPU 寄存器),所以它不是只是让编译器选择它是否会影响性能。
但是,我的一位同事告诉我这是一件不寻常的事情,我应该避免这样做。在项目的这一点上,我可能仍然可以重新安排所有内容,所以我想确认如果我们决定使用 header 内联,我们在长 运行 中是否可能会遇到一些问题?
来自 n1570(最新的 public C11 草案),§6.7.4:
- 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. The extent to which such suggestions are effective is implementation-defined.
下一节将详细介绍链接,但上面这段话基本上是 C 标准关于 inline
的全部内容。请注意这如何赋予实现所有自由,包括完全忽略 inline
.
因此,仅使用标准 C,您最终可能会得到以正常方式调用的函数的多个实例(每个翻译单元一个)。这通常不是您想要的,因为它结合了两个缺点(重复代码 和 函数调用的开销)。所以我认为标准 C inline
只对单个翻译单元私有的函数有用。即使那样,您也可以假设一个好的优化编译器会自动选择内联候选对象,而无需显式 inline
.
另一方面,如果您的编译器提供了一种方法来实际 强制 内联函数(根据您的评论,_inline
说明符对您的编译器有效),将这些函数放在 header 中是安全的。但请注意,它绝不是可移植的。
与 commented by cmaster 一样,您可以使用 function-like 宏 实现某种“手动内联”,而不是便携式解决方案。
根据对问题的评论,在头文件中定义 static _inline
函数是 对于这个特定编译器 的方法,编译器文档的引用是清楚这一点。
您以后可能会遇到的问题是它是特定编译器的特定扩展,不同于 standard inline
说明符。正如 Felix 所说,该标准允许实现选择它如何实现内联,特别是忽略说明符仍然是一致的:
Making a function an inline function suggests that calls to the function be as
fast as possible. The extent to which such suggestions are effective is
implementation-defined.
也就是说,语义似乎相同(来自 C99 的草案 1256 或 C11 的草案 1570):
6.7.4 Function specifiers
...
Any function with internal linkage can be an inline function. For a function with external
linkage, the following restrictions apply: If a function is declared with an inline
function specifier, then it shall also be defined in the same translation unit. If all of the
file scope declarations for a function in a translation unit include the inline function
specifier without extern, then the definition in that translation unit is an inline
definition. An inline definition does not provide an external definition for the function,
and does not forbid an external definition in another translation unit. An inline definition
provides an alternative to an external definition, which a translator may use to implement
any call to the function in the same translation unit. It is unspecified whether a call to the
function uses the inline definition or the external definition
因此,由于常见的实现确实尊重 inline
说明符,我认为可移植性的风险是有限的
无需将函数声明为 static inline
,因为 ISO 9899
保证函数不会在您不使用存储时导出到链接器 class 说明符 extern
在翻译单元内的任何内联函数声明中。
换句话说,如果你简单地声明为inline aaa
,符号aaa
将在外部不可见。
我引用自6.7.4 Function specifiers
If all of the file scope declarations for a function in a translation
unit include the inline function specifier without extern, then the
definition in that translation unit is an inline definition. An
inline definition does not provide an external definition for the
function, and does not forbid an external definition in another
translation unit.
emacs 在 lisp.h
中使用了这种内联技术,header 定义了 lisp objects,我引用了 emacs 的源代码:
Some operations are so commonly executed that they are implemented
as macros, not functions, because otherwise runtime performance would
suffer too much when compiling with GCC without optimization.
There's no need to inline everything, just the operations that would otherwise cause a serious performance problem.
我正在为几个编译器构建一个 C 项目,其中一些是遗留编译器,它们似乎没有 link 时间内联支持,因此直接放置 static inline
函数似乎合乎逻辑在 header 秒内,实际上每个翻译单元都有自己的副本。
此外,我需要确保某些函数是内联的,以便在某些 low-level 中断处理程序中调用时不会调用其他函数(即更改 CPU 寄存器),所以它不是只是让编译器选择它是否会影响性能。
但是,我的一位同事告诉我这是一件不寻常的事情,我应该避免这样做。在项目的这一点上,我可能仍然可以重新安排所有内容,所以我想确认如果我们决定使用 header 内联,我们在长 运行 中是否可能会遇到一些问题?
来自 n1570(最新的 public C11 草案),§6.7.4:
- 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. The extent to which such suggestions are effective is implementation-defined.
下一节将详细介绍链接,但上面这段话基本上是 C 标准关于 inline
的全部内容。请注意这如何赋予实现所有自由,包括完全忽略 inline
.
因此,仅使用标准 C,您最终可能会得到以正常方式调用的函数的多个实例(每个翻译单元一个)。这通常不是您想要的,因为它结合了两个缺点(重复代码 和 函数调用的开销)。所以我认为标准 C inline
只对单个翻译单元私有的函数有用。即使那样,您也可以假设一个好的优化编译器会自动选择内联候选对象,而无需显式 inline
.
另一方面,如果您的编译器提供了一种方法来实际 强制 内联函数(根据您的评论,_inline
说明符对您的编译器有效),将这些函数放在 header 中是安全的。但请注意,它绝不是可移植的。
与 commented by cmaster 一样,您可以使用 function-like 宏 实现某种“手动内联”,而不是便携式解决方案。
根据对问题的评论,在头文件中定义 static _inline
函数是 对于这个特定编译器 的方法,编译器文档的引用是清楚这一点。
您以后可能会遇到的问题是它是特定编译器的特定扩展,不同于 standard inline
说明符。正如 Felix 所说,该标准允许实现选择它如何实现内联,特别是忽略说明符仍然是一致的:
Making a function an inline function suggests that calls to the function be as fast as possible. The extent to which such suggestions are effective is implementation-defined.
也就是说,语义似乎相同(来自 C99 的草案 1256 或 C11 的草案 1570):
6.7.4 Function specifiers
...
Any function with internal linkage can be an inline function. For a function with external linkage, the following restrictions apply: If a function is declared with an inline function specifier, then it shall also be defined in the same translation unit. If all of the file scope declarations for a function in a translation unit include the inline function specifier without extern, then the definition in that translation unit is an inline definition. An inline definition does not provide an external definition for the function, and does not forbid an external definition in another translation unit. An inline definition provides an alternative to an external definition, which a translator may use to implement any call to the function in the same translation unit. It is unspecified whether a call to the function uses the inline definition or the external definition
因此,由于常见的实现确实尊重 inline
说明符,我认为可移植性的风险是有限的
无需将函数声明为 static inline
,因为 ISO 9899
保证函数不会在您不使用存储时导出到链接器 class 说明符 extern
在翻译单元内的任何内联函数声明中。
换句话说,如果你简单地声明为inline aaa
,符号aaa
将在外部不可见。
我引用自6.7.4 Function specifiers
If all of the file scope declarations for a function in a translation unit include the inline function specifier without extern, then the definition in that translation unit is an inline definition. An inline definition does not provide an external definition for the function, and does not forbid an external definition in another translation unit.
emacs 在 lisp.h
中使用了这种内联技术,header 定义了 lisp objects,我引用了 emacs 的源代码:
Some operations are so commonly executed that they are implemented as macros, not functions, because otherwise runtime performance would suffer too much when compiling with GCC without optimization.
There's no need to inline everything, just the operations that would otherwise cause a serious performance problem.