关于C语言中结构体的内存对齐,要搞清楚四个问题:
1、什么是结构体内存对齐?
2、为什么要内存对齐?
3、如何对齐?
4、如何避免内存对齐的影响?
首先来看第一个问题:什么是结构体内存对齐?
先来看一段代码:
#include <stdio.h>
#include <stddef.h>
struct S
{
int i;
char c;
double d;
};
int main()
{
struct S s;
//printf("%d\n", offsetof(struct S, i));
printf("%d\n", sizeof(s));
return 0;
}
在windows环境下这段代码的输出结果是多少呢?如果你的答案是13,那么恭喜你成功的掉到了坑里,正确的答案是16!为什么会是16呢?请看下图解析:
如上图所示,结构体在内存中的存储方式跟我们想象中的不大一样,而是要遵守一定的原则,这种原则就是内存对齐,具体的原则内容会在下文提到。显而易见的的是结构体这样在内存中存储会浪费一定的空间(如图中黄色区域就是浪费掉的空间),既然浪费了空间,那为什么还要内存对齐?
接着来说第二个问题:为什么要内存对齐?
为什么要内存对齐呢?原因有两个:
1、使用平台的原因:不是在所有平台上都可以访问任意内存的地址,所以需要适当的进行偏移,偏移到我们可以访问的位置上进行数据存储。
2、内存对齐可以使访问速度提高:为了访问未对齐的内存,处理器需要两次内存访问;然而,对齐的内存访问仅需要一次访问。
怎么样来理解第二个原因呢?请看下图:
第二个问题理解清楚之后再来看第三个问题:如何对齐?
在此将阐述内存对齐的三个规则:
1、数据成员对齐规则:结构体中的第一个数据成员放在0偏移处,从第二个成员开始,每个数据成员存储的起始位置要从对齐数(该成员自身的大小和默认对齐数的较小值)的整数倍开始;
2、结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。(struct a里存有struct b,b里有char,int,double等元素,那b应该从8的整数倍开始存储。)
3、收尾工作:结构体的总大小,必须是其内部最大成员的整数倍,不足的要补齐。
了解清楚规则之后再看第一个问题中所举的例子就比较容易啦
最后我们来看最后一个问题:如何避免内存对齐的影响?
方法1、尽量将结构体中相同类型的数据集中到一起,这样做可以有效的节省空间。
例1:
struct test1
{
char c1;
short s;
char c2;
int i;
};
很容易得出该结构体的大小为12;
例2:
struct test2
{
char c1;
char c2;
short s;
int i;
};
很容易得出该结构体的大小为8;
可见将相同类型的结构体成员集中到一起可以有效的节省空间。
方法2、使用#pragma pack()来修改编译器的默认对齐数
使用指令#pragma pack(n)将编译器的默认对齐数改为n
使用指令#pragma pack()编译器将取消自定义字节对齐方式
vc/vs环境下的默认对齐数为–8
Liunx环境下的默认对齐数为–8
看如下例子:(在vs环境下)
#pragma pack(1)
struct test1
{
charchar c2;
int i;
};
#pragma pack()
ragma pack()
“`;;
很容易得出该结构体的大小为8而不是12