外部链接内联函数真的是内联的吗?

Is an external linkage inline function really inline?

来自C18标准:

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.

然后我们阅读:

The declaration of an inline function with external linkage can result in either an external definition, or a definition available for use only within the translation unit. A file scope declaration with extern creates an external definition.

我写了一些代码来检查该函数是否实际上是内联的。我用这个限制来找出:

An inline definition of a function with external linkage shall not contain a definition of a modifiable object with static or thread storage duration, and shall not contain a reference to an identifier with internal linkage.

这是代码:

static int n = 5;

void inline foo() { n = 66; }
void inline foo();    // remove 'inline' in second version

int main() {
    return 0;
}

编译时我收到一条警告说内联函数正在使用静态对象,这意味着 foo() 实际上是一个内联函数,因此它提供了一个内联(非外部)定义.但是,当我从指示的行中删除 inline 说明符时,我不再收到警告。根据标准,它不是内联定义,所以我猜它提供了一个外部定义。

标准没有说明,或者至少我看不到的是,提供外部定义的内联函数是否不再是内联函数。根据我的测试,它确实不再是一个内联函数。

如果我的结论是正确的,我不知道,另一个问题出现了:为什么 extern inline 函数是无用的?

我觉得我需要自己回答,因为这比我一开始预想的还要复杂,而且自从我写这个问题以来,我的研究过程中出现了新的事实。这更像是我自己的结论,但我觉得我走在正确的道路上。所以我需要分享。反馈和 confirmation/rejection 将不胜感激。

首先,看一下这段代码:

void inline foo() { return; }

int main() {
    foo();
    return 0;
}

它可能看起来像一个简单的代码,但事实是它无法编译。嗯,实际上,它编译了,但是在 linker 步骤中失败了。为什么?让我们阅读标准中的完整 difficult-to-understand 段落:

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.

“未指定调用函数是使用内联定义还是外部定义”我们得到了为什么它没有编译的答案(link) 好吧。我的实现 (GCC) 选择了外部版本。而且 linker 不知道这样的外部函数。

标准说内联定义“不禁止在另一个翻译单元中使用外部定义”。实际上它没有,但如果从当前翻译单元调用函数 并且实现选择调用外部版本 .

,它甚至强制在其他地方定义它

那么,另一个问题出现了:如果实现选择调用外部定义,或者内联定义,为什么需要同时定义两者?嗯,我在 GCC 文档中找到了答案:你永远不知道什么时候会选择一个或另一个。例如,当没有指示优化器开关时,GCC 选择外部版本。对于许多优化的代码配置,将选择内联版本。

关于为什么内联外部函数可能无用的问题,实际上它们没有用。也可以内联外部函数。检查此文档:https://gcc.gnu.org/onlinedocs/gcc/Inline.html

可以使用外部内联函数并从其他翻译单元内联,只是不会创建内联定义。例如,内联定义仅在您希望拥有根据优化开关使用的函数的替代版本时才有用。

不过我觉得标准对外部内联函数的内联不是很清楚。例如,GCC 所做的是:non-static 内联函数不是内联函数,除非它们在函数的声明(而不是外部定义)中具有 inlineextern 说明符。

在这个问题中,您尝试在编译器中尝试推断语言规则。这通常是一个坏主意,因为 (a) 在许多情况下很难观察到违反规则的影响,并且 (b) 编译器可能会出现问题。相反,标准是语言规则的权威来源,因此应参考标准回答问题。


继续:您的代码包含您在问题中引用的 C11 6.7.4/3 的约束违规。违反约束的结果是编译器必须发出诊断,它确实这样做了。

然后你问一些修改,我假设你的意思是下面的代码:

static int n = 5;
void inline foo() { n = 66; }
void foo();
int main() { return 0; }

正如您引用的第一句话(来自 6.7.4/7)所涵盖的,foo() 的定义不是内联定义,因为并非所有 file-scope TU 中的声明包括没有 externinline 说明符。 (那句话的意思是deny the antecedent)。

由于不是内联定义,所以n = 66没有问题,代码正确

What the standard is not saying, or at least I cannot see it, is whether an inline function that provides an external definition stops being an inline function, or not

内联函数定义永远不是外部定义。这在6.7.4/7“内联定义不提供函数的外部定义”中有明确说明。

您可能会混淆“内联函数定义”和“带有 inline 说明符的函数定义”这两个概念。

another question arises: why an extern inline function is a useless thing?

如果您指的是关键字 extern inline,那是该问题未涉及的另一个主题,。带有外部链接的内联函数当然不是没用。