2011-07-07 11 views
9

私は、維持しなければならないプロジェクトで奇妙な見た目のコードを見つけました。コンパイラエラーを引き起こさないクラスの空の配列メンバがあります。私はMSVC 10.0で、このようなコードのいくつかのバリエーションをテストしてみた:int i[]空の配列宣言 - 奇妙なコンパイラの振る舞い

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; 
} 

エラーメッセージが私には絶対に賢明です。クラスDで示されているコードは整形式の標準C++です。しかし、クラスについてはどうですか?ABC?このクラスのメンバー変数int i[]はどんな種類の型ですか?

答えて

6

EDIT:

あなたの疑いが構造体/共用体の端にゼロサイズの配列を可能definition of the extension to the language、によって説明されます。私はそれを試していないが、ゼロサイズの配列の後に別のメンバーを宣言すると、失敗するはずです。

したがって、変数をスタックに割り当てる場合は、そのサイズを知る必要があります。ルールの例外は、構造体/共用体の終わりに配列を割り当てるときです。ここでは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

ありがとう、別の "いい" MicrosoftエクステンションがC++標準に... – 0xbadf00d

0

いいです。ちょうど確かに、あなたは、コンパイラがそれをエラーのように正しく示しているのではないかと疑問に思っていますか?その場合、この問題はコンパイラ間で予測できないと思いますが、私はMSVC上でこの問題が常に発生していることを認識しています。

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

彼らがしたように、私はそれを説明することができるかどうか、私は見てみましょう。私はこのような空の配列で構造体を宣言した場合、

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

コンパイラは、charポインタのX 4バイト、おそらく別の4つのバイトを割り当てるかもしれません。空文字列には構造体の先頭から4バイト先のアドレスが入ります。

長さのない文字配列なので、文字列の終わりを示す末尾の0がないため、文字列にアクセスしようとするとエラーになります。

このエラーを克服するために、後で実際の文字列の先頭を指すように構造体を初期化することができます。

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

構造体は基本的にクラスなので、完全なクラスではなく集約していることを確認すると、クラスで同じことができます。

だからそこに行く。 MSVCコンパイラは、構造体/クラス内で宣言されたときに、固定サイズの配列をポインタとして扱います。クラス定義は単なる宣言に過ぎないことに注意してください。コンパイラーは、インスタンスを作成するまで、スペースを割り当てません。あなたがそれについて考え始めると、そんなことが起きます。後でストレージを割り当てる予定があるかどうかは、コンパイラーがどのように認識しますか。それは実行時のアーティファクトになりますが、コンパイラはまだ問題について警告するのに十分なほどスマートです。