内存对齐方式及结构体的存储

来源:互联网 发布:淘宝toto真假 编辑:程序博客网 时间:2024/06/05 23:46

1. 内存对齐方式

虽然所有的变量最后都会保存在特定地址的内存中,但相应的内存空间必须满足内存对齐的要求。主要出于两个方面的原因:
(1) 平台原因:
不是所有的硬件平台(特别是嵌入式系统中使用的低端处理器)都能访问任意地址上的任意数据,某些硬件平台只能访问对齐的地址,否则会出现硬件异常。
(2) 性能原因:
如果数据存放在未对齐的内存空间中,则处理器访问变量时需要做两次内存访问,而对齐的内存访问仅需一次访问。

在32位微处理器中,处理器访问内存都是按照32位进行的,即一次读取或写入都是4个字节,比如地址0x0~0xF这16字节的内存,对于微处理器来说,不是将其看做16个单一字节,而是4个块,每块4个字节。如下图
这里写图片描述

显然,只能从0x0、0x4、0x8、0xC等地址为4的整数倍的内存中一次取出4个字节,并不能从任意地址开始一次读取4个字节。假定将一个占用4个字节的int类型的数据存放在地址0开始的4字节内存中,其示意图如下
这里写图片描述
由于int类型数据存放在块0中,因此CPU仅需一次内存访问即可完成对该数据的读取或写入。反之,如果将int类型数据存放在地址1开始的4字节内存空间中,其示意图如下
这里写图片描述
此时,数据存放在块0和块1两个块中,若要完成对该数据的访问,必须经过两次内存访问,先访问块0得到数据的3个字节,再通过访问块1得到该数据的1个字节,最后通过运算,将这几个字节合并为一个完整的int型数据。由此可见,若数据存储在未对齐的内存空间中,将大大降低CPU的效率。但在某些特定的微处理器中,它根本不愿意干这种事情,这种情况下,就出现系统异常,直接崩溃了。

2. 结构体的存储

我们知道,数组是相同类型有序数据的集合,但很多时候需要将不同类型的数据捆绑在一起作为一个整体对待,使程序设计更方便。在C语言中,这样一组数据被称为结构体。其内存对齐的规则如下:
(1) 结构体各个成员变量的内存空间的首地址必须是“对齐系数”和“变量实际长度”中较小者的整数倍。“对齐系数”是【#pragma pack指定的数值】、【未指定#pragma pack时,系统默认的对齐模数(32位系统为4字节,64位为8字节)】。假设要求变量的内存空间要求按照4字节对齐,则内存空间的首地址必须是4的整数倍,满足条件的地址为0x0, 0x4, 0x8, 0xC…
(2) 对于结构体,在其各个数据成员都完成对齐后,结构体本身也需要对齐,即结构体占用的总大小应该为“对齐系数”和“最大数据成员长度”中较小值的整数倍。

如下的结构体,在32位机器上编译,其成员数据的总长度为4+2+3+4+1+8 = 22(字节)

#pragma pack(4)struct TEST{    long a;         //4    short b;        //2    char c[3];      //3    float d;        //4    char e;         //1    double f;       //8}test;

有如下的测试程序

int main(void){    short len = sizeof(test);    printf("test length is %d.\n", len);    return 0;}

下面是输出结果,结果表明结构体占用28个字节的内存,大于22字节。
这里写图片描述
其实,正是由于内存对齐的原因造成了这种现象,下图是每个变量在内存的分布图,其中空白部分是内存弃用部分,这些浪费空间的前面,存放的都是char型数据,由于char型数据只占用一个字节,往往使得其紧接着的空间不能被其它长度更长的数据使用。
这里写图片描述

为了降低内存浪费的概率,应该在char型数据之后,存放长度较小的成员。即在定义结构体时,应该按照长度递增的顺序依次定义各个成员。优化后的实例代码为

#pragma pack(4)struct TEST{    char e;         //1    char c[3];      //3    short b;        //2    long a;         //4    float d;        //4    double f;       //8}test;

运行测试程序,输出结果为
这里写图片描述
可见,通过调整结构体的成员顺序,达到了优化内存的目的。各个成员变量在内存中的分布图为
这里写图片描述

结构体只浪费了2个字节的空间,内存使用率达到92%。显然通过优化结构体成员的定义顺序,在同样满足内存对齐的要求下,可以大大减小内存的浪费。

原创粉丝点击