Cでの構造体メモリレイアウト


85

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

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

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

Cのメモリレイアウト動作とは何ですか?何を再注文/調整する必要がありますか?

回答:


110

Cでは、コンパイラーはすべてのプリミティブ型に対して何らかの配置を指示することができます。通常、配置はタイプのサイズです。しかし、それは完全に実装固有です。

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

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

C99§6.7.2.1から:

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

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


1
一部のコンパイラ(つまりGCC)#pragma packは、セマンティクスをよりきめ細かく制御して、同じ効果を実装します。
クリス・ルッツ2010年

21
反対票を見て驚いた。誰かがエラーを指摘できますか?
ポテトスワッター2010年

2
C11にもあり_Alignasます。
idmean 2017年

117

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

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

したがって、次の構造体が与えられます。

struct ST
{
   char ch1;
   short s;
   char ch2;
   long long ll;
   int i;
};
  • ch1 オフセット0にあります
  • 整列するためにパディングバイトが挿入されます。
  • s オフセット2で
  • ch2 sの直後のオフセット4にあります
  • 整列するために3つのパディングバイトが挿入されます。
  • ll オフセット8で
  • i llの直後のオフセット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;

3
必要に応じて、前にパディングを追加します...後のように。char例に最後のメンバーを追加するのが最善です。
デュプリケータ

9
プリミティブ型は、必ずしもsizeof(T)バイトの整列を必要としません。たとえば、double一般的な32ビットアーキテクチャでは8バイトですが、多くの場合、4バイトのアラインメントのみが必要です。さらに、構造体の端のパディングは、最も幅の広い構造体メンバーの位置合わせにのみパディングします。たとえば、3文字の変数の構造体にはパディングを含めることができません。
マット

1
@ dan04、sizeof(T)の降順で構造体をレイアウトすることをお勧めします。これを行うことの欠点はありますか?
RohitMat

11

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

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

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

6.54.8からGCCドキュメントの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 デフォルトのレイアウトに戻ります。

お手入れありがとうございます。あなたが案内したように私は質問を修正しました。
eonil 2010年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.