我在一个我必须维护的项目中发现了一段奇怪的代码。有一个类的空数组成员,不会导致编译器错误。我已经使用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 ++。但是关于课程A
,B
和C
的内容是什么?这个类中的成员变量int i[]
是什么类型的?
答案 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编译器将没有固定大小的数组视为指针。请记住,类定义仅仅是声明。在为其创建实例之前,编译器不会为它们分配空间。当你开始思考它时,它就会变得如此。如果您打算稍后为其分配存储,编译器将如何知道。它变成了运行时工件,但编译器仍然足够聪明,可以警告你这个问题。