2011-07-07 153 views
9

我在一个项目中发现了一段奇怪的代码,我必须维护它。有一个类的空数组成员,不会导致编译器错误。我测试过的这样的一些代码的变化与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[]是什么类型的?

回答

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,更多相同,您正在定义不带大小的数组类型的成员变量。

+0

问题是:为什么警告(与类'A','B'和'C'有关)警告而不是错误?在我看来,这与我在局部变量声明中得到的错误相比是不对称的。 – 0xbadf00d

+0

请参阅我的编辑。 – sergio

+0

谢谢,另一个“漂亮”的微软扩展到C++标准...... – 0xbadf00d

0

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

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

让我看看我能解释它像他们做到了。如果我声明一个结构与这样的一个空数组,

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

编译器可能分配4个字节为x和可能另外4个字节用于字符指针。空将包含结构a开始之后的4个字节的地址。

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

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

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

由于结构基本上是一类,原来你可以做同样的事情用一个类,如果你要确保它的聚集,而不是一个完整的类。

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