标签:
本节研究地址对齐的相关问题;
说明几点:
(1)地址对齐可以简化处理器和存储器系统之间的硬件设计;如果可以保证所有的int类型地址对齐成4的倍数,就可以使用一个存储器操作读或写值,相反如果一个int型的变量存放在奇地址上,那么要进行两次存储器读后进行拼凑成int型变量才可以;
(2)在GCC中,2字节数据类型(如short)地址对齐必须是2的倍数,因此地址最低位必须是0,而较大的数据类型(如int、int*、float、包括long long、double、long double)的地址对齐必须是4的倍数,因此地址最低两位必须是00;注意Mircosoft对齐更严格,任何K字节的基本对象的地址必须是K的倍数,也就是说long long、double这样的地址必须是8的倍数;
(3)long double(实际上为10个字节),在GCC和windows都中分配12个字节,都进行4字节地址对齐;
(4)在#pragma pack (n)中,n为1,2,4,8这样的2的次方;在缺省情况下,编译器按照数据类型的自身边界进行对齐,如short的对齐边界为2字节;但是在指定了#pragma pack (n)后,对于所有比n大的数据类型边界都均按照n来对齐;
(5)在__attribute__((aligned(n))中,n为1,2,4,8这样的2的次方;它表示在一个结构体(联合体)在分配空间时的地址对齐方式,最后的地址一定是按照n字节对齐的;
(注:本文的编译器为GCC)
代码片段如下:
struct P1 { float a; char b; int c; char d; }; #pragma pack (1) struct P2 { float a; char b; int c; char d; }; #pragma pack () #pragma pack (2) struct P3 { float a; char b; int c; char d; }; #pragma pack () #pragma pack (4) struct P4 { float a; char b; int c; char d; }; #pragma pack () #pragma pack (8) struct P5 { float a; char b; int c; char d; }; #pragma pack () int main(void) { printf("P1, size %d\n", sizeof(P1)); printf("P2, size %d\n", sizeof(P2)); printf("P3, size %d\n", sizeof(P3)); printf("P4, size %d\n", sizeof(P4)); printf("P5, size %d\n", sizeof(P5)); printf("P1, address------\n"); P1 p1; printf("%p\n", &p1); printf("a: %p\n", &p1.a); printf("b: %p\n", &p1.b); printf("c: %p\n", &p1.c); printf("d: %p\n\n", &p1.d); printf("P2, address------\n"); P2 p2; printf("%p\n", &p2); printf("a: %p\n", &p2.a); printf("b: %p\n", &p2.b); printf("c: %p\n", &p2.c); printf("d: %p\n\n", &p2.d); printf("P3, address------\n"); P3 p3; printf("%p\n", &p3); printf("a: %p\n", &p3.a); printf("b: %p\n", &p3.b); printf("c: %p\n", &p3.c); printf("d: %p\n\n", &p3.d); printf("P4, address------\n"); P4 p4; printf("%p\n", &p4); printf("a: %p\n", &p4.a); printf("b: %p\n", &p4.b); printf("c: %p\n", &p4.c); printf("d: %p\n\n", &p4.d); printf("P5, address------\n"); P5 p5; printf("%p\n", &p5); printf("a: %p\n", &p5.a); printf("b: %p\n", &p5.b); printf("c: %p\n", &p5.c); printf("d: %p\n\n", &p5.d); }
输出如下:
P1, size 16 P2, size 10 P3, size 12 P4, size 16 P5, size 16 P1, address------ 0xbfacc840 a: 0xbfacc840 b: 0xbfacc844 c: 0xbfacc848 d: 0xbfacc84c P2, address------ 0xbfacc836 a: 0xbfacc836 b: 0xbfacc83a c: 0xbfacc83b d: 0xbfacc83f P3, address------ 0xbfacc82a a: 0xbfacc82a b: 0xbfacc82e c: 0xbfacc830 d: 0xbfacc834 P4, address------ 0xbfacc818 a: 0xbfacc818 b: 0xbfacc81c c: 0xbfacc820 d: 0xbfacc824 P5, address------ 0xbfacc808 a: 0xbfacc808 b: 0xbfacc80c c: 0xbfacc810 d: 0xbfacc814说明几点:
(1)P1和P4是同样的地址对齐,都是4地址对齐;而对于P2各数据类型只进行1地址对齐,因此大小就是所有数据类型的大小;对于P3由于#pragma pack (2),因此所有的数据类型对齐边界都不会超过2,因此像int,float这样的数据类型的对齐边界也是2;
(2)对于P4和P5来说,由于数据类型中任何一个数据类型的对齐边界最大为4,而#pragma pack (4或8)的指定并不会影响这些数据类型的对齐边界;
上述各个结构体的示意图如下:
代码片段如下
struct P1 { float a; char b; int c; char d; }__attribute__((aligned(4))); #pragma pack (1) struct P2 { float a; char b; int c; char d; }__attribute__((aligned(4))); #pragma pack () #pragma pack (2) struct P3 { float a; char b; int c; char d; }__attribute__((aligned(4))); #pragma pack () int main(void) { printf("P1, size %d\n", sizeof(P1)); printf("P2, size %d\n", sizeof(P2)); printf("P3, size %d\n", sizeof(P3)); printf("P1, address------\n"); P1 p1; printf("%p\n", &p1); printf("a: %p\n", &p1.a); printf("b: %p\n", &p1.b); printf("c: %p\n", &p1.c); printf("d: %p\n\n", &p1.d); printf("P2, address------\n"); P2 p2; printf("%p\n", &p2); printf("a: %p\n", &p2.a); printf("b: %p\n", &p2.b); printf("c: %p\n", &p2.c); printf("d: %p\n\n", &p2.d); printf("P3, address------\n"); P3 p3; printf("%p\n", &p3); printf("a: %p\n", &p3.a); printf("b: %p\n", &p3.b); printf("c: %p\n", &p3.c); printf("d: %p\n\n", &p3.d); }
输出如下:
P1, size 16 P2, size 12 P3, size 12 P1, address------ 0xbf98d9e0 a: 0xbf98d9e0 b: 0xbf98d9e4 c: 0xbf98d9e8 d: 0xbf98d9ec P2, address------ 0xbf98d9d4 a: 0xbf98d9d4 b: 0xbf98d9d8 c: 0xbf98d9d9 d: 0xbf98d9dd P3, address------ 0xbf98d9c8 a: 0xbf98d9c8 b: 0xbf98d9cc c: 0xbf98d9ce d: 0xbf98d9d2说明几点:
(1)__attribute__((aligned(4)))仅仅是将结构体本身对齐,对结构体的内部数据的对齐方式是没有影响的;
结构体示意图如下:
代码片段如下
struct P1 { char a; double b; char c; long long d; }; int main(void) { printf("P1, size %d\n", sizeof(P1)); printf("P1, address------\n"); P1 p1; printf("%p\n", &p1); printf("a: %p\n", &p1.a); printf("b: %p\n", &p1.b); printf("c: %p\n", &p1.c); printf("d: %p\n\n", &p1.d); }
输出如下:
P1, size 24 P1, address------ 0xbfbe4348 a: 0xbfbe4348 b: 0xbfbe434c c: 0xbfbe4354 d: 0xbfbe4358
说明几点:
(1)double和long long在GCC中并不是按照8个字节对齐的,而是按照4字节对齐的;和windows中不一样;
结构体示意图如下:
标签:
原文地址:http://blog.csdn.net/skyuppour/article/details/44964265