c语言结构体对齐的原因

https://www.geeksforgeeks.org/structure-member-alignment-padding-and-data-packing/
https://stackoverflow.com/questions/3903164/why-misaligned-address-access-incur-2-or-more-accesses
https://en.wikipedia.org/wiki/Data_structure_alignment

  • 实际上不仅仅是c语言,很多语言,或者说电脑程序,底层逻辑都是需要数据对齐,这是由电脑cpu,内存等基础架构决定的。

  • 系统32位或者64位,对应4或者8字节。以64位为例,cpu每次从内存读取数据是8字节。

  • 为了增加效率,系统会按照8条线路从多个内存芯片(或者分区,类似的概念)分别读取1字节,这样是并行的,一次IO就可以解决。

  • 并且cpu读取数据,在偶数上读取速度更快,如果没有对齐,比如8个字节,从1地址开始,到8地址结束,则需要先读取0-7,再读取8-15,再把两次读取的数据拼接起来,中间增加了很多操作。如果数据保存在0-7位置上,只需要读取一次就行,并且获得数据的时候直接从0位置开始就好了,而不用计算偏移从1开始。

  • 结构体的大小为什么需要按照最大成员变量填充对齐呢?也是因为这个原因,cpu读取内存数据可以说是固定位置大小读取的,64位系统就是从内存的0-7开始,然后是8-15,16-23以此类推。如果结构体最后没有填充,按照最大变量类型对齐,就会导致连续的结构体数据(比如结构体数组)中的变量位置不再合理。比如int64,本来是0-7,8-15,两次读取就可以完成,如果因为前面没对齐,导致下一个结构体起始位置不对,int64有可能从2开始,那么就是2-7,8-15,16-17,需要三次才能取出来,还要拼接,并且像蝴蝶效应一样,会导致下面的数据出现更多这种问题。

typedef struct structc_tag
{
   char        c;
   double      d;
   int         s;
} structc_t;

按照参考网站给定的例子,c占用1个,填充7个,因为d要从其倍数开始,d占用8个,s占用4个,如果s后面不填充,那么结构体大小是20,如果定义structc_t structc_array[3];就会导致第二个数据是从20开始,d就会从28开始保存,导致读取数据出现上面说的,需要3次,还要拼接,所以结构体最后也要满足条件,就是最大变量的倍数,所以strctc_t是24,int后面需要填充4.

  • 除了结构体,实际上参数传递,局部变量等等,凡是需要读写内存的数据,理论上都会进行对齐操作,当然都是系统自己处理的,目的就是为了简化系统架构设计,增加效率,减少出错和干扰。如果有非对齐的数据会怎么样,这个要根据系统架构而定,支持的架构是没问题的,而不支持的架构就会报错。

  • 为什么不能对每个内存区域单独操作呢?现在我们知道,读取内存每次读取8字节(64位cpu),每次偏移是固定的一个参数,偏移0,就是0-7,偏移1,就是8-15,为什么不能为每一条通道增加一个偏移,单独计算,这样就可以随意读取,也不用对齐。这还是为了稳定性和简介性。如果增加8个,cpu设计复杂度会大大增加,各个通道间的信号干扰也会增加,导致cpu出错,计算也会增加,实际上性能可能并不理想,综合考虑,才变成了现在的设计。

  • 这样做会浪费内存吗?肯定会浪费内存,所以合理的安排结构体的顺序,尽量减少对齐

posted @ 2022-09-14 16:40  秋来叶黄  阅读(281)  评论(0编辑  收藏  举报