7

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

2 つのデータ構造があるとします。

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

メモリ内では次のようになります。

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 ST2 {
    double d;
    int i;
    short s;
    char c1;
    char c2;
};

この場合、32 ビットと 64 ビットの両方で同じ方法 (16 バイト) で表されます。

 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|

いくつか質問があります。

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

5 に答える 5

4

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

  • メンバーは順番に存在する必要があります (C++ で private: public: ... セクションを使用しない限り)
  • パディングはメンバー間と最後の後に許可されます

それはそれについてです。残りは実装に任されています: 型によって使用されるストレージ、パディング量。通常、 ABIで適切に文書化されるか、コンパイラーで直接文書化され、操作用のツールさえあると期待できます。

実際には、一部のアーキテクチャではパディングが必要です。たとえば、SPARC では、4 で割り切れるアドレスにアラインされた32 ビットの「int」が必要です。その他のアーキテクチャでは、パディングは必須ではありませんが、アラインされていないエンティティの処理に時間がかかる場合があります。たとえば、 80286プロセッサでは追加のサイクルが必要です奇数アドレスから 16 ビット エンティティを読み取ります。(忘れる前に: 型の表現自体が異なります!)

通常、アライメント要件または最高のパフォーマンスが正確に一致します。サイズと同じ境界でアライメントする必要があります。良い反例は、10 ではなく 8 または 16 バイト アラインメントを好む80 ビット浮動小数点数 (一部のコンパイラでは double または long double として使用可能) です。

パディングコンパイラをいじるには、通常、デフォルトを設定するためのスイッチを提供します。これはバージョンごとに変化するため、アップグレード時に考慮に入れることをお勧めします。_attribute__(packed)また、gccのような内部コード オーバーライド機能や、 MS#pragmaのpackなどがあります。これらはすべて、明らかに標準への拡張です。

肝心なのは、レイアウトをいじりたい場合は、現在および将来的に対象とするすべてのコンパイラの dox を読み始めて、それらが何を行い、どのように制御するかを知ることです。そもそもレイアウトに興味がある理由によっては、ターゲット プラットフォームの dox もお読みください。

通常の動機の 1 つは、生のメモリをファイルに書き出して、それを読み戻すことを期待するときに、安定したレイアウトを用意することです。異なるコンパイラを使用する別のプラットフォーム上にある可能性があります。新しいプラットフォーム タイプがシーンに登場するまでは、これがより簡単な方法です。

他の動機はパフォーマンスです。ルールは急速に変化し、影響をすぐに予測するのは難しいため、これははるかに注意が必要です。インテルでは、基本的な「ミスアラインメント」ペナルティは長い間なくなりましたが、代わりにキャッシュライン内にあることが重要です。キャッシュ ラインのサイズはプロセッサによって異なります。また、より多くのパディングを使用すると、より良い個体が生成される可能性がありますが、完全にパックされた構造はキャッシュの使用においてより経済的です。

また、一部の操作には適切なアラインメントが必要ですが、コンパイラによって直接強制されるわけではなく、特別なアラインメント プラグマを適用する必要がある場合があります (特定のSSE関連のものなど)。

要点を繰り返します。推測をやめ、ターゲットを決めて、適切なドキュメントを読んでください。(ちなみに、私にとってSPARCIA32などのアーキテクチャ マニュアルを読むことは、多くの点で非常に楽しく、有益でした。)

于 2013-06-07T11:12:24.970 に答える
0

構造体 (およびクラス) のメンバーのアライメントは、プラットフォームに依存しますが、コンパイラにも依存します。メンバーをそのサイズに揃える理由は、パフォーマンス上の理由です。すべての整数型をそのサイズに揃えると、メモリ アクセスが削減されます。

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

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

assert(sizeof(ST1) == 16);

しかし、前に述べたように、通常は良い考えではありません。

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

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++ では、構造体/クラスのアドレスは、そのような構造体/クラスの最初のフィールドのアドレスと同じでなければならないと言われています。次に、 の後にのみパディングが可能でc3、 の前には決してパディングできませんc1

N3337 (C++11) [9.2 class.menu、p.20] から:

を使用して適切に変換された標準レイアウト構造体オブジェクトへのポインターは、reinterpret_castその最初のメンバー (または、そのメンバーがビットフィールドの場合は、それが存在するユニット) を指し、その逆も同様です。[ 注: したがって、標準レイアウトの構造体オブジェクト内に名前のないパディングがある場合がありますが、適切な配置を実現するために必要なため、先頭にはありません。—終わりのメモ]

于 2013-06-07T09:51:50.047 に答える
0

提起されたとおりに質問に答える(構造の非常に素晴らしい写真は無視してください)

勝手な推測ですが、構造体の主なルールは、最初に大きなサイズの変数を定義することですか?

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

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

これはよくわかりません。スタックに char 配列を定義すると、char 配列が適用されます。char 配列の後に int を定義すると、おそらくスタックにパディングがあり、見つけることができません。

パディングバイト、それは何のコードですか? それはASCIIテーブルのどこかにありますか?見つかりませんでした。

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

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

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

コンパイラが構造を作成するとき、そのような構造の配列を適切に作成できるようにする必要があります。このことを考慮:

struct  S {
    int wibble;
    char wobble;
};

S stuff[2];

コンパイラが wobble の後に 3 バイトのパディングを挿入しない場合、へのアクセスがstuff[1].wobble適切に整列されず、一部のハードウェアでクラッシュが発生します (他のハードウェアではパフォーマンスが大幅に低下します)。基本的に、コンパイラは、構造体の最も整列されたメンバーがそのような構造体の配列に対して常に正しく整列されるように、最後にパディングを確保する必要があります。

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

「なぜコンパイラはそれを正しく配置された場所に配置しないのですか」という意味ですか? 言語がそれを許さないからです。コンパイラは、構造体のメンバーを並べ替えることができません。パディングの挿入のみが許可されています。

于 2013-06-07T09:37:46.517 に答える
-1

変数が整列されているかどうかわからないことに注意してください (ただし、整列されていることがよくあります)。GCC を使用している場合は、データが整列されていることを確認するために pack属性を使用できます。

例 :

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

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

このテーブルは、メモリ内で整列されます。

于 2013-06-07T08:44:54.733 に答える