96

私は C# のバックグラウンドを持っています。私は C のような低レベル言語の初心者です。

C# では、structのメモリはデフォルトでコンパイラによって配置されます。コンパイラは、データ フィールドの順序を変更したり、フィールド間に追加のビットを暗黙的に埋め込むことができます。そのため、正確なレイアウトのためにこの動作をオーバーライドするために、特別な属性を指定する必要がありました。

struct私の知る限り、Cはデフォルトでaのメモリレイアウトを並べ替えたり整列したりしません。ただし、見つけるのが非常に難しい小さな例外があると聞きました。

C のメモリ レイアウト動作とは何ですか? 何を並べ替え/整列する必要がありますか?

4

3 に答える 3

130

これは実装固有ですが、実際にはルールは (ない場合#pragma packなど) は次のとおりです。

  • 構造体メンバーは、宣言された順序で格納されます。(前述のように、これは C99 標準で必要です。)
  • 必要に応じて、各構造体メンバーの前にパディングが追加され、正しい配置が保証されます。
  • 各プリミティブ型 T には、sizeof(T)バイトのアライメントが必要です。

したがって、次の構造体があるとします。

struct ST
{
   char ch1;
   short s;
   char ch2;
   long long ll;
   int i;
};
  • ch1はオフセット 0 にあります
  • アラインするためにパディングバイトが挿入されます...
  • sオフセット 2
  • ch2s の直後のオフセット 4 にあります
  • 整列するために 3 つのパディング バイトが挿入されます...
  • llオフセット 8
  • ill の直後のオフセット 16 にあります
  • 構造体全体が 8 バイトの倍数になるように、最後に 4 つのパディング バイトが追加されます。これを 64 ビット システムで確認しました。32 ビット システムでは、構造体に 4 バイト アラインメントを許可する場合があります。

sizeof(ST)24もそうです。

パディングを避けるためにメンバーを再配置することで、16 バイトに減らすことができます。

struct ST
{
   long long ll; // @ 0
   int i;        // @ 8
   short s;      // @ 12
   char ch1;     // @ 14
   char ch2;     // @ 15
} ST;
于 2010-05-01T06:20:49.497 に答える
115

C では、コンパイラはすべてのプリミティブ型のアライメントを指定できます。通常、配置は型のサイズです。しかし、それは完全に実装固有です。

すべてのオブジェクトが適切に配置されるように、パディング バイトが導入されます。再注文はできません。

おそらく、リモートで最新のコンパイラはすべて#pragma pack、パディングの制御を可能にし、ABI への準拠をプログラマに任せることを実装しています。(ただし、これは厳密には非標準です。)

C99 §6.7.2.1 から:

12 構造体または共用体オブジェクトの各非ビット フィールド メンバは、その型に適した実装定義の方法で整列されます。

13 構造体オブジェクト内で、ビットフィールド以外のメンバーとビットフィールドが存在するユニットには、宣言された順序で増加するアドレスがあります。適切に変換された構造体オブジェクトへのポインターは、その最初のメンバー (または、そのメンバーがビットフィールドの場合は、それが存在するユニット) を指し、その逆も同様です。構造体オブジェクト内に名前のないパディングがある場合がありますが、先頭にはありません。

于 2010-05-01T05:26:25.140 に答える
12

データ構造の整列に関するウィキペディアの記事を読むことから始めて、データの整列について理解を深めることができます。

ウィキペディアの記事から:

データ アライメントとは、ワード サイズの倍数に等しいメモリ オフセットにデータを配置することを意味します。これにより、CPU がメモリを処理する方法により、システムのパフォーマンスが向上します。データを揃えるために、最後のデータ構造の終わりと次のデータ構造の始まりの間に無意味なバイトを挿入する必要がある場合があります。これはデータ構造のパディングです。

GCC ドキュメントの6.54.8 Structure-Packing プラグマから:

Microsoft Windows コンパイラとの互換性のために、GCC は一連の #pragma ディレクティブをサポートします。これらのディレクティブは、構造体 (ゼロ幅のビットフィールドを除く)、共用体、および後で定義されるクラスのメンバーの最大アラインメントを変更します。以下の n 値は、常に小さい 2 の累乗である必要があり、新しいアライメントをバイト単位で指定します。

  1. #pragma pack(n)新しい配置を設定するだけです。
  2. #pragma pack()コンパイルの開始時に有効だったアラインメントを設定します (コマンド ライン オプション -fpack-struct[=] コード生成オプションも参照してください)。
  3. #pragma pack(push[,n])現在の配置設定を内部スタックにプッシュし、オプションで新しい配置を設定します。
  4. #pragma pack(pop)配置設定を内部スタックの一番上に保存されているものに戻します (そしてそのスタック エントリを削除します)。#pragma pack([n])この内部スタックには影響しないことに注意し てください。したがって、複数の インスタンス が#pragma pack(push) 続き、単一の.#pragma pack(n)#pragma pack(pop)

i386 や powerpc などの一部のターゲットは#pragma、ドキュメントに記載されているように構造体をレイアウトする ms_struct を サポートしています__attribute__ ((ms_struct))

  1. #pragma ms_struct on宣言された構造体のレイアウトをオンにします。
  2. #pragma ms_struct off宣言された構造体のレイアウトをオフにします。
  3. #pragma ms_struct resetデフォルトのレイアウトに戻ります。
于 2010-05-01T05:26:01.100 に答える