( คำตอบข้างต้นอธิบายเหตุผลค่อนข้างชัดเจน แต่ดูเหมือนจะไม่ชัดเจนเกี่ยวกับขนาดของช่องว่างภายในดังนั้นฉันจะเพิ่มคำตอบตามสิ่งที่ฉันเรียนรู้จากThe Lost Art of Structure Packingมันมีวิวัฒนาการไม่ จำกัดC
แต่ ยังใช้กับGo
, Rust
. )
จัดหน่วยความจำ (สำหรับ struct)
กฎ:
- ก่อนที่สมาชิกแต่ละคนจะมีช่องว่างภายในเพื่อให้เริ่มต้นจากที่อยู่ที่สามารถแบ่งได้ตามขนาดของมัน
เช่นในระบบ 64 บิตint
ควรเริ่มที่ที่อยู่หารด้วย 4 และlong
8 short
ด้วย 2
char
และ char[]
เป็นพิเศษอาจเป็นที่อยู่หน่วยความจำใด ๆ ดังนั้นพวกเขาจึงไม่ต้องการช่องว่างภายในก่อนหน้าพวกเขา
- สำหรับ
struct
นอกเหนือจากการจัดตำแหน่งที่จำเป็นสำหรับสมาชิกแต่ละคนขนาดของโครงสร้างทั้งหมดจะถูกจัดให้อยู่ในขนาดที่หารได้ตามขนาดของสมาชิกรายใหญ่ที่สุดโดยการขยายที่ท้าย
เช่นถ้าสมาชิกที่ใหญ่ที่สุดของ struct ก็จะlong
หารด้วย 8 int
จากนั้นเป็น 4 จากนั้นเท่ากับshort
2
คำสั่งของสมาชิก:
- ลำดับของสมาชิกอาจส่งผลกระทบต่อขนาดของโครงสร้างจริงดังนั้นควรคำนึงถึงสิ่งนั้นด้วย เช่น
stu_c
และstu_d
จากตัวอย่างด้านล่างมีสมาชิกเหมือนกัน แต่ในลำดับที่ต่างกันและส่งผลให้มีขนาดแตกต่างกันสำหรับ 2 struct
ที่อยู่ในหน่วยความจำ (สำหรับ struct)
กฎ:
- ที่
อยู่โครงสร้างระบบ 64 บิตเริ่มต้นจาก(n * 16)
ไบต์ ( คุณสามารถเห็นในตัวอย่างด้านล่างทั้งหมดที่อยู่ hex พิมพ์ structs จบลงด้วย0
. )
เหตุผล : ที่ใหญ่ที่สุดที่เป็นไปได้สมาชิก struct บุคคลคือ 16 ไบต์ ( long double
)
- (อัปเดต)หากโครงสร้างมีเพียง
char
สมาชิกในฐานะที่อยู่ของมันสามารถเริ่มต้นที่ที่อยู่ใดก็ได้
พื้นที่ว่าง :
- ช่องว่างระหว่าง 2 structs สามารถนำมาใช้โดยตัวแปรที่ไม่ใช่โครงสร้างที่อาจพอดี.
เช่นในtest_struct_address()
ด้านล่างตัวแปรx
อยู่ระหว่าง struct ที่อยู่ติดกันและ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