2

Visual Studio 2008 で C++ を使用しています。次のような構造があるとします。

    struct StructOfInts
    { 
        int a;
        int b;
        int c; 
    };

これは、次のように読み書きすることを意図しています。

    void Read( std::istream& is, StructOfInts& myStruct  )
    {
        is.read( (char*)&myStruct.a, sizeof myStruct.a );
        is.read( (char*)&myStruct.b, sizeof myStruct.b );
        is.read( (char*)&myStruct.c, sizeof myStruct.c );
    }
    void Write( std::ostream& os, StructOfInts& myStuct )
    {
        os.write( (char*)&myStruct, sizeof myStruct );
    }

上記のコードは、ファイルの読み取りまたは書き込み時に何らかのメモリ破損を引き起こす可能性がありますか? メモリの破損とは、誤った値が読み込まれていることを意味します。読み込まれている -1.#QNB 値のソースを特定しようとしていますが、これが原因ではないかと考えています。また、pragma pack を使って構造体を pack すると違いはありますか?

4

2 に答える 2

2

はい、フィールド間のパディングの可能性があるため、コードによって無効な値が読み込まれる可能性がありstructます。の例を使用しstruct StructOfIntsて、コンパイラが次のようにフィールド間にパディングを挿入するとします。

byte  | 0 1 2 3 | 4 5     | 6 7 8 9 | 10 11 12 13
value | field a | padding | field b | field c

次に、構造体をストリームに書き込むと、次のような結果になる可能性があります

byte | 0  1  2  3   | 4   5   | 6  7  8  9   | 10 11 12 13
char | \0 \0 \0 'a' | '?' '?' | \0 \0 \0 'b' | \0 \0 \0 'c'

フィールドに (それぞれ) 値が含まれている場合(int)'a', (int)'b', (int)'c'

次に、値を読み戻すと、次のようになります

myStruct->a = int version of \0 \0 \0 'a'
myStruct->b = int version of '?' '?' \0 \0
myStruct->c = int version of \0 'b' \0 \0

これは明らかにあなたが望むものではありません。

について調べたところ#pragma pack、このケースに役立つようです。コンパイラはパディングを挿入しません (実装定義です...)。したがって、値は (ほとんどの場合) 正しく読み書きされます。

また、別のこと: あるシステム (コンピューター/OS/コンパイラー) で書き込みを行い、別のシステムでデータを読み取ると、エンディアンの問題も問題を引き起こす可能性があります。

于 2013-07-03T17:16:49.827 に答える
1

このケースをテストする簡単な方法は次のとおりです。

static_assert(sizeof(StructOfInts) == (3 * sizeof(int)), "size mismatch");

これを実現する最善の方法 (IMO) は、対称形式を使用することです。つまり、フィールドごとにシリアライズしてから、フィールドごとにデシリアライズします。

つまり、実装で使用する動作に依存することは、標準 (BAD) ではなく、ターゲット アーキテクチャの ABI に依存することです。したがって、それは「腐敗」につながる可能性があります。

構造体のサイズは ABI によって異なる可能性があり、int のサイズやバイト オーダーでさえも異なる可能性があり、結果として「破損」が発生します。パディングとアラインメントも ABI によって指定されます。

そのため、固定幅の型、明示的なエンディアン、およびフィールドごとの対称シリアル化が必要になることがよくあります。

于 2013-07-03T17:22:53.613 に答える