2013-06-07 20 views
6

私は構造体を扱っており、それらについていくつか質問があります。理解しているように、構造変数は順次メモリに配置されます。ブロック(ワード)の長さは、マシンアーキテクチャ(32ビット - 4バイト、64ビット - 8バイト)に依存します。C:データ構造の整列

32 bit - 20 bytes  
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 
------------------------------------------------------------------------------------------ 
c1| PB| s | s | c1| PB| PB| PB| d | d | d | d | d | d | d | d | i | i | i | i | 

64 bit - 24 bytes | 20 | 21 | 22 | 23 | 
previous sequence + --------------------- 
        | PB | PB | PB | PB | 

しかし、我々はマシン語にこのデータフィットを作るために、それを並べ替えることができます:それはなりますメモリで

struct ST1 { 
    char c1; 
    short s; 
    char c2; 
    double d; 
    int i; 
}; 

は、我々は2つのデータ構造を持っているとしましょう。このように:

0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 
---------------------------------------------------------------------- 
d | d | d | d | d | d | d | d | i | i | i | i | s | s | ch1| ch2| 

I質問のいくつかあり:

    32及び64の両方についてこの場合

    struct ST2 { 
        double d; 
        int i; 
        short s; 
        char c1; 
        char c2; 
    }; 
    

    それは同じ方法(16バイト)で表されるビット

  • これは野生の推測のようですが、structの主なルールは、最初に大きなサイズの変数を定義することですか?
  • 私が理解しているように、スタンドアロン変数では機能しません。 char str[] = "Hello";のように?
  • パディングバイトは、どのようなコードですか?それはASCIIテーブルのどこかにありますか?申し訳ありませんが、それを見つけることができませんでした。
  • 2つの構造体はすべてのメンバが異なるアドレスでメモリに表現され、メモリにシーケンシャルに配置することはできませんか?
  • このような構造:struct ST3 { char c1; char c2; char c3;} st3; size = 3を持っていますが、他のタイプのメンバーを追加すると、それが整列することがわかります。しかし、なぜそれはそれの前に整列していないのですか?

答えて

-1

変数が整列していることはよく分かりませんが、よくあることです。 GCCを使用する場合は、属性を使用して、データが整列されていることを確認することができます。

例:私はそれは、スタンドアローンの変数を扱ういない理解したよう

struct foo { 
    char c; 
    int x; 
} __attribute__((packed)); 

。 Like char str [] = "Hello" ;?

この表はあなたの記憶に整列されます。

+2

待ち時間は何ですか? 'packed'を使うとパディングが取り除かれ、メンバを正しく位置合わせしないようにすることができます。これはx86でも意味があります(例えば、doubleにアクセスすることは正しくアライメントされていればアトミックになります) – Christoph

+1

Cristophが言うように、完全な誤情報です。 – Casey

0

提起として、(構造のあなたのとても素敵な絵を無視して)、あなたの質問に答える

それは野生の推測のようなものだが、構造体の主なルールは、最初に大きなサイズの変数を定義するのですか?

常に最も整列が必要なものを入れてください。私は例えば最初にchar[99]を入れません。一般的に、これはポインタ、64ビットのネイティブ型、32ビットのネイティブ型などとして機能しますが、構造体に他の構造体が含まれている場合は非常に注意する必要があります。

私が理解しているように、スタンドアロン変数では機能しません。 Like char str[] = "Hello";

私はこれを本当に理解していません。スタック上にchar配列を定義すると、char配列になります。 char配列とそれに続くintを定義すると、おそらくスタック上にパディングがあり、それを見つけることができません。

パディングバイトは、どのようなコードですか?それはASCIIテーブルのどこかにありますか?申し訳ありませんが、それを見つけることができませんでした。

コードもデータもありません。コンパイラが挿入するパディングであり、プログラムの同じ実行または異なる実行で構造体の異なるインスタンス間で異なる場合もあれば、異なる値を含む場合もあります。

2つの構造体は、すべてのメンバが異なるアドレスでメモリに表され、メモリにシーケンシャルに配置することはできませんか?

これはわかりません。コンパイラが構造体の間にパディングを挿入できるかどうか尋ねていますか?そうでない場合は、この回答はあまり役に立たないので、明確にしてください。

コンパイラが構造体を作成するときに、そのような構造体の配列を健全に作成できるようにする必要があります。このことを考えてみましょう:コンパイラはウォブル後パディングの3つのバイトを挿入しません

struct S { 
    int wibble; 
    char wobble; 
}; 

S stuff[2]; 

場合は、適切に配置されることはありませんstuff[1].wobbleへのアクセスは、いくつかのハードウェア上でのクラッシュ(および他のハードウェア上の凶悪パフォーマンス)になりますました。基本的に、コンパイラは、構造体の最も整列したメンバが常にそのような構造体の配列に対して正しく配置されるように、最後にパディングを確保する必要があります。

このような構造:struct ST3 { char c1; char c2; char c3;} st3;サイズが3の場合、他のタイプのメンバーを追加すると、そのメンバーが整列することがわかります。しかし、なぜそれはそれの前に整列していないのですか?

コンパイラが正しく配置された場所に配置しないのはなぜですか?言語はそれを許さないので。コンパイラは、構造体のメンバの順序を変更することはできません。パディングを挿入することのみが許可されています。

0

構造体(およびクラス)のメンバーの配置は、プラットフォームによって異なりますが、コンパイラでもtrueです。メンバをそのサイズに揃える理由は、パフォーマンス上の理由によるものです。すべての整数型をそのサイズに合わせると、メモリアクセスが減少します。

通常、コンパイラにアラインメントを減らすよう強制できますが、特定の理由(たとえば、異なるプラットフォーム間のデータ互換性、通信データなど)を除いては、お勧めできません。

#pragma pack(1) 
struct ST1 { 
    char c1; 
    short s; 
    char c2; 
    double d; 
    int i; 
}; 

assert(sizeof(ST1) == 16); 

をしかし、私は前に言ったように、通常は良いアイデアではありません:ビジュアルC++では、たとえば、そのため#pragma packが存在します。

コンパイラは、いくつかのフィールドの後にパッドバイトを追加するだけではないことに注意してください。また、構造体がすべてのフィールドが右揃えになるようにメモリに確保されていることを保証しています。私は、より大きなフィールドタイプは、二重なので、あなたのST1のサンプルで、意味、コンパイラはdフィールドが(#pragma packまたは同様のオプションを使用している場合を除く)8バイトで整列されますが保証されます。ご質問について

ST1 st1; 

assert(&st1.d % 8 == 0); 

  • スペースを節約したい場合は、はい、サイズ順にフィールドを並べ替えることをお勧めします。構造化された構造体の場合は、構造体のサイズの代わりに、内部構造体のより大きなフィールドのサイズを使用します。
  • スタンドアロン変数を処理しています。しかし、コンパイラは(構造体やクラスのメンバではなく)メモリ内の変数を順序付けることができます。たとえば、

short s[27]; 
int32_t i32[34]; 
int64_t i64[45]; 

assert(s % 2 == 0); 
assert(i32 % 4 == 0); 
assert(i64 % 8 == 0); 
  • パディングバイトはできるが何も入っています。通常は初期化されたデータです(少なくとも初期化します)。デバッグの理由から、コンパイラによって特定のバイトパターンが含まれることがあります。
  • メモリ内の異なるアドレスで表現されているすべてのメンバーの構造について:申し訳ありませんが、私はあなたが何を求めているのかよく分かりません。
  • 標準のC++では、struct/classのアドレスは、そのstruct/classの最初のフィールドのアドレスと同じでなければならないと言います。その後、c3の後にのみパディングが可能ですが、c1の前にはパディングはできません。
  • N3337(C++ 11)[9.2 class.menu、P.20]から

標準レイアウト構造体オブジェクトへのポインタは、適切に変換 reinterpret_castを使用して、を指し最初のメンバー(またはそのメンバーがビットフィールドである場合は )、その逆の場合も同様です。 [ 注: 標準レイアウトの構造体オブジェクト内には名前のないパディングがありますが、必要に応じて適切なアライメントを達成するために、最初は埋め込まれていないかもしれません。末端ノート]

3

基本的なルールは単純です:

  • メンバーが順番に存在する必要があります(プライベート使用++ Cでない限り:...セクション:パブリック)パディングがある
  • メンバー間および最後のメンバー間で許可されています。

それはそれです。残りの部分は実装に残ります。タイプによって格納される記憶域、パディング量です。通常、それはABIで適切に文書化されるか、またはコンパイラで直接的に文書化され、操作のためのツールさえあることが期待できます。実際のパディングで

は、いくつかのアーキテクチャ上で、を言う、SPARCは、他人に4で割り切れるアドレスに並ぶことは必須ではありませんが、ずれエンティティが処理に多くの時間がかかることがあり32ビット「int型」が必要と言う必要があります80286プロセッサは、奇数アドレスから16ビットエンティティを読み取るために余分なサイクルが必要です。 (私が忘れる前に:型の表現自体は異なります!)

通常、アライメントの要件または最高のパフォーマンスは正確に一致します。境界と同じサイズで整列する必要があります。良いカウンターの例は、0ではなく8または16バイトの整列のようなの80ビット浮動小数点数です(いくつかのコンパイラーではdoubleまたはlong doubleとして利用できます)。

パディングコンパイラーでは、通常、デフォルト設定に切り替えます。それはバージョンごとに変わるので、アップグレードのほうがよいでしょう。そして、_attribute__(packed)gcc#pragmaパックなど多くのコード内のコードをオーバーライドします。それらはすべて明らかに標準への拡張です。

結論を出すには、今や将来的にターゲットとするすべてのコンパイラのdoxを読んで、それらが何を行い、どのように制御するのかを知ることです。あなたが最初にレイアウトに興味を持っている理由に応じて、おそらくターゲットプラットフォームのdoxも読んでください。

通常、1つの動機は、生のメモリをファイルに書き出し、それを読み込む際に安定したレイアウトを持つことです。多分異なるコンパイラを使用して異なるプラットフォーム上で。それは、新しいプラットフォームタイプがシーンに入るまで、より簡単なものです。

その他の動機はパフォーマンスです。それは、ルールが速く変わるので、方法がより難しく、効果をすぐに予測することは難しいです。インテルでは、基本的な「ミスアライメント」のペナルティが長い間消えてしまったのではなく、キャッシュ・ラインの内側にあることになります。キャッシュラインのサイズは、プロセッサによって異なります。また、より多くのパッディングを使用することでより良い個人を作り出すことができ、完全にパックされた構造はキャッシュ使用でより経済的です。

一部の操作では、適切な位置合わせが必要ですが、コンパイラによって直接実行されるわけではありません。特定の位置合わせプラグマを適用する必要があります(特定のSSE関連のものなど)。

ボトムラインリピート:推測を止め、目標を決めて適切なdoxを読んでください。 (ところで、私はSPARCためのアーキテクチャマニュアルを読むため、IA32と他の人が多くの点で驚異的な楽しさとゲインた。)

0

そのアクセスに多くの命令およびサイクルを要しIntelアーキテクチャ上のgccについて(読み取り/書き込み)奇数番号のメモリアドレス。偶数番号のメモリアドレスにパディングが追加されます