C语言 没有“静态”或“外部”的“内联”在 C99 中有用吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6312597/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is "inline" without "static" or "extern" ever useful in C99?
提问by Sven Marnach
When I try to build this code
当我尝试构建此代码时
inline void f() {}
int main()
{
f();
}
using the command line
使用命令行
gcc -std=c99 -o a a.c
I get a linker error (undefined reference to f). The error vanishes if I use static inlineor extern inlineinstead of just inline, or if I compile with -O(so the function is actually inlined).
我收到链接器错误(对 的未定义引用f)。如果我使用static inlineorextern inline而不是 just inline,或者如果我编译-O(因此该函数实际上是内联的),错误就会消失。
This behaviour seems to be defined in paragraph 6.7.4 (6) of the C99 standard:
这种行为似乎在 C99 标准的第 6.7.4 (6) 段中定义:
If all of the file scope declarations for a function in a translation unit include the
inlinefunction specifier withoutextern, 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不带的函数说明符extern,则该翻译单元中的定义是内联定义。内联定义不为函数提供外部定义,也不禁止另一个翻译单元中的外部定义。内联定义提供了外部定义的替代方案,翻译器可以使用它来实现对同一翻译单元中的函数的任何调用。未指定对函数的调用是使用内联定义还是外部定义。
If I understand all this correctly, a compilation unit with a function defined inlineas in the above example only compiles consistently if there is also an external function with the same name, and I never know if my own function or the external function is called.
如果我正确理解了所有这些,则具有inline如上示例中定义的函数的编译单元仅在还有同名的外部函数时才能一致地编译,并且我永远不知道是否调用了我自己的函数或外部函数。
Isn't this behaviour completely daft? Is it ever useful to define a function inlinewithout staticor externin C99? Am I missing something?
这种行为是不是完全愚蠢?在inline没有static或extern在 C99 中定义函数是否有用?我错过了什么吗?
Summary of answers
答案摘要
Of course I was missing something, and the behaviour isn't daft. :)
当然,我遗漏了一些东西,而且这种行为并不愚蠢。:)
As Nemo explains, the idea is to put the definitionof the function
正如Nemo 解释的那样,这个想法是把函数的定义
inline void f() {}
in the header file and only a declaration
在头文件中,只有一个声明
extern inline void f();
in the corresponding .c file. Only the externdeclaration triggers the generation of externally visible binary code. And there is indeed no use of inlinein a .c file -- it's only useful in headers.
在相应的 .c 文件中。只有extern声明会触发外部可见的二进制代码的生成。inline在 .c 文件中确实没有使用- 它只在标题中有用。
As the rationale of the C99 committee quoted in Jonathan's answerexplicates, inlineis all about compiler optimisations that require the definition of a function to be visible at the site of a call. This can only be achieved by putting the definition in the header, and of course a definition in a header must not emit code everytime it is seen by the compiler. But since the compiler is not forced to actually inline a function, an external definition must exist somewhere.
正如乔纳森的回答中引用的 C99 委员会的基本原理所解释的那样,inline都是关于编译器优化,要求函数的定义在调用站点可见。这只能通过将定义放在头文件中来实现,当然头文件中的定义不能每次被编译器看到时都发出代码。但是由于编译器实际上并没有被强制内联函数,因此外部定义必须存在于某处。
采纳答案by Nemo
Actually this excellent answer also answers your question, I think:
其实这个优秀的答案也回答了你的问题,我认为:
The idea is that "inline" can be used in a header file, and then "extern inline" in a .c file. "extern inline" is just how you instruct the compiler which object file should contain the (externally visible) generated code.
这个想法是可以在头文件中使用“inline”,然后在 .c 文件中使用“extern inline”。“extern inline”就是您指示编译器哪个目标文件应该包含(外部可见)生成的代码的方式。
[update, to elaborate]
[更新,详细说明]
I do not think there is any use for "inline" (without "static" or "extern") in a .c file. But in a header file it makes sense, and it requires a corresponding "extern inline" declaration in some .c file to actually generate the stand-alone code.
我认为 .c 文件中的“内联”(没有“静态”或“外部”)没有任何用处。但是在头文件中它是有道理的,并且它需要在某些 .c 文件中进行相应的“extern inline”声明才能实际生成独立代码。
回答by Jonathan Leffler
From the standard (ISO/IEC 9899:1999) itself:
从标准 (ISO/IEC 9899:1999) 本身:
Appendix J.2 Undefined Behaviour
- ...
- A function with external linkage is declared with an
inlinefunction specifier, but is not also defined in the same translation unit (6.7.4).- ...
附录 J.2 未定义行为
- ...
- 具有外部链接的
inline函数用函数说明符声明,但不在同一翻译单元 (6.7.4) 中定义。- ...
The C99 Committee wrote a Rationale, and it says:
C99 委员会写了一个基本原理,它说:
6.7.4 Function specifiers
A new feature of C99:The
inlinekeyword, adapted from C++, is a function-specifierthat can be used only in function declarations. It is useful for program optimizations that require the definition of a function to be visible at the site of a call. (Note that the Standard does not attempt to specify the nature of these optimizations.)Visibility is assured if the function has internal linkage, or if it has external linkage and the call is in the same translation unit as the external definition. In these cases, the presence of the
inlinekeyword in a declaration or definition of the function has no effect beyond indicating a preference that calls of that function should be optimized in preference to calls of other functions declared without theinlinekeyword.Visibility is a problem for a call of a function with external linkage where the call is in a different translation unit from the function's definition. In this case, the
inlinekeyword allows the translation unit containing the call to also contain a local, or inline, definition of the function.A program can contain a translation unit with an external definition, a translation unit with an inline definition, and a translation unit with a declaration but no definition for a function. Calls in the latter translation unit will use the external definition as usual.
An inline definition of a function is considered to be a different definition than the external definition. If a call to some function
funcwith external linkage occurs where an inline definition is visible, the behavior is the same as if the call were made to another function, say__func, with internal linkage. A conforming program must not depend on which function is called. This is the inline model in the Standard.A conforming program must not rely on the implementation using the inline definition, nor may it rely on the implementation using the external definition. The address of a function is always the address corresponding to the external definition, but when this address is used to call the function, the inline definition might be used. Therefore, the following example might not behave as expected.
inline const char *saddr(void) { static const char name[] = "saddr"; return name; } int compare_name(void) { return saddr() == saddr(); // unspecified behavior }Since the implementation might use the inline definition for one of the calls to
saddrand use the external definition for the other, the equality operation is not guaranteed to evaluate to 1 (true). This shows that static objects defined within the inline definition are distinct from their corresponding object in the external definition. This motivated the constraint against even defining a non-constobject of this type.Inlining was added to the Standard in such a way that it can be implemented with existing linker technology, and a subset of C99 inlining is compatible with C++. This was achieved by requiring that exactly one translation unit containing the definition of an inline function be specified as the one that provides the external definition for the function. Because that specification consists simply of a declaration that either lacks the
inlinekeyword, or contains bothinlineandextern, it will also be accepted by a C++ translator.Inlining in C99 does extend the C++ specification in two ways. First, if a function is declared
inlinein one translation unit, it need not be declaredinlinein every other translation unit. This allows, for example, a library function that is to be inlined within the library but available only through an external definition elsewhere. The alternative of using a wrapper function for the external function requires an additional name; and it may also adversely impact performance if a translator does not actually do inline substitution.Second, the requirement that all definitions of an inline function be “exactly the same” is replaced by the requirement that the behavior of the program should not depend on whether a call is implemented with a visible inline definition, or the external definition, of a function. This allows an inline definition to be specialized for its use within a particular translation unit. For example, the external definition of a library function might include some argument validation that is not needed for calls made from other functions in the same library. These extensions do offer some advantages; and programmers who are concerned about compatibility can simply abide by the stricter C++ rules.
Note that it is notappropriate for implementations to provide inline definitions of standard library functions in the standard headers because this can break some legacy code that redeclares standard library functions after including their headers. The
inlinekeyword is intended only to provide users with a portable way to suggest inlining of functions. Because the standard headers need not be portable, implementations have other options along the lines of:#define abs(x) __builtin_abs(x)or other non-portable mechanisms for inlining standard library functions.
6.7.4 函数说明符
C99的一个新的特点:该
inline关键字,改编自C ++,是一个函数说明符只能在函数声明被使用。它对于需要函数定义在调用站点可见的程序优化很有用。(请注意,标准并未尝试指定这些优化的性质。)如果函数具有内部链接,或者如果它具有外部链接并且调用与外部定义在同一个翻译单元中,则可以确保可见性。在这些情况下,
inline关键字在函数的声明或定义中的存在除了表明应该优化该函数的调用优先于其他没有inline关键字声明的函数的调用之外,没有任何影响。对于具有外部链接的函数调用,可见性是一个问题,其中调用位于与函数定义不同的翻译单元中。在这种情况下,
inline关键字允许包含调用的翻译单元也包含函数的本地或内联定义。一个程序可以包含一个带有外部定义的翻译单元、一个带有内联定义的翻译单元和一个带有声明但没有函数定义的翻译单元。后一个翻译单元中的调用将照常使用外部定义。
函数的内联定义被认为是与外部定义不同的定义。如果调用某个
func具有外部链接的函数时,内联定义可见,则行为与调用另一个__func具有内部链接的函数(例如 )相同 。符合要求的程序不得依赖于调用哪个函数。这是标准中的内联模型。符合要求的程序不能依赖于使用内联定义的实现,也不能依赖于使用外部定义的实现。函数的地址总是对应于外部定义的地址,但是当这个地址用于调用函数时,可能会使用内联定义。因此,以下示例可能不会按预期运行。
inline const char *saddr(void) { static const char name[] = "saddr"; return name; } int compare_name(void) { return saddr() == saddr(); // unspecified behavior }由于实现可能对其中一个调用使用内联定义,而对另一个调用
saddr使用外部定义,因此不能保证相等运算的计算结果为 1 (true)。这表明内联定义中定义的静态对象与其在外部定义中的对应对象不同。这促使约束甚至反对定义const这种类型的非对象。内联被添加到标准中,这样它就可以用现有的链接器技术实现,并且 C99 内联的一个子集与 C++ 兼容。这是通过要求恰好一个包含内联函数定义的翻译单元指定为为函数提供外部定义的翻译单元来实现的。因为该规范仅由缺少
inline关键字或同时包含inlineand的声明组成extern,所以它也将被 C++ 翻译器接受。C99 中的内联确实以两种方式扩展了 C++ 规范。首先,如果一个函数是
inline在一个翻译单元中声明的 ,那么它不需要inline在所有其他翻译单元中声明。例如,这允许在库中内联但只能通过其他地方的外部定义使用的库函数。为外部函数使用包装函数的替代方法需要一个额外的名称;如果翻译器实际上不进行内联替换,它也可能对性能产生不利影响。其次,内联函数的所有定义“完全相同”的要求被以下要求所取代:程序的行为不应取决于调用是使用可见的内联定义还是外部定义实现的功能。这允许内联定义专门用于特定翻译单元中的使用。例如,库函数的外部定义可能包括一些参数验证,这些验证对于从同一库中的其他函数进行的调用是不需要的。这些扩展确实提供了一些优势;而关心兼容性的程序员可以简单地遵守更严格的 C++ 规则。
请注意,这是没有合适的实现来提供的标准头的标准库函数内置定义,因为这会打破一些旧的代码,包括它们的头后redeclares标准库函数。该
inline关键字仅旨在为用户提供一种可移植的方式来建议函数内联。因为标准头不需要是可移植的,所以实现还有其他选项:#define abs(x) __builtin_abs(x)或其他用于内联标准库函数的不可移植机制。
回答by Fred Foo
> I get a linker error (undefined reference to f)
> 我收到链接器错误(对 的未定义引用f)
Works here: Linux x86-64, GCC 4.1.2. May be a bug in your compiler; I don't see anything in the cited paragraph from the standard that forbids the given program. Note the use of ifrather than iff.
在这里工作:Linux x86-64,GCC 4.1.2。可能是您的编译器中的错误;我在禁止给定程序的标准中引用的段落中没有看到任何内容。注意使用if而不是iff。
An inline definition provides an alternative to an external definition, which a translator mayuse to implement any call to the function in the same translation unit.
内联定义提供了外部定义的替代方案,翻译器可以使用它来实现对同一翻译单元中的函数的任何调用。
So, if you know the behavior of the function fand you want to call it in a tight loop, you may copy-paste its definition into a module to prevent function calls; or, you may provide a definition that, for the purposes of the current module, is equivalent (but skips input validation, or whatever optimization you can imagine). The compiler writer, however, has the option of optimizing for program size instead.
所以,如果你知道函数的行为f并且你想在一个紧密的循环中调用它,你可以将它的定义复制粘贴到一个模块中以防止函数调用;或者,您可以提供一个定义,就当前模块而言,该定义是等效的(但跳过输入验证或您可以想象的任何优化)。然而,编译器编写者可以选择优化程序大小。

