空数组声明 - 奇怪的编译器行为
我在我必须维护的项目中发现了一段看起来很奇怪的代码。类中有一个空数组成员,不会导致编译器错误。我已经使用 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[]
是什么类型?
I've found a strange looking piece of code in a project I have to maintain. There's an empty array member of a class which doesn't lead to an compiler error. I've tested some variations of such a code with 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;
}
The error message at int i[]
is absolutely sensible to me. The code which is shown with class D
is well-formed standard C++. But what's about the classes A
, B
and C
? What kind of types are the member variables int i[]
in this classes?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
编辑:
您的疑问可以通过 的定义来解释语言的扩展,允许在结构/联合的末尾使用零大小的数组。我还没有尝试过,但是如果您在零大小的数组之后声明另一个成员,它应该会失败。
所以,如果你在堆栈上分配一个变量,你必须知道它的大小;该规则的例外是在结构/联合的末尾分配数组时,可能会出现一些 C 典型的技巧。
在 C++ 中,这会引发警告,因为默认的复制构造函数和赋值运算符可能不起作用。
上一个答案:
编译器会警告您,您正在尝试定义一个大小为零的数组。标准 C/C++ 中不允许这样做。
让我们逐个班级看看差异。
在 D 类中:
templatestruct D { 静态 int i[]; };
它之所以有效,是因为您只是声明了静态成员变量的类型。为了进行链接,您还需要在定义语句中定义实际的数组,就像您所做的那样:
在这里您还可以通过初始值设定项指定数组的大小。
对于 B 类,您正在做类似的事情,但定义是:
即,您不指定大小并收到警告。
对于 A 类,更多的是相同的,您定义了一个没有大小的数组类型的成员变量。
EDIT:
your doubt is explained by the definition of the extension to the language, which allows for zero-sized arrays at the end of structs/unions. I have not tried it, but if you declare another member after the zero-sized array, it should fail.
so, if you allocate a variable on the stack, you have to know its size; the exception to the rule is when allocating an array at the end of a struct/union, where some C-typical trickery is possible.
In c++ this raises a warning because the default copy constructor and assignment operator will probably not work.
PREVIOUS ANSWER:
The compiler warns you about the fact that you are trying to define an array with zero size. This is not allowed in standard C/C++.
Let's see the differences class by class.
In class D:
template<class T> struct D { static int i[]; };
it works because you are just declaring the type of a static member variable. For this to link, you need also defining the actual array, in a definition statement like you do:
here you also specify the size of the array through the initializer.
With class B, you are doing something similar, but the definition is:
i.e., you don't specify the size and get the warning.
With class A, more of the same, you are defining a member variable of type array without the size.
好一个。可以肯定的是,您想知道为什么编译器不将其标记为错误,对吗?在这种情况下,我认为这个问题在编译器中是不可预测的,但我知道这种情况一直在 MSVC 上发生。
http://support.microsoft.com/kb/98409
让我看看是否可以解释一下就像他们一样。如果我要像这样声明一个带有空数组的结构体,
编译器可能会为 x 分配 4 个字节,并可能为 char 指针分配另外 4 个字节。空将包含结构体 a 开头之后 4 个字节的地址。
由于它是一个无长度的字符数组,因此尝试访问它将会出错,因为没有尾随 0 来表示字符串的结尾。
我可以选择稍后初始化该结构以指向实际字符串的开头以克服此错误。
由于结构基本上是一个类,因此如果您确保它是一个聚合而不是一个完整的类,那么您可以对一个类执行相同的操作。
那么就这样吧。当在结构/类中声明时,MSVC 编译器将没有固定大小的数组视为指针。请记住,类定义只是声明。在您为其创建实例之前,编译器不会为其分配空间。当你开始思考它时,它就开始了。编译器如何知道您稍后是否计划为其分配存储空间。它成为一个运行时工件,但编译器仍然足够聪明,可以警告您该问题。
Good one. Just to be certain, you are wondering why the compiler isn't flagging it as an error right? In that case, I think this problem is unpredictable across compilers but I'm aware of this happening on MSVC all the time.
http://support.microsoft.com/kb/98409
Let me see if I can explain it like they did. If I were to declare a struct with an empty array like this,
the compiler might allocate 4 bytes for x and probably another 4 bytes for the char pointer. empty will contain the address 4 bytes past the start of struct a.
Since it is a character array of no length, trying to access it would be an error since there is no trailing 0 to signify the end of the string.
I could choose to initialize the struct later to point to the start of an actual string to overcome this error.
Since a struct is basically a class, turns out you can do the same thing with a class if you make sure its an aggregate and not a full class.
So there ya go. MSVC compilers treat arrays with no fixed sized as a pointer when declared within a struct/class. Remember that class definitions are merely just declarations. The compiler doesn't allocate space for them until you create an instance for it. When you start to think about it, it sorta makes since. How will the compiler know if you plan to allocate storage for it later. It becomes a run-time artifact but the compiler was still smart enough to warn you about the problem.