学习总结
内存对齐三原则:
- 数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小或者成员的子成员大小(只要该成员有子成员,比如说是数组,结构体等)的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储。
- 结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储.(struct a里存有struct b,b里有char,int ,double等元素,那b应该从8的整数倍开始存储.)
- 结构体的总大小,也就是sizeof的结果,.必须是其内部最大成员的整数倍.不足的要补齐。
首先了解内存对齐是什么?
简单来讲内存对齐是一种提高内存访问速度的策略,cpu在访问未对其的内存需要经过俩次内存访问,而经过内存对齐一次就可以了。
首先第一点要知道的是,内存对齐默认为4字节对齐,当有比4字节更大的类型时,按更大的类型的字节进行对齐。
例如:
#include <iostream>
using namespace std;
struct Examples
{
char a;
short b;
};
int main()
{
Examples ls;
cout << sizeof(Examples) << endl;
printf("%p\n", &(ls.a));
cout << &(ls.b) << endl;
//char a 的内存起始地址为 012FFE6C
//short b 的内存起始地址为 012FFE6E
return 0;
}
#include <iostream>
using namespace std;
struct Examples
{
char a;
short b;
long long c;
};
int main()
{
Examples ls;
cout << sizeof(Examples) << endl;
printf("%p\n", &(ls.a));
cout << &(ls.b) << endl;
cout << &(ls.c) << endl;
return 0;
}
/*
此时 Examples结构体的大小为24字节
ls.a的起始地址是 00DAFD90
ls.b的起始地址是 00DAFD92
ls.c的起始地址是 00DAFD98
*/
由上可证明 内存对齐的默认对齐大小为 4字节,当有比4字节更大的类型时,按更大的类型的字节进行对齐。
但是同样也要注意一点,内存对齐的标准时对于不同类型的数据成员之间的对齐。
然后我们由上例进行进一步的改造来加深一下我们对内存对齐的理解
#include <iostream>
using namespace std;
struct Examples
{
char a; //4
int b; //4
long long c; //8
short d; //2
char f; //6
};
int main()
{
Examples ls;
cout << sizeof(Examples) << endl;
printf("%p\n", &(ls.a));
cout << &(ls.b) << endl;
cout << &(ls.c) << endl;
cout << &(ls.d) << endl;
printf("%p\n", &(ls.f));
return 0;
}
/*
此时 Examples结构体的大小为24字节
ls.a的起始地址是 010FF8B4
ls.b的起始地址是 010FF8B8
ls.c的起始地址是 010FF8BC
ls.d的起始地址是 010FF8C4
ls.f的起始地址是 010FF8C6
*/
首先我们来看一下这个结构体各个成员的内存大小
a = 4字节
b = 4字节
c = 8字节
d = 2字节
f = 6字节
结构体大小为 4 + 4 + 8 + 2 + 6 =24
然后我们来理解一下为何各个数据成员的内存大小为这些呢?
1、a为何为4字节
a 为 char 类型 1 字节的数据成员,而 b 为int 类型4 字节的数据成员。所以 a要对b进行内存对齐,所以此时的 a为4个字节。
2、b为何也为4字节
c 的类型为 long long ,8字节的数据成员。b为4字节,所以按照内存对齐规则,此时的 b应该为8字节才对,为何是4字节呢?因为 a的内存大小此时也为4字节,4 + 4 = 8字节 同样符合内存对齐规则,所以此时的b也为4字节。同理 d 与 f 也可套用此原理。d 的内存大小为2字节,而 f 的内存大小为6字节,2 + 6 = 8,同样符合内存对齐规则。原因就是内存地址对齐时,如果数据成员之间的大小相加不超过需要对齐的内存大小时,多个数据成员会整合到同一个内存空间中。
我们依旧可以使用一个例子来证明:
#include <iostream>
using namespace std;
struct Examples
{
char a;
short g;
int b;
long long c;
short d;
char f;
};
int main()
{
Examples ls;
cout << sizeof(Examples) << endl;
printf("%p\n", &(ls.a));
cout << &(ls.g) << endl;
cout << &(ls.b) << endl;
cout << &(ls.c) << endl;
cout << &(ls.d) << endl;
printf("%p\n", &(ls.f));
return 0;
}
/*
此时 Examples结构体的大小依旧为24字节
ls.a的起始地址是 00D6FEC4
ls.g的起始地址是 00D6FEC6
ls.b的起始地址是 00D6FEC8
ls.c的起始地址是 00D6FECC
ls.d的起始地址是 00D6FED4
ls.f的起始地址是 00D6FED6
*/