文章目录
内存对齐的原因
- 平台原因:不是所有的的硬件平台都能访问任意地址上的任意数据;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
- 性能原因:对齐的内存访问可以省时,相当于说,内存对齐是拿空间换时间的做法。
结构体的内存对齐是拿空间来换取时间的做法。
内存对齐规则
- 第一个成员在与结构体变量偏移量为0的地址处。
- 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。 对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。 VS中默认的对齐数为8,Linux中的默认的对齐数为4。
- 结构体总大小为最大对齐数(每个成员变量除了第一个成员都有一个对齐 数)的整数倍。
- 如果嵌套了结构体的情况,嵌套的结构体的对齐数是自己成员变量的最大对齐数。
空类的计算
空类的大小是:1个字节,用来占位。
嵌套结构体大小
struct Node1{
int a;
char c;
};
struct Node2
{
int data; //对齐数是0,大小是4
struct Node1 node;//默认对齐数是Node1的成员最大对齐数4,大小是8
};
int main()
{
cout<<sizeof(Node2) << endl;//结果是12
}
栗子说明结构体设计原则
- 让占用空间小的成员尽量集中在一起。
struct A
{ //对齐数 至此所占内存大小
char d = 'a'; // 0 1
short b = 2; // 2 4
int a = 20; // 4 8
long c = 8; // 4 12
}x;
struct B{ //对齐数 至此所占内存大小
int a = 20; // 0 4
short b = 2; // 2 6
long c = 8; // 4 12
char d = 'a'; // 1 16
}y;
我们看到,仅仅是因为结构体中成员的先后顺序不同就会造成,结构体大小不同。那在设计结构体的时候,我们既要满足对齐,又要节省空间,如何做到:让占用空间小的成员尽量集中在一起。
- 结构体传参的时候,要传结构体的地址。
位段大小计算
- 位段的成员可以是 int unsigned int signed int 或者是 char (属于整形家族)类型。
- 位段的成员名后边有一个冒号和一个数字,数字代表改成员变量所占的位数。
- 位段的空间开辟:如果结构体中只含有char类型成员,则结构体以1字节为单位开辟空间,否则以4字节开辟空间。
struct A
{
int _a : 2;
int _b : 5;
int _c : 10;
int _d : 30;
};
int类型按照4字节大小开辟空间,第一块四字节空间可以存下_a、_b、_c,因为2+5+10<=32,_d只能重新开辟4个字节,所以结果是8字节。
struct S
{
char a : 3;
char b : 4;
char c : 5;
char d : 4;
};
char类型按照1字节大小开辟空间,第一块1字节空间可以存下a、b,因为3+4<=8,_c、_d只能重新开辟2个字节,分别存储,所以结果是3字节。
struct C
{
char a : 3;
char b : 4;
int c : 10;
int d : 30;
};
答案是12,存在int,该结构体以4字节开辟空间,第一块空间可以存下a和b,还需要两块四字节空间f分别存储c和d,总共12字节。
struct C
{
char a : 3;
char b : 4;
long long int c : 30;
};
答案是16。4+8存储数据,结构体总大小要是最大对齐数的整数倍,最大对齐数是8,所以结果是16。