web-dev-qa-db-ja.com

C構造体のメモリアライメント

私は32ビットマシンで作業しているので、メモリアライメントは4バイトにする必要があると思います。構造体があるとします:

_typedef struct {
    unsigned short v1;
    unsigned short v2;
    unsigned short v3;
} myStruct;
_

実際のサイズは6バイトで、整列サイズは8であると仮定しますが、sizeof(myStruct)は6を返します。

しかし、私が書いた場合:

_typedef struct {
    unsigned short v1;
    unsigned short v2;
    unsigned short v3;
    int i;
} myStruct;
_

実際のサイズは10バイト、alignedは12、そして今回はsizeof(myStruct) == 12です。

誰かがその違いを説明できますか?

53
Ivan

exactlyが同じで、a differenceは、宣言される順序が異なるため、各構造体のサイズは異なる場合があります(多くの場合、異なります)。

たとえば、これを参照してください、

#include <iostream>
using namespace std;
struct A
{
   char c;
   char d;
   int i; 
};
struct B
{
   char c;
   int i;   //note the order is different!
   char d;
};
int main() {
        cout << sizeof(A) << endl;
        cout << sizeof(B) << endl;
}

gcc-4.3.4でコンパイルすると、次の出力が得られます。

8
12

つまり、両方の構造体のメンバーが同じであっても、サイズは異なります!

Ideoneのコード: http://ideone.com/HGGVl

要するに、標準ではパディングの実行方法については説明されていないため、コンパイラーは自由に決定を下すことができ、ユーザーはcannotすべてを仮定しますコンパイラーも同じ決定を下します。

19
Nawaz

デフォルトでは、値はサイズに応じて整列されます。したがって、shortのような2バイト値は2バイト境界に位置合わせされ、intのような4バイト値は4バイト境界に位置合わせされます

この例では、iの前に2バイトのパディングを追加して、iが4バイト境界に収まるようにします。

(構造全体は、少なくとも構造内の最大値と同じ大きさの境界に位置合わせされるため、構造は4バイト境界に位置合わせされます。)

実際のルールはプラットフォームによって異なります- データ構造のアライメント のWikipediaページに詳細があります。

コンパイラでは通常、(たとえば)#pragma packディレクティブ。

13
RichieHindle

想定:

sizeof(unsigned short) == 2
sizeof(int)            == 4

次に、私は個人的に次のものを使用します(コンパイラは異なる場合があります)。

unsigned shorts are aligned to 2 byte boundaries
int will be aligned to 4 byte boundaries.


typedef struct
{
   unsigned short v1;    // 0 bytes offset
   unsigned short v2;    // 2 bytes offset
   unsigned short v3;    // 4 bytes offset
} myStruct;              // End 6 bytes.


// No part is required to align tighter than 2 bytes. 
// So whole structure can be 2 byte aligned.

typedef struct
{
    unsigned short v1;      // 0 bytes offset
    unsigned short v2;      // 2 bytes offset
    unsigned short v3;      // 4 bytes offset
    /// Padding             // 6-7 padding (so i is 4 byte aligned
    int i;                  // 8 bytes offset
} myStruct;                 // End 12 bytes

// Whole structure needs to be 4 byte aligned.
// So that i is correctly aligned.
5
Martin York

まず、パディングの詳細はコンパイラーに任されていますが、OSはアライメント要件に関していくつかの規則を課しています。この回答は、OSが異なる場合がありますが、gccを使用していることを前提としています

特定の構造体とその要素が占めるスペースを決定するには、次のルールに従います。

最初に、構造体が常にallデータ型に適切に位置合わせされたアドレスで始まると仮定します。

次に、構造体のすべてのエントリに対して:

  • 必要な最小スペースは、sizeof(element)で指定された要素の生のサイズです。
  • 要素の配置要件は、要素の基本型の配置要件です。特に、これはchar[20]配列のアライメント要件がプレーンcharの要件と同じであることを意味します。

最後に、構造体全体のアライメント要件は、各要素のアライメント要件の最大値です。

gccは指定された要素の後にパディングを挿入して、次の要素(または最後の要素について話している場合は構造体)が正しく整列されるようにします。 neverは、メモリを節約する場合でも、構造体の要素の順序を並べ替えます。

アライメント要件自体も少し変わっています。

  • 32ビットLinuxでは、2バイトのデータ型には2バイトのアライメントが必要です(アドレスは偶数でなければなりません)。すべての大きなデータ型は、4バイトの境界整列(0x00x40x8または0xCで終わるアドレス)を持つ必要があります。これは、4バイトより大きい型(doublelong doubleなど)にも適用されることに注意してください。
  • 32ビットWindowsは、型のサイズがKバイトの場合、Kバイトに揃える必要があるという点でより厳密です。つまり、doubleは、0x0または0x8で終わるアドレスにのみ配置できます。これに対する唯一の例外は、long doubleであり、実際には12バイトの長さであるにもかかわらず、4バイトにアライメントされています。
  • LinuxとWindowsの両方で、64ビットマシンでは、KバイトタイプはKバイトに揃える必要があります。繰り返しますが、long doubleは例外であり、16バイトに揃える必要があります。
4
Abhay Buch

各データ型は、独自のサイズのメモリ境界に揃える必要があります。したがって、shortは2バイト境界に位置合わせする必要があり、intは4バイト境界に位置合わせする必要があります。同様に、long longは8バイト境界にある必要があります。

2
Jonathan

2番目のsizeof(myStruct)12である理由は、v3iの間に挿入され、iを32ビットに揃えるパディングです。境界。 2バイトあります。

Wikipedia は、パディングと整列を合理的に明確に説明しています。

1
NPE

各varのサイズに基づいて境界に配置されているように聞こえるので、アドレスがアクセスされるサイズの倍数になります(したがって、shortsが2に整列され、intが4に整列されます)。 int、sizeof(mystruct)は10である必要があります。もちろん、これはすべて、使用されているコンパイラーと、使用されている設定によって決まります。

0
Necrolis

標準では、完全な型の構造体のレイアウトについてはあまり言及されていません。コンパイラ次第です。アクセスするために境界で開始するにはintが必要であると判断しましたが、ショートのためにサブ境界のメモリアドレス指定を行う必要があるため、それらをパディングする必要はありません

0
Martin Beckett