目录
- 1. 内存对齐规则
- 2. 简单易懂的内存对齐示例
- 2.1 简单结构体
- 2.2 含位域的结构体
- 2.3 空类的大小
- 2.4 嵌套结构体
- 3. 为什么需要内存对齐?
- 4. 类型在不同系统下所占字节数
1. 内存对齐规则
- 第一个成员在与结构体变量偏移量为0的位置处。
- 其他成员变量要对齐到某个数字(对其数)的整数倍的地址处。对其数=编译器默认的一个对齐数与该成员大小的较小者。
vs中默认的值是8 Linux中默认的值是4
- 结构体总大小为最大对其数(每一个成员变量都有一个对其数)的整数倍。
- 如果嵌套了结构体对齐到自己的最大对其数是整数倍处,结构体的整体大小就是最大对齐数(含嵌套结构体的对齐数)的整数倍。
- 如对内存对齐有明确要求,可用
#pragma pack(n)
指定(n必须是2的N次方),以n和结构体中最长数据成员长度中较小者为有效值
可能只看上面的描述你还是有点搞不懂,那么我们可以看一下具体的例子和视图讲解:
struct s1{
char c1;
int i;
char c2;
};
//结构体总大小为:12
2. 简单易懂的内存对齐示例
2.1 简单结构体
struct s1
{
char str; //1字节
short x; //2字节
int num; //4字节
};
// sizeof(s1) = 8 // (1+1) + 2 + 4 = 8 第2个加1处的内存是空的
2.2 含位域的结构体
一个位域必须存储在同一个字节中,不能跨两个字节。如一个字节所剩空间不够存放另一位域时,应从下一单元起存放该位域。
struct s2
{
unsigned char a:7; //字段a占用了一个字节的7个bit
unsigned char b:2; //字段b占用了2个bit
unsigned char c:7; //字段c占用了7个bit
}s1;
// sizeof(s1) = 3 // 1 + 1 + 1 = 3
struct s3
{
char t : 4; //4表示在一个字节中占4个比特位
char k : 4;
};
// sizeof(s3) = 1 //0.5 + 0.5 = 1
struct s4
{
char t : 4; //4表示在一个字节中占4个比特位
char k : 4;
unsigned short i : 8;
char a;
};
// sizeof(s4) = 6 //(0.5 + 0.5 + 1) + 2 + (1+1) = 6
// 解释:0.5和0.5占一个字节,两个占4比特的刚好可以放在一个字节中,short为2字节,对齐数是2,所以0.5 + 0.5 + 1中加的1是空的
2.3 空类的大小
class A {};
// sizeof(A) = 1
空类,没有任何成员变量和成员函数,编译器是支持空类实例化对象的,对象必须要被分配内存空间才有意义,大小: 1Byte (字节)
2.4 嵌套结构体
class A {
private:
double dd;
int ii;
int* pp;
};
class Test {
private:
int i;
A a;
double d;
char* p;
};
int main()
{
A a1;
Test test;
cout << sizeof(a1) << endl;
cout << sizeof(test) << endl;
}
// x86(32位操作系统)平台运行结果:40
// x64(64位操作系统)平台运行结果:48
对其数 = 编译器默认的一个对齐数与该成员大小的较小者,vs中默认的值是8
-
x86:指针是4字节,类A的大小是16,8与16的较小者是8。内存大小为:8 + 16 + 8 + 4 = 36,又因为36不是最大对齐数8的倍数,所以内存向后偏移,大小为 40
-
x64:指针是8字节,类A大小为24,8与24的较小者是8。内存大小为:8 + 24 + 8 +8 = 48,48刚好是最大对齐数8的倍数,所以内存大小为48
3. 为什么需要内存对齐?
字节对齐主要是为了提高内存的访问效率
cpu一次能读取多少内存要看数据总线是多少位,如果是16位,则一次只能读取2个字节,如果是32位,则可以读取4个字节,并且cpu不能跨内存区间访问。
假设有这样一个结构体如下:
struct s
{
char a;
int b;
};
假设地址空间是类似下面这样的:
-
在没有字节对齐的情况下,变量a就是占用了
0x00000001
这一个字节,而变量b则是占用了0x00000002~0x000000005
这四个字节,那么cpu如果想从内存中读取变量b,首先要从变量b的开始地址0x00000002
读到0x0000004
,然后再读取一次0x00000005
这个字节,相当于读一个int,cpu从内存读取了两次。 -
而如果进行字节对齐的话,变量a还是占用了
0x00000001
这一个字节,而变量b则是占用了0x00000005~0x00000008
这四个字节,那么cpu要读取变量b的话,就直接一次性从0x00000005
读到0x00000008
,就一次全部读取出来了。 -
所以说,字节对齐的根本原因其实在于cpu读取内存的效率问题,对齐以后,cpu读取内存的效率会更快。结构体的内存对齐是拿空间来换取时间的做法
4. 类型在不同系统下所占字节数
类型 | win32 | win64 | linux32 | linux64 |
---|---|---|---|---|
char | 1 | 1 | 1 | 1 |
short | 2 | 2 | 2 | 2 |
int | 4 | 4 | 4 | 4 |
long | 4 | 4 | 4 | 8 |
long long | 8 | 8 | 8 | 8 |
float | 4 | 4 | 4 | 4 |
double | 8 | 8 | 8 | 8 |
void*(指针) | 4 | 8 | 4 | 8 |