2012-04-21 11 views
6

Visual C++では、構造体メンバのアライメントに影響するコンパイラスイッチ(/Zp)とpackプラグマの両方が用意されています。しかし、私は彼らがどのように動作するかについていくつかの誤解を持っているようです。 MSDNによればVisual C++での構造体の整列

、部材の位置合わせは、nの 複数またはメンバーのサイズの倍数のいずれかである境界になり、所与のアライメント値のためにN、

、方 が小さい。

パックの値を8バイト(デフォルト)としましょう。構造体内では、サイズが8バイト未満のメンバーは、それ自身のサイズの倍数のオフセットになると思います。サイズが8バイト以上のメンバーは、8バイトの倍数のオフセットになります。 Foo構造のサイズが12バイトである

#include <tchar.h> 

#pragma pack(8) 

struct Foo { 
    int i1; 
    int i2; 
    char c; 
}; 

struct Bar { 
    char c; 
    Foo foo; 
}; 

int _tmain(int argc, _TCHAR* argv[]) { 
    int fooSize = sizeof(Foo); // yields 12 
    Bar bar; 
    int fooOffset = ((int) &bar.foo) - ((int) &bar); // yields 4 

    return 0; 
} 

今すぐ次のプログラムを取ります。だからBarの中では、私はFooメンバがオフセット8(8の倍数)にあると予想していますが、実際にはオフセット4になります。なぜですか?

また、Fooは実際には4 + 4 + 1 = 9バイトのデータしか持っていません。コンパイラは、最後にパディングバイトを自動的に追加します。しかし、8バイトのアライメント値が与えられても、4の代わりに8の倍数にパディングすべきではありませんか?

ご理解ください!

+0

あなたの 'int'は4バイトだけですか?これを実行しているマシンは何ですか? –

+0

@トニー:32ビットアプリケーションです。 intが4ではなく8バイトだった場合、2つのFooは12バイトしかできませんでした。:-) –

答えて

7

あなたの抜粋はこれを「どちらか小さい方」と説明しています。 32ビットプラットフォームでは、intは4バイトです。 4は8より小さい。したがって、4バイトのアライメントを持つ。

プラグマは、物を梱包し、解凍しないようにします。それは理由がない限り踏み込まないでしょう。

+0

ちょっと待ってください。私はFooメンバーを持つBar構造体を持っています。だから私は "メンバーのサイズ"はFooのサイズを12としたと仮定しています。コンパイラは実際に/ inside/Fooを見ていて、/ primitive/typeの最大のメンバーを探しています(おそらく再帰的に)?もしそうなら、この動作のための(オンライン)参照がいくつかありますか? –

+1

コンパイラは、すべての型の配置要件を認識しています。 'Foo'のアライメント要件は4です。これは最大の型ではありませんが、通常は必ずしもそうではありませんが、通常はすべての包含型の最大整列要件となる外部構造の要件です。 (それは完全な型のサイズにすることはできません。そうでなければ、9,000バイトの構造体に続く整数に9,000バイトのパディングがあります。サイズ) –

+0

ありがとう、それは動作を説明します。 - それは完全な構造体のサイズにすることはできませんあなたの主張について:私はそれが正確に "どちらか小さい"のルールの理由だと思った。あなたの9,000バイト構造体は、8バイトよりも大きいので、代わりに8の倍数にアライメントされるので、8,996ではなく4バイトだけが無駄になります。だから、私にとってMSDNアルゴリズムは実際には理にかなっていました。あまりにも真実の半分しかないようです! –

1

見積もりによると、8バイトのアライメントをテストするには、8バイト以上のデータ型が必要です。ここでは、いくつかの明示的なサイズの種類のサンプルを示します。また、小さな要素を最後に置くと、構造体の端から落とすことができるため、パディングが表示されません。 VC 2010でこれをコンパイルする

#include <stdio.h> 
int 
main(int argc, char *argv[]) 
{ 
    struct S { 
     __int64 a; 
     __int8 b; 
     __int64 c; 
    }; 
#pragma pack(push,1) 
    struct T { 
     __int64 a; 
     __int8 b; 
     __int64 c; 
    }; 
#pragma pack(pop) 
#pragma pack(push,8) 
    struct U { 
     __int64 a; 
     __int8 b; 
     __int64 c; 
    }; 
    struct B { 
     __int8 c; 
     struct U s; 
    }; 
#pragma pack(pop) 

    printf("S %d T %d U %d B %d\n", 
      sizeof(struct S), sizeof(struct T), 
      sizeof(struct U), sizeof(struct B)); 
    return 0; 
} 

C:\src>cl -nologo -W3 -Od packing.c && packing.exe 
packing.c 
S 24 T 17 U 24 B 32 
4

は、最初の場所でなぜアライメント事項に留意してください。バイトを多重化しなくても、CPUがメモリをすばやく読み取ることができます。 cpu neversは構造体を1つのgulpで読み込み、メンバーにのみアクセスします。したがって、Foo構造体が12バイトであるという事実は重要ではありません。メンバーの整列だけが重要です。 Fooメンバーが4より大きい配置要件を持っていないと仮定すると、Bar.fooメンバーは4に整列する必要があります。

Fooは9の代わりに12バイトで説明も使用できます。コンパイラは最後に3バイトのパディングを追加して、Fooの配列に各配列要素に対して正しく整列されたメンバーが残るようにします。

関連する問題