空数组声明 - 奇怪的编译器行为

时间:2011-07-07 07:18:33

标签: c++ arrays standards

我在一个我必须维护的项目中发现了一段奇怪的代码。有一个类的空数组成员,不会导致编译器错误。我已经使用MSVC 10.0测试了这些代码的一些变体:

template<class T> struct A {
    int i[];
}; // warning C4200: nonstandard extension used : zero-sized array in struct/union

template<class T> struct B { static int i[]; };
template<class T> int B<T>::i[];

struct C {
    int i[];
}; //warning C4200: nonstandard extension used : zero-sized array in struct/union

template<class T> struct D { static int i[]; };
template<class T> int D<T>::i[4];
template<>        int D<int>::i[] = { 1 };


int main()
{
    A<void> a;
    B<void> b;
    C c;
    D<void> d0;
    D<int>  d1;

    a.i[0] = 0;     // warning C4739: reference to variable 'a' exceeds its storage space

    b.i[0] = 0;     // warning C4789: destination of memory copy is too small

    c.i[0] = 0;     // warning C4739: reference to variable 'c' exceeds its storage space

    int i[];        // error C2133: 'i' : unknown size

    d0.i[0] = 0;    // ok
    d0.i[1] = 0;    // ok

    return 0;
}

int i[]的错误消息对我来说绝对明智。用类D显示的代码是格式良好的标准C ++。但是关于课程ABC的内容是什么?这个类中的成员变量int i[]是什么类型的?

2 个答案:

答案 0 :(得分:6)

编辑:

您的疑问由definition of the extension to the language解释,它允许结构/联合的末尾的零大小的数组。我没有尝试过,但如果你在零大小的数组之后声明另一个成员,它应该会失败。

所以,如果你在堆栈上分配一个变量,你必须知道它的大小;规则的例外是在struct / union的末尾分配一个数组时,可能会有一些C典型的技巧。

在c ++中,这会引发警告,因为默认的复制构造函数和赋值运算符可能不起作用。

以前的答案:

编译器警告您,您正在尝试定义一个零大小的数组。标准C / C ++中不允许这样做。

让我们按类看到差异。

在D组:

template<class T> struct D { static int i[]; };

它的工作原理是因为您只是声明静态成员变量的类型。要进行链接,还需要在定义语句中定义实际数组:

 template<>        int D<int>::i[] = { 1 };

在这里,您还可以通过初始化程序指定数组的大小。

对于B类,你正在做类似的事情,但定义是:

 template<class T> int B<T>::i[];

即,您没有指定尺寸并获得警告。

对于类A,更多相同,您正在定义一个不带大小的数组类型的成员变量。

答案 1 :(得分:-1)

好的。只是要确定,你想知道为什么编译器没有将它标记为错误对吗?在这种情况下,我认为这个问题在编译器中是不可预测的,但我知道这种情况一直发生在MSVC上。

http://support.microsoft.com/kb/98409

让我看看我能不能像他们那样解释。如果我要声明一个像这样的空数组的结构,

struct a
{
   int x;
   char empty[];
};

编译器可能为x分配4个字节,并且可能为char指针分配另外4个字节。 empty将包含结构a开头之后的4个字节的地址。

由于它是一个没有长度的字符数组,因此尝试访问它将是一个错误,因为没有尾随0来表示字符串的结尾。

我可以选择稍后初始化struct以指向实际字符串的开头来克服此错误。

struct a myStruct = { 1, "hello world"}; // empty now points to the start of "hello world"

由于struct基本上是一个类,如果你确定它是一个聚合而不是一个完整的类,你可以对一个类做同样的事情。

所以你去吧。当在struct / class中声明时,MSVC编译器将没有固定大小的数组视为指针。请记住,类定义仅仅是声明。在为其创建实例之前,编译器不会为它们分配空间。当你开始思考它时,它就会变得如此。如果您打算稍后为其分配存储,编译器将如何知道。它变成了运行时工件,但编译器仍然足够聪明,可以警告你这个问题。