在C ++中extern声明“T的未知数组”

时间:2017-12-16 05:30:20

标签: c++ c++11 language-lawyer linkage

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

克++

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

不同

2 个答案:

答案 0 :(得分:0)

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

@Bob__对我原来帖子的评论后更新。

我在我尝试的所有三个编译器上获得相同的结果(最多改变指针地址) - 谢谢,@ Bob__。既然所有这些都在编译,我们是否应该说它是需要更新的标准语言?

答案 1 :(得分:0)

从正式的角度来看,您错过的单词是

  

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

不说

  

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

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

如果这听起来不太令人信服,那么恕我直言,块作用域外部声明不是该标准中最受欢迎的功能。例如,参见this,那里的编译器实现的行为与声明的C ++ 11、14、17标准完全不同。最终在C ++ 17中,对于这种情况,具有内部和外部链接的实体被认为是错误的形式(编译器尚未实现)。

第二,从实际的角度来看,这就像是名称冲突:您想要在静态范围内拥有两个对象的静态存储持续时间,并且它们的名称在同一范围内。您为什么要这样做,即使达到了这个目的,您达到的实际目标是什么,对于代码的可读性来说,这太令人困惑了?为什么不将内部静态存储持续时间变量命名为与外部变量不同的名称呢?

很抱歉,我的帖子没有给出直接答案,而是提出了更多问题。感谢您提出一个有趣的案例,表明该标准在某些方面可能会更加清晰。