none
構造体のパディングについて RRS feed

  • 質問

  • お世話になっております。

    基本的なことだと思いますが、構造体のパディングについて質問があります。

    現在、32bitのソースを64bit化する作業を行っています。

    調査したところ、ソース中に構造体を丸ごとmemcpy()している箇所が見つかりました。32bitと64bit環境では構造体のアライメントが異なるため、その点を考慮したソースに変更しようと考えております。

    そこで、使用している構造体のサイズを32bitと64bitで取得したところ、4バイト/8バイト境界ではない値が取得されました。

    例えば、以下のような構造体の場合です。

    struct Ex{

        BYTE val_b[5];

        char val_c;

    };

    上記の構造体のサイズををsizeof()で取得すると、32bit/64bit環境ともに6バイトが取得されます。

    私はval_cの後に2バイトパディングされ、8バイトになるものと思っていました。

    また、以下の構造体の場合は、32bit環境では8バイト、64bit環境では16バイトが取得され、アライメント通りに取得されました。

    struct hoge{

       char val_c;

       void* val_v;

    }

    struct hoge2{

       void* val_v;

       char val_c;

    };

     

    初めの構造体で6バイトが返ってくる理由はなんでしょうか?なぜ4バイト/8バイト境界にならないのでしょうか?

    ※構造体内に配列があるとアライメント通りにならない??

    環境はVisualStudio2010です。構造体のアライメントは、設定で変更しておらず、「既定」を指定しております。

    2010年7月21日 3:07

回答

  • /Zp (構造体メンバの配置) - MSDN より
    このオプションでは、バイト数も指定できます。バイト数を指定すると、2 番目以降の構造体メンバは、メンバ型のサイズか n バイト境界 (n は 1、2、4、8、または 16 のいずれか) のどちらか小さい方に格納されます。
    書いてある通り、メンバの型サイズかコンパイルオプションで指定されたものの小さいほうですので、

    struct {
     char a[5];
     char b;
    };

    struct {
     char a[5];
     char b[3];
    };
    の様に続くメンバが1バイトの場合、コンパイルオプションにかかわらずパディングされません。

    struct {
     char a;
     short b;
    };
    では、/Zp1でパディングを行わせない場合を除き、aとbの間に1バイトのパディングが行われるだけです。
    (shortのサイズ2の倍数に置かれるため、1バイトパディングされます。)

    • 回答としてマーク ofen 2010年7月21日 5:19
    2010年7月21日 3:42
  • アラインメントのしくみについては、次の記事がとても詳しいです。

     データ型のアラインメントとは何か,なぜ必要なのか?
     http://www5d.biglobe.ne.jp/~noocyte/Programming/Alignment.html

    構造体 Ex の方は、メンバーが BYTE と char なので 1 バイト アラインメントになるが、
    構造体 hoge と hoge2 の方は、メンバーに void* があるため、ポインターのサイズ アラインメント (32bit / 64bit) でないと困る、ということでしょうか。

    個人的な意見としては、プロセスの境界をまたぐ (シリアライズとか通信とか) 以外の部分については、
    64bit に移行するからといって、それほどアラインメントを気にする必要はないと思いますが。

    • 回答としてマーク ofen 2010年7月21日 5:19
    2010年7月21日 3:49

すべての返信

  • /Zp (構造体メンバの配置) - MSDN より
    このオプションでは、バイト数も指定できます。バイト数を指定すると、2 番目以降の構造体メンバは、メンバ型のサイズか n バイト境界 (n は 1、2、4、8、または 16 のいずれか) のどちらか小さい方に格納されます。
    書いてある通り、メンバの型サイズかコンパイルオプションで指定されたものの小さいほうですので、

    struct {
     char a[5];
     char b;
    };

    struct {
     char a[5];
     char b[3];
    };
    の様に続くメンバが1バイトの場合、コンパイルオプションにかかわらずパディングされません。

    struct {
     char a;
     short b;
    };
    では、/Zp1でパディングを行わせない場合を除き、aとbの間に1バイトのパディングが行われるだけです。
    (shortのサイズ2の倍数に置かれるため、1バイトパディングされます。)

    • 回答としてマーク ofen 2010年7月21日 5:19
    2010年7月21日 3:42
  • アラインメントのしくみについては、次の記事がとても詳しいです。

     データ型のアラインメントとは何か,なぜ必要なのか?
     http://www5d.biglobe.ne.jp/~noocyte/Programming/Alignment.html

    構造体 Ex の方は、メンバーが BYTE と char なので 1 バイト アラインメントになるが、
    構造体 hoge と hoge2 の方は、メンバーに void* があるため、ポインターのサイズ アラインメント (32bit / 64bit) でないと困る、ということでしょうか。

    個人的な意見としては、プロセスの境界をまたぐ (シリアライズとか通信とか) 以外の部分については、
    64bit に移行するからといって、それほどアラインメントを気にする必要はないと思いますが。

    • 回答としてマーク ofen 2010年7月21日 5:19
    2010年7月21日 3:49
  • 追記

    32bitと64bit環境では構造体のアライメントが異なるため、

    32ビット/64ビットに依存するわけではなく、コンパイルオプションです。
    (VC++2008では32ビット/64ビットどちらにおいても/Zp8がデフォルトだった記憶があります。)

    ポインターのサイズが4>8に変化しているので、デフォルトの/Zp8や、/Zp16において、ポインター変数が32ビット環境では4バイト境界に、64ビット環境では8バイト境界にパディングが行われるための勘違いだと思います。

    2010年7月21日 3:49
  • 調査したところ、ソース中に構造体を丸ごとmemcpy()している箇所が見つかりました。32bitと64bit環境では構造体のアライメントが異なるため、その点を考慮したソースに変更しようと考えております。

    もっと追記

    struct A {
    //省略
    };
    このような構造体で
    A a;
    A b;
    aをbにコピーするならば、
    memcpy(&b, &a ,sizeof b);やmemcpy(&b, &a ,sizeof (A));よりも
    素直にb = a;がいいと思います。
    struct Aがクラスになった場合でも、b = a;であれば=演算子(operator=)をオーバーロードしてやることで問題なく動作します。

     

    • 回答としてマーク ofen 2010年7月21日 5:19
    • 回答としてマークされていない ofen 2010年7月21日 5:19
    2010年7月21日 5:14
  • らーめん食べたいなぁさん、totojoさん回答ありがとうございました。

    アライメントに関して思いっきり勘違いしていたようで、おかげさまで解決できました。

    あと、構造体のコピーに関してなんですが、ちょっと事情が複雑でして、

    32bit版で構造体をそのままバイト配列にmemcpy()して、それをファイルに出力しているんです。

    そのデータを64bit版でも再利用したい・・・ということで、そのまま読み込むと32bit版とアライメントが

    異なるので、読み込めない、、、、というところが今回の質問の始まりでした。

    使っている構造体の最大要素はintなので、32bit/64bitどちらの環境でもアライメントは4バイトとなり、

    そのままmemcpy()でデータコピーで済みそうです。

    2010年7月21日 5:27