web-dev-qa-db-ja.com

コードを壊さずに「構造体/共用体のゼロサイズの配列」警告(C4200)を正しく修正するにはどうすればよいですか?

いくつかのコードをライブラリに統合しています。これは、速度が十分に最適化された複雑なデータ構造であるため、あまり変更しないようにしています。統合プロセスはうまくいき、実際にはほぼ終了しています(コンパイルされます)。一つだけ私を悩ませています。 C4200の警告が複数回表示されます。

warning C4200: nonstandard extension used : zero-sized array in struct/union
Cannot generate copy-ctor or copy-assignment operator when UDT contains a zero-sized array

コードは機能しますが、この警告は私に忍び寄ります(特にcopy-ctorのある部分)。次のように宣言された構造のため、警告が表示されます。

#pragma pack( Push )
#pragma pack( 1 )
// String
struct MY_TREEDATSTR
{
    BYTE btLen;
    DWORD dwModOff;
    BYTE btPat[0];
};

typedef MY_TREEDATSTR TREEDATSTR;
typedef MY_TREEDATSTR *PTREEDATSTR;

#pragma pack( pop )

btPat[0]に注意してください。コードを壊したり、コードをあまり変更したりせずに、この警告を簡単に正しく取り除く方法はありますか? #pragmaに注意してください。この警告によると、何か意味がありますか?そして、なぜ構造がこのように宣言されているのですか? (私が理解しているのは、#pragmaではなく、btPatのことです)。

注:私は この同様の質問 を見ましたが、それは本当に私を助けませんでした。

更新:私が言ったように、コードは機能し、正しい結果をもたらします。したがって、コピーコンストラクタまたは代入演算子は明らかに実際には必要ありません。そして、私がコードを見ると、どの構造もmemcpy-edされません。

19
PeterK

これがMSVCコンパイラ(警告メッセージで通知される内容)の場合は、#pragma警告を使用してこの警告を無効にできます。

#pragma warning( Push )
#pragma warning( disable : 4200 )
struct _TREEDATSTR
{
    BYTE btLen;
    DWORD dwModOff;
    BYTE btPat[0];
};
#pragma warning( pop )

ところで、copy-constructorに関するメッセージは気味が悪いわけではありませんが、良いことです btPatの不明なバイトがないと_TREEDATSTRのインスタンスをコピーできないことを意味します:コンパイラはどのようにかわかりませんbig _TREEDATSTRは実際には(サイズが0の配列であるため)、したがってコピーコンストラクターの生成を拒否します。これは、これを行うことができないことを意味します。

_TREEDATSTR x=y;

とにかく動作しないはずです。

15

代わりにbtPat[1]と言うように変更してみてください。 C++とCの両方の標準では、配列に0個の要素を含めることはできないと規定されていると思います。 _TREEDATSTR構造体自体のサイズに依存するコードでは問題が発生する可能性がありますが、通常、これらの種類の構造体はバッファーからタイプキャストされ、(この場合)バッファーの最初のバイトが実際のバイト数を決定します。 btPatで。この種のアプローチは、C配列の境界チェックがないという事実に依存しています。

3
dreamlax

このC(アンチ)パターンの主なアイデアは、_TREEDATSTR要素に必要な追加メモリを取得することです。言い換えると、割り当てはmalloc(sizeof(_TREEDATSTR)+ len)で行われます。

プラグマパックは、フィールド間に空のスペースを残さないようにコンパイラーに要求するために使用されます(通常、コンパイラーは、多くの最新のプロセッサーでは速度が大幅に向上するため、配列のフィールド間に未使用のバイトを残すことがあります)。

ただし、アラインされていないアクセスが遅いだけでなく、完全に禁止されている(segfault)アーキテクチャがあるため、これらのコンパイラはプラグマパックを自由に無視できることに注意してください。プラグマパックを使用するコードは本質的に移植性がありません。

構造の最初にdwordを配置したと思いますが、これにはプラグマパックは必要なかったでしょう。また、警告を消音する方法は、1つの要素配列を割り当て、(len-1)余分なバイトを使用して割り当てを行うことです。

C++では、これらすべてのものは非常に危険です。なぜなら、基本的にコンパイラをだまして、オブジェクトのサイズが実際よりも小さいと思い込ませ、C++がコピーロジック言語であることを考えると、これはトラブルを求めることを意味します(たとえばオブジェクトの最初の部分にのみ作用するコピー構築および割り当て機能の場合)。日常の使用では、たとえばstd :: vectorを代わりに使用する方がはるかに優れていますが、もちろんこれは高額になります(二重間接参照、すべての_TREEDATSTRインスタンスのメモリが増えます)。

私は通常、他のすべてのプログラマーがばかだと思うのは好きではないので、この種の悪いトリックが使用された場合、おそらくそれには十分な理由があります...しかし、決定的な判断には、はるかに詳細な検査が必要になります。

要約する:

  1. 配列の最後にゼロ要素配列を使用することは、可変サイズのオブジェクトを作成するために使用されるトリックです。割り当ては、sizeof(structure)+ n * sizeof(array_element)バイトを要求することによって行われます。
  2. プラグマパックは、構造体フィールド間に余分なパディングバイトを追加しないようにコンパイラーに指示するために使用されます。これは、メモリレイアウトを正確に制御する必要がある場合に必要です(たとえば、これらのオブジェクトが手書きのアセンブリによってアクセスされているため)。
  3. あなたが本当にそれを必要とし、あなたが何をしているのかを知っているのでない限り、C++でそれをしないでください

コードがダーティで再生したいので、警告を「正しく」沈黙させる方法はありません(そして、C++コンパイラはオブジェクトサイズにだまされたくないのです)。このオブジェクトを他のオブジェクト内で使用する場合、または他のオブジェクトのベースとして使用する場合、または値でそれを渡す場合は、どんな悪いことが起こってもそれを要求します。

2
6502

コピーコンストラクターと代入演算子関数について不平を言っているなら、あなたはあなた自身のものを提供することができませんでした。それらが必要ない場合は、非公開として宣言してください。

これは、気付かずに割り当てまたはコピーしている場合、コードの他の場所で多くのエラーを生成する可能性があります。その場合、自動生成されたものがないため、とにかく機能しませんでした。

1
Brian Hooper

これは古いスレッドであることに気づきましたが、OPの質問に対する純粋なc ++ 11ソリューションを提供したいと思います。アイデアは、次のように、割り当てられるオブジェクトをラップし、パディングを追加して、配列内のオブジェクトを2つのアドレスの累乗に揃えることです。

template<typename T, std::size_t ObjectPaddingSize>
struct PaddedType : private T { private: char padding [ ObjectPaddingSize ]; };

template<typename T> // No padding.
struct PaddedType<T, 0> : private T { };

template<typename T>
struct PaddedT : private PaddedType<T, NextPowerOfTwo<sizeof ( T )>::value - sizeof ( T )> { };

オブジェクトのパディングサイズは、コンパイル時に次のクラスで計算できます(Lが2の累乗の場合はLを返し、それ以外の場合は2 gt Lの次の累乗を返します)。

template<std::size_t L>
class NextPowerOfTwo {

    template <std::size_t M, std::size_t N>
    struct NextPowerOfTwo1 {

        enum { value = NextPowerOfTwo1<N, N & ( N - 1 )>::value };
    };

    template <std::size_t M>
    struct NextPowerOfTwo1<M, 0> {

        enum { value = M << 1 };
    };

    // Determine whether S is a power of 2, if not dispatch.

    template <std::size_t M, std::size_t N>
    struct NextPowerOfTwo2 {

        enum { value = NextPowerOfTwo1<M, M>::value };
    };

    template <std::size_t M>
    struct NextPowerOfTwo2<M, 0> {

        enum { value = M };
    };

public:

    enum { value = NextPowerOfTwo2<L, L & ( L - 1 )>::value };
};
0
degski