C++ 中“T 的未知边界数组”的外部声明

Posted

技术标签:

【中文标题】C++ 中“T 的未知边界数组”的外部声明【英文标题】:extern declaration of an "array of unknown bound of T" in C++ 【发布时间】:2018-05-30 06:45:54 【问题描述】:

我用-std=c++11 -Wall 用g++ (7.1) 和clang++ (xcode 9.0) 编译了以下程序并得到了结果:

g++

0x10052c050
0x10052c040
0x10052c040

叮当++

0x108b74024
0x108b74018
0x108b74018

表示extern int a[];static int a[3]; 声明了相同的实体,具有相同的链接(内部链接)。

//a.cpp
#include <stdio.h>
int a[3];
void f()

    printf("%p\n", (void*)a);
;
//b.cpp
extern void f();
static int a[3];
void g()

    printf("%p\n", (void*)a);
    extern int a[];
    printf("%p\n", (void*)a);

int main(int argc, char* argv[])

    f();
    g();
    return 0;

但是,在 C++11 [3.9/6] 中:

...数组对象的声明类型可能是一个未知大小的数组,因此在翻译单元中的某一点是不完整的,稍后会完成;这两个点的数组类型(“T 的未知边界数组”和“N T 的数组”)是不同的类型。 ...

int a[3];int a[];是不同的类型,

在 C++11 [3.5/6] 中:

如果存在具有相同名称和类型的链接的实体的可见声明,忽略在最内层封闭命名空间范围之外声明的实体,块范围声明声明相同的实体并且接收前一个声明的链接。如果有多个这样的匹配实体,则程序是非良构的。 否则,如果没有找到匹配的实体,则块范围实体接收外部链接。

“同名同类型”不兼容,所以extern int a[];不会收到之前声明的链接(static int a[3];), 因此,“否则,如果没有找到匹配的实体,则块范围实体接收外部链接。”兼容。

我的问题是:

为什么编译的结果与C++11标准的写法不一致? 或者如果我的理解是错误的,那是对的?

注意:这个问题不同于error: extern declaration of 'i' follows declaration with no linkage

【问题讨论】:

可能因为 a 被 odr-used 用于不完整的类型,所以程序格式不正确;不需要诊断,因此有奇怪的行为? @xskxzr 是的,我已经编辑过了,谢谢。 【参考方案1】:

“extern int a[]”没有完全定义“a”。

在@Bob__ 对我的原始帖子发表评论后更新。

我在我尝试过的所有三个编译器上都得到了相同的结果(直到指针地址发生变化)——谢谢@Bob__。既然都在编译,我们应该说是标准的语言需要更新吗?

【讨论】:

@Shankar:“既然他们都在编译,我们应该说它是需要更新的标准语言吗?”那么问题是,标准实际上所说的应该在这里发生什么?是否有其他相关的标准段落?标准是说委员会打算就这个程序说什么,还是这是标准的缺陷?根据当前标准的措辞,完全有可能所有 3 个编译器都是错误的,并且应该对所有 3 个编译器进行修补,而没有针对标准的缺陷解决方案。【参考方案2】:

从形式上看,你漏掉的词是

数组类型在这两点(“T 的未知边界数组”和“N T 的数组”)是不同的类型。

没说

数组类型(“T 的未知边界数组”和“N T 的数组”)是不同的类型。

因此,您示例中的两个 a 声明基本上都是数组类型。

如果这听起来不太令人信服,那么恕我直言,块作用域外部声明并不是标准中最流行的特性。例如,参见this,编译器实现的行为与 C++11、14、17 标准中所述的完全不同。最终在 C++17 中,在这种情况下,具有内部和外部链接的实体被认为是不正确的(编译器尚未实现)。

其次,从实际的角度来看,这感觉像是名称冲突:您希望在同一范围内拥有两个具有相同名称且具有静态存储持续时间的对象。你为什么要这样做,你通过这个实现的实际目标是什么,即使这是可能的,这对于代码的可读性来说会非常混乱?为什么不将您的内部静态存储持续时间变量命名为与外部变量不同的名称?

抱歉,我的帖子没有立即给出答案,而是提出了更多问题。感谢您带来一个有趣的案例,表明标准在某些方面可以更加清晰。

【讨论】:

以上是关于C++ 中“T 的未知边界数组”的外部声明的主要内容,如果未能解决你的问题,请参考以下文章

外部“C”是不是应包含 C++ 函数的声明或定义?

C 与 C++ 中的外部函数

C++ Visual Studio Windows:如何在 dll 中声明但不定义外部函数

node源码详解 —— 在main函数之前 —— js和C++的边界,process.binding

C++ 枚举类型声明可以放在 int main 的前面吗?

nvcc:结合外部和常量