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

Pep*_*Pep 5 c inline extern language-lawyer

来自 C18 标准:

如果翻译单元中函数的所有文件范围声明都包含不带 extern 的内联函数说明符,则该翻译单元中的定义内联定义

然后我们读到:

带有外部链接的内联函数的声明可以导致外部定义或仅在翻译单元内可用的定义。带有 extern 的文件范围声明会创建一个外部定义。

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

具有外部链接的函数的内联定义不应包含具有静态或线程存储持续时间的可修改对象的定义,并且不应包含对具有内部链接的标识符的引用。

这是代码:

static int n = 5;

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

int main() {
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

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

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

如果我的结论是正确的(我不知道),那么就会出现另一个问题:为什么 extern 内联函数是无用的东西?

M.M*_*M.M 3

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


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

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

static int n = 5;
void inline foo() { n = 66; }
void foo();
int main() { return 0; }
Run Code Online (Sandbox Code Playgroud)

正如您引用的第一句话(来自 6.7.4/7)所涵盖的, 的定义foo()不是内联定义,因为 TU 中的所有文件范围声明并不包含inline不带 的说明符extern。(这句话的目的是否认先行词)。

由于它不是内联定义,因此没有问题并且n = 66代码是正确的。

标准没有说,或者至少我看不到它,是提供外部定义的内联函数是否不再是内联函数

内联函数定义绝不是外部定义。这在 6.7.4/7“内联定义不为函数提供外部定义”中有明确规定。

也许您的困惑是由于将“内联函数定义”和“带有说明符的函数定义”概念混为一谈inline

另一个问题出现了:为什么外部内联函数是无用的东西?

如果您指的extern inline是本问题未涉及的另一个主题的关键字,请参阅此处。具有外部链接的内联函数当然不是没有用的。