当前位置:首页 > 内存 > 正文

struct内存对齐原则

  • 内存
  • 2024-05-08 21:57:50
  • 8571

一、C++中结构体的大小结构体(struct)的sizeof值并不是简单地将每个元素占用的字节相加,而是考虑到存储空间的字节对齐。我们先看一下下面定义的两个结构体。
struct
{
chara;
shortb;
charc;
}S1;
struct
{
chara;
charb;
shortc;
}S2;
分别用程序测试,得到sizeof(S1)=6,sizeof(S2)=4
可见,虽然两个结构体中包含的元素相同,但由于其中存储的元素类型的顺序不同,所以占用的字节也不同。这就是字节对齐的原因。通过字节对齐,有助于加快计算机的访问速度,否则会占用更多的指令周期。
字节对齐原则
结构体默认的字节对齐一般满足三个条件:
1)结构体变量的首地址可以被其最宽的基本类型成员的大小整除;
2)结构体每个成员相对于结构体首地址的偏移量(偏移量,即每个成员的起始地址)是该成员自身大小的整数倍。如果有必要,编译器会在它们之间添加填充字节(internaladding);
3)结构体的总大小是结构体最宽基本类型成员大小的整数倍。如有必要,编译器将在最后一个成员之后添加填充。字节(尾随填充)。
注意:当结构体成员中有数组成员,如inta[10]时,必须将其视为10个整型变量才参与计算。
通过这三个原则,就不难理解上面两个结构体的区别了。
对于structS1,为了让short变量满足字节对齐准则(2),即它的存储位置相对于结构体首地址的偏移量是其自身大小的整数倍(short占用2个字节),并且在字节a后面必须填充一个字节以进行对齐;并且基于准则(3),为了满足该结构体的总大小为shortsize的整数倍,必须在c后面填充一个字节。
对于structS2来说,不需要像上面提到的那样填充字节,因为它的直接顺序存储已经满足了对齐标准。
如果将上面两个结构体中的short改为int(占用4个字节)会发生什么情况?程序得到sizeof(S1)=12,sizeof(S2)=8
利用上述标准,不难计算出这样的结果。S1中,a后填充3个字节,c后填充3个字节,总共12个字节;S2中,a、b依次存储后,填充两个字节,总共8个。字节。
当然,在某些时候也可以设置字节对齐。这需要使用#pragmapack。
#pragmapack(push)//压入并保存
#pragmapack(1)//设置1字节对齐
struct
{
chara;
shortb;
charc;
}S1;
#pragmapack(pop)//恢复之前的设置
如上图,设置对齐方式为1字节对齐,则S1不带填充字节,sizeof是每个元素占用的字节总和,即4。这在从外部二进制文件读取struct大小的数据到struct中时非常有用。
此外,还有以下方法:
·__attribute((aligned(n))),使得作用的结构体成员在n字节自然边界上对齐。如果结构中任意成员的长度大于n,则按照最大成员的长度进行对齐。
·__attribute__((packed)),取消编译过程中结构体的优化对齐,按照实际占用的字节数进行对齐。


二、结构体struct字节对齐

(这样理解就可以了,不要太混乱,也不需要看下面的内容)。

并非所有硬件平台都可以访问任意地址的数据;有些硬件平台只能访问某些地址的某些类型的数据,否则会出现硬件异常,对齐的边界将无法直接读取数据。

从上图可以看出,如果CPU的读取粒度是4字节,对于一个int类型,如果按照内存对齐来存储,处理器。。读取4个字节只需要访问内存一次内存需要访问两次
典型的流程是第一次提取4字节,提取第一个字节,第二次提取4字节,提取后三个bytes然后拼接成一个完整的int类型数据。

32位和64位程序对齐之间存在差异。一种默认为4字节,另一种默认为8字节。

Char是一个字节,你可以把它放在任何地方。当大于1字节时,需要注意对齐粒度。禁止多字节数据超出粒度。
结构体前面的区域负责后续数据的地址号,如果大于4的话。小数必须是2的倍数。int是4的倍数,double是8的倍数。最后一个字段负责下一个结构的开始。(放入数组时结构需要对齐)