275

検討:

struct mystruct_A
{
   char a;
   int b;
   char c;
} x;

struct mystruct_B
{
   int b;
   char a;
} y;

構造物のサイズはそれぞれ12と8です。

これらの構造はパッドまたはパックされていますか?

パディングまたはパッキングはいつ行われますか?

4

11 に答える 11

326

パディング は、構造体のメンバーを「自然な」アドレス境界に揃えintます。たとえば、メンバーにはオフセットがありmod(4) == 0、32ビットプラットフォーム上にあります。パディングはデフォルトでオンになっています。次の「ギャップ」を最初の構造に挿入します。

struct mystruct_A {
    char a;
    char gap_0[3]; /* inserted by compiler: for alignment of b */
    int b;
    char c;
    char gap_1[3]; /* -"-: for alignment of the whole struct in an array */
} x;

一方、パッキング__attribute__((__packed__))は、コンパイラがパディングを実行できないようにします。これは明示的に要求する必要があります。GCCでは、次のようになります。

struct __attribute__((__packed__)) mystruct_A {
    char a;
    int b;
    char c;
};

632ビットアーキテクチャでサイズの構造を生成します。

ただし、注意してください。アラインされていないメモリアクセスは、それを許可するアーキテクチャ(x86やamd64など)では低速であり、SPARCなどの厳密なアラインメントアーキテクチャでは明示的に禁止されています。

于 2010-11-29T17:24:02.100 に答える
106

上記の回答は理由を明確に説明していますが、パディングのサイズについては完全には明確ではないようです。そこで、The Lost Art of Structure Packingから学んだことに従って回答を追加します。これは、に限定されないように進化しましたCが、Go、にも適用可能Rust


メモリアライン(構造体用)

ルール:

  • 個々のメンバーの前に、サイズで割り切れるアドレスから開始するようにパディングがあります。
    たとえば、64ビットシステムでintは、4で割り切れるアドレス、およびlong8で割り切れるアドレスから開始する必要がありますshort
  • charは特別で、char[]任意のメモリアドレスにすることができるため、前にパディングする必要はありません。
  • の場合struct、個々のメンバーの位置合わせの必要性を除いて、構造体全体のサイズは、最後にパディングすることにより、最大の個々のメンバーのサイズで割り切れるサイズに位置合わせされます。
    たとえば、構造体の最大メンバーがlong8で割り切れ、int次に4で割り切れ、short次に2で割り切れる場合。

メンバーの順序:

  • メンバーの順序は、構造体の実際のサイズに影響を与える可能性があるため、この点に注意してください。たとえば、以下のstu_cstu_dの例では、メンバーは同じですが、順序が異なり、2つの構造体のサイズが異なります。

メモリ内のアドレス(構造体用)

ルール:

  • 64ビットシステム
    の構造体アドレスは(n * 16)バイトから始まります。(以下の例でわかるように、構造体の印刷されたすべての16進アドレスはで終わり0ます。
    理由:可能な最大の個々の構造体メンバーは16バイト(long double)です。
  • (更新)構造体にasメンバーのみが含まれている場合char、そのアドレスは任意のアドレスから開始できます。

空のスペース

  • 2つの構造体の間の空のスペースは、収まる可能性のある非構造体変数によって使用される可能性があります。
    たとえば、test_struct_address()以下では、変数xは隣接する構造体gとの間に存在しますh。宣言され
    ているかどうかに関係なく、のアドレスは変更されず、無駄になった空きスペースを再利用するだけです。 の同様のケース。xhxg
    y

64ビットシステムの場合

memory_align.c

/**
 * Memory align & padding - for struct.
 * compile: gcc memory_align.c
 * execute: ./a.out
 */ 
#include <stdio.h>

// size is 8, 4 + 1, then round to multiple of 4 (int's size),
struct stu_a {
    int i;
    char c;
};

// size is 16, 8 + 1, then round to multiple of 8 (long's size),
struct stu_b {
    long l;
    char c;
};

// size is 24, l need padding by 4 before it, then round to multiple of 8 (long's size),
struct stu_c {
    int i;
    long l;
    char c;
};

// size is 16, 8 + 4 + 1, then round to multiple of 8 (long's size),
struct stu_d {
    long l;
    int i;
    char c;
};

// size is 16, 8 + 4 + 1, then round to multiple of 8 (double's size),
struct stu_e {
    double d;
    int i;
    char c;
};

// size is 24, d need align to 8, then round to multiple of 8 (double's size),
struct stu_f {
    int i;
    double d;
    char c;
};

// size is 4,
struct stu_g {
    int i;
};

// size is 8,
struct stu_h {
    long l;
};

// test - padding within a single struct,
int test_struct_padding() {
    printf("%s: %ld\n", "stu_a", sizeof(struct stu_a));
    printf("%s: %ld\n", "stu_b", sizeof(struct stu_b));
    printf("%s: %ld\n", "stu_c", sizeof(struct stu_c));
    printf("%s: %ld\n", "stu_d", sizeof(struct stu_d));
    printf("%s: %ld\n", "stu_e", sizeof(struct stu_e));
    printf("%s: %ld\n", "stu_f", sizeof(struct stu_f));

    printf("%s: %ld\n", "stu_g", sizeof(struct stu_g));
    printf("%s: %ld\n", "stu_h", sizeof(struct stu_h));

    return 0;
}

// test - address of struct,
int test_struct_address() {
    printf("%s: %ld\n", "stu_g", sizeof(struct stu_g));
    printf("%s: %ld\n", "stu_h", sizeof(struct stu_h));
    printf("%s: %ld\n", "stu_f", sizeof(struct stu_f));

    struct stu_g g;
    struct stu_h h;
    struct stu_f f1;
    struct stu_f f2;
    int x = 1;
    long y = 1;

    printf("address of %s: %p\n", "g", &g);
    printf("address of %s: %p\n", "h", &h);
    printf("address of %s: %p\n", "f1", &f1);
    printf("address of %s: %p\n", "f2", &f2);
    printf("address of %s: %p\n", "x", &x);
    printf("address of %s: %p\n", "y", &y);

    // g is only 4 bytes itself, but distance to next struct is 16 bytes(on 64 bit system) or 8 bytes(on 32 bit system),
    printf("space between %s and %s: %ld\n", "g", "h", (long)(&h) - (long)(&g));

    // h is only 8 bytes itself, but distance to next struct is 16 bytes(on 64 bit system) or 8 bytes(on 32 bit system),
    printf("space between %s and %s: %ld\n", "h", "f1", (long)(&f1) - (long)(&h));

    // f1 is only 24 bytes itself, but distance to next struct is 32 bytes(on 64 bit system) or 24 bytes(on 32 bit system),
    printf("space between %s and %s: %ld\n", "f1", "f2", (long)(&f2) - (long)(&f1));

    // x is not a struct, and it reuse those empty space between struts, which exists due to padding, e.g between g & h,
    printf("space between %s and %s: %ld\n", "x", "f2", (long)(&x) - (long)(&f2));
    printf("space between %s and %s: %ld\n", "g", "x", (long)(&x) - (long)(&g));

    // y is not a struct, and it reuse those empty space between struts, which exists due to padding, e.g between h & f1,
    printf("space between %s and %s: %ld\n", "x", "y", (long)(&y) - (long)(&x));
    printf("space between %s and %s: %ld\n", "h", "y", (long)(&y) - (long)(&h));

    return 0;
}

int main(int argc, char * argv[]) {
    test_struct_padding();
    // test_struct_address();

    return 0;
}

実行結果- test_struct_padding()

stu_a: 8
stu_b: 16
stu_c: 24
stu_d: 16
stu_e: 16
stu_f: 24
stu_g: 4
stu_h: 8

実行結果- test_struct_address()

stu_g: 4
stu_h: 8
stu_f: 24
address of g: 0x7fffd63a95d0  // struct variable - address dividable by 16,
address of h: 0x7fffd63a95e0  // struct variable - address dividable by 16,
address of f1: 0x7fffd63a95f0 // struct variable - address dividable by 16,
address of f2: 0x7fffd63a9610 // struct variable - address dividable by 16,
address of x: 0x7fffd63a95dc  // non-struct variable - resides within the empty space between struct variable g & h.
address of y: 0x7fffd63a95e8  // non-struct variable - resides within the empty space between struct variable h & f1.
space between g and h: 16
space between h and f1: 16
space between f1 and f2: 32
space between x and f2: -52
space between g and x: 12
space between x and y: 12
space between h and y: 8

したがって、各変数のアドレス開始はg:d0 x:dc h:e0 y:e8です。

ここに画像の説明を入力してください

于 2016-07-01T11:20:13.797 に答える
54

私はこの質問が古く、ここでのほとんどの回答がパディングを非常によく説明していることを知っていますが、それを自分で理解しようとしているときに、起こっていることの「視覚的」イメージが助けになると思いました。

プロセッサは、一定のサイズ(ワード)の「チャンク」でメモリを読み取ります。プロセッサワードの長さが8バイトだとします。これは、メモリを8バイトのビルディングブロックの大きな行と見なします。メモリから情報を取得する必要があるたびに、それらのブロックの1つに到達して取得します。

変数の配置

上の画像のように、Char(1バイト長)がどこにあるかは関係ありません。これは、Char(1バイト長)がこれらのブロックの1つに含まれ、CPUが1ワードのみを処理する必要があるためです。

4バイトのintや8バイトのdoubleのように、1バイトより大きいデータを処理する場合、それらをメモリ内で整列させる方法によって、CPUで処理する必要のあるワード数が異なります。4バイトのチャンクが常にブロック内に収まるように配置されている場合(メモリアドレスは4の倍数)、1ワードのみを処理する必要があります。そうしないと、4バイトのチャンクの一部が1つのブロックにあり、一部が別のブロックにある可能性があり、プロセッサがこのデータを読み取るために2ワードを処理する必要があります。

同じことが8バイトのdoubleにも当てはまりますが、常にブロック内にあることを保証するために、8の倍数のメモリアドレスにある必要があります。

これは8バイトのワードプロセッサを考慮していますが、この概念は他のサイズのワードにも適用されます。

パディングは、これらのデータ間のギャップを埋めて、それらがそれらのブロックと整列していることを確認することで機能します。これにより、メモリの読み取り中のパフォーマンスが向上します。

ただし、他の回答で述べられているように、パフォーマンス自体よりもスペースが重要な場合があります。RAMがあまりないコンピューターで大量のデータを処理している可能性があります(スワップスペースを使用できますが、処理速度が大幅に低下します)。最小のパディングが行われるまでプログラム内の変数を配置できますが(他のいくつかの回答で大いに例証されているように)、それだけでは不十分な場合は、パディングを明示的に無効にすることができます

于 2016-08-07T04:55:33.480 に答える
23

構造体パッキングは、構造体のパディング、位置合わせが最も重要な場合に使用されるパディング、スペースが最も重要な場合に使用されるパディングを抑制します。

一部のコンパイラは#pragma、パディングを抑制したり、nバイト数にパックしたりするために提供しています。これを行うためのキーワードを提供するものもあります。通常、構造体のパディングを変更するために使用されるプラグマは、次の形式になります(コンパイラによって異なります)。

#pragma pack(n)

たとえば、ARMは__packed構造体のパディングを抑制するキーワードを提供します。詳細については、コンパイラのマニュアルを参照してください。

したがって、パック構造はパディングのない構造です。

一般的にパックされた構造が使用されます

  • スペースを節約する


  • 何らかのプロトコルを使用してネットワーク経由で送信するようにデータ構造をフォーマットする(エンディアンに対処する必要があるため、これはもちろん良い習慣ではありません)

于 2013-02-19T07:33:16.263 に答える
5

パディングとパッキングは、同じことの2つの側面にすぎません。

  • パッキングまたは位置合わせは、各メンバーが四捨五入されるサイズです。
  • パディングは、配置に一致するように追加される余分なスペースです

ではmystruct_A、デフォルトの配置を4とすると、各メンバーは4バイトの倍数で配置されます。のサイズcharは1であるためa、およびのパディングcは4〜1 = 3バイトですが、int bすでに4バイトのパディングは必要ありません。の場合も同じように機能しmystruct_Bます。

于 2010-11-29T17:23:05.797 に答える
3

パディングのルール:

  1. 構造体のすべてのメンバーは、そのサイズで割り切れるアドレスにある必要があります。このルールが満たされていることを確認するために、要素間または構造体の最後にパディングが挿入されます。これは、ハードウェアによるバスアクセスをより簡単かつ効率的にするために行われます。
  2. 構造体の最後のパディングは、構造体の最大メンバーのサイズに基づいて決定されます。

ルール2の理由:次の構造体を検討してください。

構造体1

この構造体の配列(2つの構造体)を作成する場合、最後にパディングは必要ありません。

Struct1アレイ

したがって、構造体のサイズ=8バイト

以下のように別の構造体を作成するとします。

構造体2

この構造体の配列を作成する場合、最後に必要なパディングのバイト数の2つの可能性があります。

A.最後に3バイトを追加し、Longではなくintに揃える場合:

Struct2配列をintに揃えます

B.最後に7バイトを追加し、Longに揃える場合:

LongにアラインされたStruct2配列

2番目の配列の開始アドレスは8の倍数(つまり24)です。構造体のサイズ=24バイト

したがって、構造体の次の配列の開始アドレスを最大のメンバーの倍数に揃えることによって(つまり、この構造体の配列を作成する場合、2番目の配列の最初のアドレスは複数のアドレスで開始する必要があります構造体の最大メンバーの24(3 * 8))で、最後に必要なパディングバイト数を計算できます。

于 2020-01-23T07:52:59.023 に答える
3

変数は、その配置(通常はサイズ)で割り切れる任意のアドレスに格納されます。したがって、パディング/パッキングは構造体専用ではありません。実際、すべてのデータには独自の配置要件があります

int main(void) {
    // We assume the `c` is stored as first byte of machine word
    // as a convenience! If the `c` was stored as a last byte of previous
    // word, there is no need to pad bytes before variable `i`
    // because `i` is automatically aligned in a new word.

    char      c;  // starts from any addresses divisible by 1(any addresses).
    char pad[3];  // not-used memory for `i` to start from its address.
    int32_t   i;  // starts from any addresses divisible by 4.

これは構造体に似ていますが、いくつかの違いがあります。まず、2種類のパディングがあると言えます。a)各メンバーをそのアドレスから適切に開始するために、メンバー間にいくつかのバイトが挿入されます。b)アドレスから次の構造体インスタンスを適切に開始するために、各構造体にいくつかのバイトが追加されます。

// Example for rule 1 below.
struct st {
    char      c;  // starts from any addresses divisible by 4, not 1.
    char pad[3];  // not-used memory for `i` to start from its address.
    int32_t   i;  // starts from any addresses divisible by 4.
};

// Example for rule 2 below.
struct st {
    int32_t   i;  // starts from any addresses divisible by 4.
    char      c;  // starts from any addresses.
    char pad[3];  // not-used memory for next `st`(or anything that has same
                  // alignment requirement) to start from its own address.
};
  1. 4構造体の最初のメンバーは常に、最大のメンバーの配置要件(ここでは、の配置)によって決定される構造体自体の配置要件で割り切れるアドレスから始まりますint32_t。これは通常の変数とは異なります。通常の変数は、その配置で割り切れる任意のアドレスを開始できますが、構造体の最初のメンバーには当てはまりません。ご存知のように、構造体のアドレスは最初のメンバーのアドレスと同じです。
  2. 構造体の内部に追加の埋め込み末尾バイトがあり、次の構造体(または構造体の配列内の次の要素)をそれ自体のアドレスから開始することができます。考えてみてくださいstruct st arr[2];arr[1](の最初のメンバー)をarr[1]4で割り切れるアドレスから開始するには、各構造体の最後に3バイトを追加する必要があります。

これは私がTheLostArt ofStructurePackingから学んだことです。

注:データ型の配置要件が何であるかは、_Alignof演算子を使用して調べることができます。offsetofまた、マクロを介して構造体内のメンバーのオフセットを取得できます。

于 2021-04-25T21:41:42.290 に答える
2

それについては仕方がありません!主題を把握したい人は、次のことをしなければなりません、

于 2019-03-09T10:45:37.460 に答える
2

これらの構造はパッドまたはパックされていますか?

それらは埋められています。

最初に頭に浮かぶ唯一の可能性は、それらを詰めることができる場所でcharありint、同じサイズである場合です。そのため、構造の最小サイズでは、char/int/char構造のパディングはできませんint/char

ただし、これには両方sizeof(int)4sizeof(char)が必要です(12と8のサイズを取得するには)。常に1つの標準によって保証されているため、理論全体が崩壊します。sizeof(char)

同じ幅charint場合、サイズは4と4ではなく、1と1になります。したがって、12のサイズを取得するには、最後のフィールドの後にパディングが必要になります。


パディングまたはパッキングはいつ行われますか?

コンパイラの実装が望むときはいつでも。コンパイラーは、フィールド間にパディングを自由に挿入でき、最後のフィールドの後に(ただし、最初のフィールドの前には挿入できません)。

一部のタイプは特定の境界に配置されているとパフォーマンスが向上するため、これは通常、パフォーマンスのために行われます。アラインされていないデータにアクセスしようとすると、機能を拒否する(つまり、クラッシュする)アーキテクチャもあります(はい、私はあなたを見ています、 ARM)。

一般に、などの実装固有の機能を使用して、パッキング/パディング(同じスペクトルの実際には反対側)を制御できます#pragma pack特定の実装でそれを実行できない場合でも、コンパイル時にコードをチェックして、要件を満たしていることを確認できます(実装固有のものではなく、標準のC機能を使用)。

例えば:

// C11 or better ...
#include <assert.h>
struct strA { char a; int  b; char c; } x;
struct strB { int  b; char a;         } y;
static_assert(sizeof(struct strA) == sizeof(char)*2 + sizeof(int), "No padding allowed");
static_assert(sizeof(struct strB) == sizeof(char)   + sizeof(int), "No padding allowed");

これらの構造にパディングがある場合、このようなものはコンパイルを拒否します。

于 2021-01-20T05:12:54.080 に答える
1

構造体のパッキングは、コンパイラーに構造体をパックするように明示的に指示した場合にのみ実行されます。パディングはあなたが見ているものです。32ビットシステムは、各フィールドをワードアラインメントにパディングしています。コンパイラーに構造体をパックするように指示した場合、それらはそれぞれ6バイトと5バイトになります。ただし、そうしないでください。それは移植性がなく、コンパイラーがはるかに遅い(そして時にはバグのある)コードを生成するようにします。

于 2010-11-29T17:23:29.120 に答える
-1

データ構造の整列は、データがコンピュータのメモリに配置され、アクセスされる方法です。これは、データの配置とデータ構造のパディングという2つの別個の関連する問題で構成されています。最近のコンピュータがメモリアドレスからの読み取りまたはメモリアドレスへの書き込みを行う場合、これはワードサイズのチャンク(たとえば、32ビットシステムでは4バイトのチャンク)以上で行われます。データアライメントとは、ワードサイズの倍数に等しいメモリアドレスにデータを配置することを意味します。これにより、CPUがメモリを処理する方法により、システムのパフォーマンスが向上します。データを整列させるには、最後のデータ構造の終わりと次のデータ構造の始まりの間に意味のないバイトを挿入する必要がある場合があります。これはデータ構造のパディングです。

  1. メモリ内のデータを整列させるために、メモリ割り当て中に他の構造体メンバーに割り当てられたメモリアドレスの間に1つ以上の空のバイト(アドレス)が挿入(または空のまま)されます。この概念は構造パディングと呼ばれます。
  2. コンピュータプロセッサのアーキテクチャは、一度に1ワード(32ビットプロセッサでは4バイト)をメモリから読み取ることができるような方法です。
  3. プロセッサのこの利点を利用するために、データは常に4バイトのパッケージとして整列され、他のメンバーのアドレスの間に空のアドレスが挿入されます。
  4. Cでのこの構造パディングの概念のため、構造のサイズは常に私たちが考えるものと同じではありません。
于 2015-08-15T15:25:46.957 に答える