结构体内存对齐
#include <stdio.h>
struct S1{
char c1;//1
int i;//4
char c2;//1
};
struct S2{
char c1;//1
char c2;//1
int i;//4
};
int main(){
printf("%d\n",sizeof(struct S1));//12
printf("%d\n",sizeof(struct S2));//8
return 0;
}
对齐规则:
第一个成员在与结构体变量偏移量为0的地址处
其它成员变量要对齐到某个数字(对齐数)的整数倍的地址处 对齐数 = 编译默认的一个对齐数与该成员大小的较小值。 vs中默认是8
(即:第二个成员和编译器的默认对齐数比较大小,第三个成员和编译器的默认对齐数比较大小,第四个成员和编译器的默认对齐数比较大小…..)
结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍
如果嵌套了结构体的情况,嵌套结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体对齐数)的整数倍。
如果编译器没有默认对齐数,那么大小就是成员的自身大小
S1
struct S1{ char c1;//1 int i;//4 char c2;//1 };
内存地址 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 结构体成员 c1 \ \ \ i i i i c2 \ \ \ c1 为char类型,占用1个字节;
i 为int类型,占用4个字节;
#include <stdio.h> #include <stddef.h> struct S1{ char c1;//1 int i;//4 char c2;//1 }; int main(){ printf("%d\n",sizeof(struct S1));//12 printf("%d\n",offsetof(struct S1,c1));//0 返回结构体成员在这个类型中的偏移量 printf("%d\n",offsetof(struct S1,i));//4 返回结构体成员在这个类型中的偏移量 printf("%d\n",offsetof(struct S1,c2));//8 返回结构体成员在这个类型中的偏移量 return 0; }
S2
struct S2{ char c1;//1 char c2;//1 int i;//4 };
内存地址 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 结构体成员 c1 c2 \ \ i i i i 结构体总大小为最大对齐数(每个成员变量都一个对齐数)的整数倍:最后的
i
放入后已经是整数倍了,不用偏移#include <stdio.h> #include <stddef.h> struct S2{ char c1;//1 char c2;//1 int i;//4 }; int main(){ printf("%d\n",sizeof(struct S2));//8 printf("%d\n",offsetof(struct S2,c1));//0 返回结构体成员在这个类型中的偏移量 printf("%d\n",offsetof(struct S2,c2));//1 返回结构体成员在这个类型中的偏移量 printf("%d\n",offsetof(struct S2,i));//4 返回结构体成员在这个类型中的偏移量 return 0; }
嵌套
#include <stdio.h> #include <stddef.h> struct S3{ double d;//8 char c;//1 int i;//4 }; struct S4{ char c1;//1 struct S3 s3; double d; }; int main(){ printf("%d\n",sizeof(struct S3)); printf("%d\n",sizeof(struct S4)); return 0; }
S3
struct S3{ double d;//8 char c;//1 int i;//4 };
内存地址 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 结构体成员 d d d d d d d d c \ \ \ i i i i 结构体总大小为最大对齐数(每个成员变量都一个对齐数)的整数倍,当前是16,为最大8的整数倍。
#include <stdio.h> struct S3{ double d;//8 char c;//1 int i;//4 }; int main(){ printf("%d\n",sizeof(struct S3));//16 return 0; }
S4
struct S4{ char c1;//1 struct S3 s3; double d; };
内存地址 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 结构体成员 c1 \ \ \ \ \ \ \ \ s3 s3 s3 s3 s3 s3 s3 s3 s3 s3 s3 s3 s3 s3 s3 s3 d d d d d d d d 第一个成员:从0开始,占用1个字节
第二个成员:嵌套结构体对齐到自己的最大对齐数的整数倍处,此处S3的最大对齐数是8,所以从8为位置处开始
第三个成员:自己需要占用8个自己,如果当前在所占字节的整数倍处,则从当前开始,如果没有,则跳到整数倍位置处再开始
结束位置是所有成员最大对齐数的整数倍,这里最大是8,32是8的整数倍,所以是32
#include <stdio.h> struct S4{ char c1;//1 struct S3 s3; double d; }; int main(){ printf("%d\n",sizeof(struct S4));//32 return 0; }
为什有内存对齐:
- 平台原因:不是所有的硬件平台都能访问任意地址上的任意数据;某些硬件平台只能在某些地址处取得特定类型的数据,否则会抛出硬件异常。
- 性能原因:数据结构,尤其是栈,应尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要做两次内存访问;而对齐的内存访问仅需要一次访问。
结构体的内存对齐是拿空间换时间的做法
结构体在设计的时候,要尽量满足对齐:
让占用空间小的成员集中在一起
默认对齐数
在vs上默认对齐数是8
#include <stdio.h>
#pragma pack(8)//设置默认对齐数为8,一般是2的n次方(0,1,2,3,4......)
#pragma pack()//取消设置的默认对齐数,还原为默认
#pragma pack(1)//还原默认对齐数为1
struct S{
int i;//4个字节
double d;//8个字节
};
int main(){
printf("%d\n",sizeof(struct S));
return 0;
}
设置默认对齐数为8 —> 结构体占用16个字节
内存地址 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 结构体成员 i i i i \ \ \ \ d d d d d d d d 设置默认对齐数为1 —> 结构体占用12个字节
内存地址 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 结构体成员 i i i i d d d d d d d d - 第一个成员:从0开始,占用4个字节,默认对齐数为4,对齐数:4;占用0-3,
- 第二个成员:从4开始,占用字节数为8,默认对齐数是4,取较小的对齐数:4;占用4-11
- 总大小是最大对齐数 :4,12是4的整数倍,所以是12