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

问题描述

根据C18标准:

如果翻译中函数的所有文件范围声明 单位包括没有extern的内联函数说明符,则 该翻译单元中的定义是内联定义

然后我们阅读:

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

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

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

这是代码:

static int n = 5;

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

int main() {
    return 0;
}

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

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

如果我的结论是正确的(我不知道),则会出现另一个问题:为什么外部内联函数是没有用的东西?

解决方法

在问题中,您尝试在编译器中尝试尝试并推导语言规则。这通常是一个坏主意,因为(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()的定义不是内联定义,因为TU中的所有文件范围声明都不是正确的包括inline而不包含extern的说明符。 (该句子的目的是deny the antecedent)。

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

标准不是在说,或者至少我看不到它,是提供外部定义的内联函数是否停止成为内联函数

内联函数定义绝不是外部定义。在6.7.4 / 7“内联定义不为函数提供外部定义”中明确指出了这一点。

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

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

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

,

我觉得我需要回答自己,因为这比开始时想象的要复杂得多,而且自从我写下问题以来,在研究过程中出现了新的事实。这更像是我自己的结论,但我觉得自己是对的。所以我需要分享。反馈和确认/拒绝将不胜感激。

首先,请看下面的代码:

void inline foo() { return; }

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

这似乎是一个简单的代码,但事实是它没有编译。好吧,实际上,它可以编译,但是在链接器步骤中失败。为什么?让我们从标准中阅读完整的难以理解段落:

对于具有外部链接的功能 ,以下限制 apply:如果使用内联函数说明符声明了函数, 然后必须 ALSO 在同一个翻译单元中定义。如果全部 翻译单元中函数的文件范围声明 包括没有extern的内联函数说明符,然后 该翻译单元中的定义是内联定义。内联 定义不提供函数的外部定义, 并且不禁止在另一种翻译中使用外部定义 单元。 内联定义为外部定义提供了替代方法 定义,翻译人员可以使用该定义来实现对 功能在同一个翻译单元中。尚不确定 对函数的调用使用内联定义或外部 定义。

“未确定对函数的调用是使用内联定义还是使用外部定义” ,我们得到了为什么编译效果不佳(链接)的答案。我的实现(GCC)选择了外部版本。而且链接器不知道这种外部功能。

该标准规定内联定义“不禁止在另一个翻译单元中使用外部定义” 。实际上并没有,但是如果从当前的翻译单元调用该函数并且实现选择调用外部版本,它甚至强制在其他地方定义它。

然后,出现另一个问题:如果实现选择调用外部定义或内联定义,为什么必须同时定义两者?好吧,我在GCC文档中找到了答案:您永远都不知道何时选择一个。例如,当未指示优化器开关时,GCC选择外部版本。对于许多优化的代码配置,将选择内联版本。

关于为何内联外部函数可能无用的问题,实际上却并非如此。外部函数也可以内联。检查此文档:https://gcc.gnu.org/onlinedocs/gcc/Inline.html

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

但是,我认为关于外部内联函数的内联标准不是很清楚。例如,GCC所做的是:非静态内联函数不是内联函数,除非它们在函数的声明(而不是外部定义)中具有inlineextern指定符。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...