结构体内存对齐

转载自https://blog.csdn.net/weixin_43866583/article/details/127718501?spm=1001.2014.3001.5502

什么是内存对齐

我们都知道,定义的变量(元素)是要按照顺序一个一个放到内存中去的,它们也不一定就是紧密排列的,是要按照一定的规则就行排放的,这就是内存对齐。

对结构体来说,元素的存储从首地址开始,第一个元素的地址和整个结构体的首地址相同,其他的每个元素放置到内存中时,它都会认为内存是按照元素自己的大小来划分空间的,所以元素放置在内存中的位置一定会在元素自己宽度(字节数)的整数倍上开始,这就是所谓的结构体内存对齐问题。

为什么要有内存对齐

考虑平台的原因。实际的硬件平台跑代码是有所区别的,一些硬件平台可以对任意地址上的任意数据进行访问,而有一些硬件平台就不行,就是有限制,所以内存对齐是一种解决办法。

考虑性能的原因。CPU访问内存时,如果内存不对齐的话,为了访问到数据的话就需要几次访问,而对齐的内存只需要访问一次即可,提高了CPU访问内存的速度。

结构体的内存对齐规则是什么

内存对齐值称为内存对齐有效值,这个值可以是1、2、4、8、16

1
2
3
4
5
规则1,结构体第一个成员一定是放在结构体内存地址里面的第1位。

规则2,成员对齐规则:除了第一个成员,之后的每个数据成员的对齐要按照成员自身的长度和内存对齐有效值进行比较,按两者中最小的那个进行对齐,即偏移的倍数。

规则3,结构体整体对齐规则:数据成员完成对齐之后,对整个结构体的大小进行对齐。按照结构体的大小必须要是内存对齐有效值和结构体中最大数据成员长度两者中的最小值的整数倍,不足的在后面补空。

规则的验证

编译器内存对齐有效值=4

1
2
3
4
5
typedef struct 
{
int a;
char b;
}StructDef_t;

首先,a为int型占4个字节,放在最开始的位置,即offset=0的位置,放在0、1、2、3的地址。

然后,用规则2:b占一个字节,内存对齐有效值为4,所以b要相对于结构体首地址的偏移要为1的倍数,放在4的地址。

最后,从上面的一步我们知道了这个结构体内的成员对齐之后占了五个字节。用规则3:结构体内最大的成员占4个字节,内存对齐有效值为4,所以整个结构体的大小要为4的倍数,5不是4的倍数,所以要在后面补齐,为8个字节。

所以,最终这个结构体占用8个字节!

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
#include<stdio.h>
typedef struct
{
int i;
char c1;
char c2;
}Test1;

typedef struct{
char c1;
int i;
char c2;
}Test2;

typedef struct{
char c1;
char c2;
int i;
}Test3;

int main()
{
printf("%d\n",sizeof(Test1)); // 输出8
printf("%d\n",sizeof(Test2)); // 输出12
printf("%d\n",sizeof(Test3)); // 输出8
return 0;
}

注意:从上面的三个结构体中可以发现,通过调换结构体里面的数据成员的位置,可以改变结构体占空间的大小,这是因为数据元素位置不同,对齐之后的结果也不同。这种方式可以用于结构体的空间优化,通过调整元素的位置,减少内存的占用!

自定义内存的对齐值

C语言中是允许用户自己定义内存对齐值的,使用一个伪指令即可

1
2
#pragma pack (n)    // 自定义对齐值,n=1,2,4,8,16
#pragma pack ( ) // 取消自定义字节对齐

1 字节对齐

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include<stdio.h>

#pragma pack (1)

typedef struct
{
int a;
char b;
}StructDef_t;


int main()
{
StructDef_t Test;
printf("a addr = %x\r\n",&Test.a);
printf("b addr = %x\r\n",&Test.b);
printf("byte = %d\r\n",sizeof(Test));
}

#pragma pack ()

在1字节内存对齐情况下,这里的结构体占5个字节!

2 字节对齐

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include<stdio.h>

#pragma pack (2)

typedef struct
{
int a;
char b;
}StructDef_t;


int main()
{
StructDef_t Test;
printf("a addr = %x\r\n",&Test.a);
printf("b addr = %x\r\n",&Test.b);
printf("byte = %d\r\n",sizeof(Test));
}

#pragma pack ()

在2字节内存对齐情况下,这里的结构体占6个字节!

4 字节对齐

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include<stdio.h>

#pragma pack (1)

typedef struct
{
int a;
char b;
}StructDef_t;


int main()
{
StructDef_t Test;
printf("a addr = %x\r\n",&Test.a);
printf("b addr = %x\r\n",&Test.b);
printf("byte = %d\r\n",sizeof(Test));
}

#pragma pack ()

​ 在4字节内存对齐情况下,这里的结构体占8个字节!