开发者

Empty array declaration - strange compiler behavior

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'开发者_StackOverflow中文版 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?


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:

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

here you also specify the size of the array through the initializer.

With class B, you are doing something similar, but the definition is:

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

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.


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,

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

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.

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

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜