考慮してください:
struct mystruct_A
{
char a;
int b;
char c;
} x;
struct mystruct_B
{
int b;
char a;
} y;
構造のサイズはそれぞれ12と8です。
これらの構造物はパディングまたはパックされていますか?
パディングまたはパッキングはいつ行われますか?
padding
物事を大きくします。packing
物事を小さくします。全く違います。
考慮してください:
struct mystruct_A
{
char a;
int b;
char c;
} x;
struct mystruct_B
{
int b;
char a;
} y;
構造のサイズはそれぞれ12と8です。
これらの構造物はパディングまたはパックされていますか?
パディングまたはパッキングはいつ行われますか?
padding
物事を大きくします。packing
物事を小さくします。全く違います。
回答:
パディング は、構造体のメンバーを「自然な」アドレス境界に揃えます。たとえば、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;
一方、パッキングは、コンパイラーがパディングを行うのを防ぎます-これは明示的に要求される必要があります-GCCの下で__attribute__((__packed__))
は次のようになります:
struct __attribute__((__packed__)) mystruct_A {
char a;
int b;
char c;
};
6
32ビットアーキテクチャでサイズの構造を生成します。
ただし、非整列メモリーアクセスは、それを許可するアーキテクチャー(x86やamd64など)では低速であり、SPARCなどの厳密な整列アーキテクチャーでは明示的に禁止されています。
(上記の答えは非常に明確な理由を説明したが、完全ので、私は私から学んだことに基づいて答えを追加します、パディングのサイズについて明確ではないと思われる構造体パッキングのロストアート、それがない限界まで進化してきたC
が、また、適用しますGo
、Rust
。)
ルール:
int
は、4で割り切れるアドレスlong
、8で割り切れるアドレス、2 で始まる必要がありますshort
。char
そしてchar[]
、彼らは彼らの前にパディングを必要としないので、任意のメモリアドレス可能性があり、特別です。struct
、各個々のメンバーのアライメント必要以外、全体構造体自体のサイズは終わりにパディングすることによって、最大の個々のメンバーのサイズによってサイズ割り切れるに整列されます。long
、8で割り切れるそしてint
、その後、4でshort
、その後2で。メンバーの順番:
stu_c
およびstu_d
以下の例から同じメンバーを持っていますが、異なる順序で、及び2つの構造体のために異なるサイズになります。ルール:
(n * 16)
バイトから始まります。(以下の例では、構造体の印刷された16進アドレスはすべてで終わり0
ます。)long double
)です。char
asメンバーのみが含まれる場合、そのアドレスは任意のアドレスから開始できます。空のスペース:
test_struct_address()
以下でx
は、隣接する構造体g
との間に変数が存在しますh
。が宣言されてx
h
x
g
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です。
<The Lost Art of C Structure Packing>
、この答えよりも少し長いと思っていても、ルールをかなりよく説明しています。この本は無料でオンラインで入手できます:catb.org/esr/structure-packing
私はこの質問が古いことを知っており、ここでのほとんどの回答はパディングを非常にうまく説明していますが、私自身がそれを理解しようとしている間に、起こっていることの「視覚的な」イメージがあると思いました。
プロセッサは、一定のサイズ(ワード)の「チャンク」でメモリを読み取ります。プロセッサワードの長さが8バイトであるとします。これは、メモリを8バイトのビルディングブロックの大きな行と見なします。メモリから情報を取得する必要があるたびに、これらのブロックのいずれかに到達して取得します。
上の画像のように、Char(1バイト長)がどこにあるかは関係ありません。これは、これらのブロックの1つ内にあり、CPUが1ワードのみを処理する必要があるためです。
4バイトの整数や8バイトの倍数など、1バイトより大きいデータを処理する場合、メモリ内でのデータの整列方法によって、CPUで処理する必要のあるワード数が異なります。4バイトのチャンクが常にブロックの内側に収まるように配置されている場合(メモリアドレスは4の倍数)、1ワードのみを処理する必要があります。それ以外の場合、4バイトのチャンクは、ブロックの一部と別のブロックの両方を持ち、プロセッサがこのデータを読み取るために2ワードを処理する必要があります。
同じことが8バイトdoubleにも当てはまりますが、常にブロック内にあることを保証するために、8の倍数のメモリアドレスになければなりません。
これは8バイトのワードプロセッサを考慮していますが、この概念は他のサイズのワードにも適用されます。
パディングは、それらのデータ間のギャップを埋めてそれらがそれらのブロックと整列していることを確認することで機能し、メモリの読み取り中のパフォーマンスを向上させます。
ただし、他の回答に記載されているように、パフォーマンスよりもスペースの方が重要な場合があります。RAMが少ないコンピュータで大量のデータを処理している可能性があります(スワップ領域を使用することはできますが、処理速度は非常に遅くなります)。最小のパディングが行われるまで(他の回答で大きく例示されているように)、プログラム内の変数を配置できますが、それで十分でない場合は、パッキングとは明示的にパディングを無効にできます。
構造パッキングは、構造パディングを抑制します。配置が最も重要な場合に使用されるパディング、スペースが最も重要な場合に使用されるパッキングです。
一部のコンパイラでは#pragma
、パディングを抑制したり、nバイトにパックしたりできます。これを行うためのキーワードを提供するものもあります。一般に、構造パディングの変更に使用されるプラグマは、次の形式になります(コンパイラによって異なります)。
#pragma pack(n)
たとえば、ARMには__packed
構造パディングを抑制するキーワードが用意されています。これについては、コンパイラのマニュアルを参照してください。
つまり、パック構造はパディングのない構造です。
一般的にパックされた構造が使用されます
スペースを節約する
何らかのプロトコルを使用してネットワーク経由で送信するデータ構造をフォーマットする(
エンディアンを処理する必要があるため、これはもちろん良い方法ではありません)
それについてお尻はありません!主題を把握したい人は、次のことを行う必要があります、
- エリックS.レイモンドによって書かれた構造のパッキングの失われた芸術を熟読する
- 一瞥エリックのコード例
- 最後に重要なことですが、構造体が最大の型の配置要件に合わせて配置されるパディングに関する次のルールを忘れないでください 。
パディングのルール:
ルール2の理由:次の構造体を検討してください。
この構造体の(2つの構造体の)配列を作成する場合、最後にパディングは必要ありません。
したがって、構造体のサイズ= 8バイト
次のように別の構造体を作成するとします。
この構造体の配列を作成する場合、最後に必要なパディングのバイト数には2つの可能性があります。
A.最後に3バイトを追加し、それをLongではなくintに揃えると、次のようになります。
B.最後に7バイトを追加し、それをLongに揃えると、次のようになります。
2番目の配列の開始アドレスは8の倍数(つまり24)です。構造体のサイズ= 24バイト
したがって、構造体の次の配列の開始アドレスを最大のメンバーの倍数に揃えることで(つまり、この構造体の配列を作成する場合、2番目の配列の最初のアドレスは、倍数のアドレスから開始する必要があります)構造体の最大のメンバーのここで、これは24(3 * 8))であり、最後に必要なパディングバイト数を計算できます。
データ構造のアラインメントは、データがコンピューターのメモリに配置されアクセスされる方法です。これは、2つの別個の関連する問題で構成されています。データの配置とデータ構造のパディングです。最近のコンピューターがメモリアドレスの読み取りまたは書き込みを行う場合、これはワードサイズのチャンク(32ビットシステムでは4バイトチャンクなど)以上で行われます。データアライメントとは、ワードサイズの倍数に等しいメモリアドレスにデータを配置することを意味します。これにより、CPUによるメモリの処理方法により、システムのパフォーマンスが向上します。データを整列するには、最後のデータ構造の終わりと次のデータ構造のパディングである次のデータ構造の始まりの間に、意味のないバイトを挿入する必要がある場合があります。