0

構造体ビットのパッキング順序の問題について読んでいますが、露出が限られているため、自分で遭遇したことはありません。ただし、これらの議論は主に非常に複雑なアプリケーションに関するものであったことに注意してください。

私は今、そのようなifstreamからの情報を保持するための構造体を書いています

struct MyFileStruct
{
    char data1[40];
    int data2;
    char data3[12];
    // etc..
};

ifstream fin;
// .. snip ..
fin.read((char*)&myfilestruct, sizeof(MyFileStruct));

そして、この単純なシナリオで、おそらく別の OS または 32/64 ビット アーキテクチャで問題が発生するかどうかを考えてみました。では、正確には、いつビット パッキングの順序が考慮されるのでしょうか?

4

4 に答える 4

3

その特定の構造体で発生する可能性が最も高い問題は、エンディアンです。リトルエンディアン システムでは、最下位アドレスのバイトに最下位 8 ビットが含まれますint。ビッグ エンディアン システムでは、上位 8 ビット。

したがって、ある種類のシステムでその構造体のバイトをファイルに書き込み、そのファイルを別の種類のシステムに転送して読み返すと、. に異なる値が表示されdata2ます。

ただし、他の構造体、または通常とは異なるシステム/コンパイラで発生する可能性のある問題は他にもあります。

  • 基本型のサイズ -int通常は 4 バイトですが、必須ではありません。long異なる一般的なシステムではサイズが異なります (Windows では 4 バイト、64 ビット Linux では 8 バイト)。ファイルから構造体を読み込もうとして、他の C++ 実装によって実際に書き込まれたものとは異なるバイト数を期待する場合、明らかに問題があります。
  • パディング - コンパイラは、未使用のバイトをメンバー間の構造体に入れることができます。これは一般に、位置合わせを確実にするために行われます。たとえば、多くのコンパイラでは、intメンバーのオフセットは常に 4 の倍数です。構造体ではとにかく 40 は 4 の倍数であるため、これは何の違いもありませんが、最初の配列が 39 バイトの場合、整列する実装int未使用のバイトが挿入され、挿入されなかったバイトは挿入されません。一部の CPU (x86 など) では、アラインメントは便利ですが必須ではありません。intその場合、コンパイラーは通常、構造体にパディングするかどうかを示すアノテーションを付ける方法を持っています。

これらの種類の違いが存在するため、構造体をファイル (またはソケット) に直接書き込むことは一般に正当ではありません。誰がそれを読んでもその構造体のメモリ表現がまったく同じであるという特定のケースでは、それを回避することができます。ファイルを読み書きする必要があるプログラムは、その形式で動作します。

于 2012-09-01T17:46:55.710 に答える
2

問題が発生する典型的な例は、構造体の配列をシリアル化する場合です。構造体のサイズが 12 で、4 バイト境界または 8 バイト境界にパックされているとします。ディスク上に 2 つの要素の配列がある場合、最初の要素はオフセット 0 から始まります。2 つ目は、位置 12 (4 バイト境界にパックする場合) または位置 16 (8 バイト境界にパックする場合) から開始します。したがって、配列を読み取ると、最初の要素は正しく表示されますが、2 番目の要素 (および後続の要素) は混乱する可能性があります。

Visual Studio では、デフォルトのパッキングは 32 ビットと 64 ビットの両方のコンパイルで 8 バイト境界までであるため、運が良ければ問題がないことに注意してください。ただし、これを確認したい場合は、32 ビット コンパイルを 4 バイト境界に揃えてコンパイルするように設定し、64 ビット コンパイルを 8 バイト境界に揃えてコンパイルするように設定します (たとえば)。次に、前の段落のように構造体の配列を作成します。

于 2012-09-01T18:01:34.590 に答える
1

パッキングルール(および同様に、エンディアン)は、例を含めて、次の場合に考慮事項になる可能性があります。

  • プログラムは別のコンパイラでコンパイルされています
  • プログラムは、同じコンパイラの異なるバージョンでコンパイルされます(理論的には)
  • プログラムは、ターゲットOS、ターゲットハードウェア、または32/64ビット設定の変更を含むがこれらに限定されないさまざまなコンパイラオプションでコンパイルされます。
  • コンパイラ指令は、などのソースコードに追加され#pragma packます。

安全な一般的なルールは、構造体を読み取る実行可能ファイルが同じ実行可能ファイルによって記述された場合にのみ、コードが機能することが保証されるということです。

これが懸念される場合、パッキングの問題(エンディアンではない)の一般的な解決策の1つは、非標準のコンパイラ指令を使用して、効率を犠牲にしてパッキングを削除することです。

これはpragma pack、Microsoftコンパイラおよび__attribute__ ((__packed__))gccで実行できます。

于 2012-09-01T17:38:34.477 に答える
0

一般的なルールは、同じコンパイラでコンパイルされたコード (およびコンパイラ オプションを含む) で記述したファイルを読み取ることができるということです。これの最も単純な形式は、後で読み込めるようにバイナリ データを書き出すプログラムです。それを超えると、実装固有の動作になり、簡単な答えはありません。

于 2012-09-01T17:23:11.797 に答える