内存对齐
看下面的例子:
// 32位系统上
#include<stdio.h>
struct{
int x;
char y;
}s;
int main(){
printf("%d\n", sizeof(s)); // 输出 8
return 0;
}
32位系统上int是4字节, char是1字节, 上述一个结构体占用内存应该是 4+ 1=5byte. 但是结果却是8
为什么要内存对齐
一般的处理器以2,4,6,16字节甚至32字节为单位来取内存,即 内存存取粒度
以32位系统为例,存取粒度为4字节,该处理器只能从地址为4的倍数的内存开始读取数据。
内存对齐能够提高CPU读取数据的速度,减少CPU访问数据的出错性, 避免指针访问出错
对齐规则
对齐模数 gcc中默认对齐模数 #pargma pack(4)
有效对齐值(对齐单位): 给定值#pargma pack(4)
和结构体最长数据类型中较小的那个
对齐规则:
- 结构体第一个成员的偏移量为0, 以后每个成员相对于结构体首地址的offset都是该成员大小与有效对齐值中较小那个的整数倍, (如有需要编译器会在成员之间填充字节)
- 结构体总大小为 有效对齐值的整数倍
// 32位系统
#include<stdio.h>
struct {
int i;
char c1;
char c2;
} x1;
struct{
char c1;
int i;
char c2;
}x2;
struct{
char c1;
char c2;
int i;
}x3;
int main(){
printf("%d\n",sizeof(x1)); // 输出8
printf("%d\n",sizeof(x2)); // 输出12
printf("%d\n",sizeof(x3)); // 输出8
return 0;
}
上述结构体中最长数据类型为int 4字节
以x2为例
- c1 为1字节, 有效对齐单位4字节,则按照1 字节对齐,占用第0个单位
- i 为4字节, 有效对齐单位4字节, 相较于首地址偏移要为4的倍数,所以在前一个数据类型后面填充,使得i偏移从4开始占用4字节(占用4,5,6,7单元)
- c2 为1字节,有效对齐单位4字节,所以c相对于首地址偏移要为1的倍数,这个直接跟在i后面(占用第8单元)
- 然后使用规则2,x2中最长数据类型占用为4字节,有效对齐单位也是4字节,所以整个x2结构体需要是4的倍数,现在占用了4+4+1=9个, 需要补齐,在后面填充3字节,
故而x2一共占用了12字节
各平台上各种数据类型大小;
16位 | 32位 | 64位 | |
---|---|---|---|
char | 1个字节8位 | 1个字节8位 | 1个字节 |
short | 2个字节16位 | 2个字节16位 | 2个字节 |
int | 2个字节16位 | 4个字节32位 | 4个字节 |
long | 4个字节32位 | 4个字节32位 | 8个字节 |
long long | 8个字节 | 8个字节 | 8个字节 |
指针 | 2个字节 | 4个字节 | 8个字节 |
double | 8个字节 | 8个字节 | 8个字节 |
参考:C/C++内存对齐详解