当前位置:首页 > > 充电吧
[导读]内存对齐问题是各种开发类面试中最热门的问题,面试管一般认为这个问题可以考察被面试者对内存细节的了解情况,确实这个问题对于C++初学者来说是个十足的难题因为它不仅涉及了pragma pack(n) 设定

内存对齐问题是各种开发类面试中最热门的问题,面试管一般认为这个问题可以考察被面试者对内存细节的了解情况,确实这个问题对于C++初学者来说是个十足的难题因为它不仅涉及了pragma pack(n) 设定的内存对齐系数还涉及了相关内存分配的细节。


内存对齐:

我们知道现代计算机体系中CPU按照双字、字、字节访问存储内存,并通过总线进行传输,若未经一定规则的对齐,CPU的访址操作与总线的传输操作将会异常的复杂,所以现代编译器中都会对内存进行自动的对齐。

1.内存对齐系数

说道内存对齐,就不得不说内存对齐系数, 对齐系数最简单的设置方法是使用 #pragma pack(n)进行设置,这部分点进链接在我的文章内有详细说明!

2.sizeof

说到内存对齐第二个不得不说的就是sizeof,它的基本作用是判断数据类型或者表达式长度,要注意的是这不是一个函数,而是一个C++中的关键字!字节数的计算在程序编译时进行,而不是在程序执行的过程中才计算出来!

3.类型的长度与数据成员对齐

你的计算机中,数据类型的长度指的就是在你的计算机中对数据类型使用sizeof得到的结果,当然这个在各种不同的编译环境下得到的结果是不同的。
比如在32位Visual Studio环境下:
cout << sizeof(char) << endl;  // 1
cout << sizeof(short) << endl;  // 2
cout << sizeof(int) << endl;  // 4
cout << sizeof(long) << endl;  // 4

cout << sizeof(double) << endl;  // 8


而在64位G++编译环境下:
cout << sizeof(char) << endl;  // 1
cout << sizeof(short) << endl;  // 2
cout << sizeof(int) << endl;  // 4
cout << sizeof(long) << endl;  // 8

cout << sizeof(double) << endl;  // 8

下面我将在32位Visual Studio环境下讲解数据成员对齐:
首先我们要清楚结构体struct中的成员在内存中的分配是连续的,struct内的首地址也就是struct内第一个数据成员的地址,换句话说struct内第一个数据成员离struct开始的距离offset = 0。
数据成员对齐的规则就是,而在第一个成员之后,每个成员距离struct首地址的距离 offset, 都是struct内成员自身长度(sizeof) 与 #pragma pack(n)中的n的最小值的整数倍,如果未经对齐时不满足这个规则,在对齐时就会在这个成员前填充空子节以使其达到数据成员对齐。

默认n为8时:

#pragma pack(8)

struct {
    char a;
    double b;
} myStruct;
cout << sizeof myStruct << endl;  // 16
cout << (int *)&myStruct.a << endl;  // 0024F898

cout << &myStruct.b << endl;  // 0024F8A0(因运行时而异)


当设置n为4也就是min(sizeof(double), n) = 4 时:
#pragma pack(4)
struct {
    char a;
    double b;
} myStruct;
cout << sizeof myStruct << endl; // 12
cout << (int *)&myStruct.a << endl; // 0046F76C
cout << &myStruct.b << endl; // 0046F770
第一个例子时,最小值为8,填充7个字节到char a 之后。

第二个例子时,最小值为4,填充3个字节到char a之后。

4.整体对齐

编译器在进行过数据成员对齐之后,还要进行整体对齐。与数据对齐相似但不是完全相同, 如果数据对齐完成时struct的大小不是 struct内成员自身长度最大值(sizeof) 与 #pragma pack(n)中的n的最小值的整数倍。(注意这里是成员中长度最大的那个与n比较,而不是特定的一个成员。)就要在struct的最后添加空字节直到对齐。
当设置n为4也就是min(sizeof(short), n) = 2 时:
#pragma pack(4)
struct {
    char a;
    short b;
    char c;
} myStruct;
cout << sizeof myStruct << endl;  // 6
cout << (int *)&myStruct.a << endl;  // 003DFED0
cout << &myStruct.b << endl;  // 003DFED2
cout << (int *)&myStruct.c << endl;  // 003DFED4
在上面的例子中,char a offset为0 因成员对齐占据[D0]填充[D1]共两个字节,short b是最大长度成员无需对齐占据[D2-D3]两个字节,它的offset是2,而char c的offset是4占据[D4]无需成员对齐,但此时struct的大小是2+2+1 = 5字节,不是2的整数倍,所以我们要填充空子节在最后直到struct大小达到2的整数倍,这就是整体对齐。
经过了数据成员对齐与整体对齐之后内存对齐就完成了,如果深入思考上述规则还会发现:即使是同样数目与数量的数据成员,在摆放的顺序不同时struct的大小也会不同,下面就是一个例子:
这样摆放是12字节:


摆放方式改变时结果确变成了8字节:


由于这种特性,如果在网络编程或相关内存操作时如果不加以注意的话,就会造成隐秘而难以纠正的错误,请大家务必小心!



本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: 驱动电源

在工业自动化蓬勃发展的当下,工业电机作为核心动力设备,其驱动电源的性能直接关系到整个系统的稳定性和可靠性。其中,反电动势抑制与过流保护是驱动电源设计中至关重要的两个环节,集成化方案的设计成为提升电机驱动性能的关键。

关键字: 工业电机 驱动电源

LED 驱动电源作为 LED 照明系统的 “心脏”,其稳定性直接决定了整个照明设备的使用寿命。然而,在实际应用中,LED 驱动电源易损坏的问题却十分常见,不仅增加了维护成本,还影响了用户体验。要解决这一问题,需从设计、生...

关键字: 驱动电源 照明系统 散热

根据LED驱动电源的公式,电感内电流波动大小和电感值成反比,输出纹波和输出电容值成反比。所以加大电感值和输出电容值可以减小纹波。

关键字: LED 设计 驱动电源

电动汽车(EV)作为新能源汽车的重要代表,正逐渐成为全球汽车产业的重要发展方向。电动汽车的核心技术之一是电机驱动控制系统,而绝缘栅双极型晶体管(IGBT)作为电机驱动系统中的关键元件,其性能直接影响到电动汽车的动力性能和...

关键字: 电动汽车 新能源 驱动电源

在现代城市建设中,街道及停车场照明作为基础设施的重要组成部分,其质量和效率直接关系到城市的公共安全、居民生活质量和能源利用效率。随着科技的进步,高亮度白光发光二极管(LED)因其独特的优势逐渐取代传统光源,成为大功率区域...

关键字: 发光二极管 驱动电源 LED

LED通用照明设计工程师会遇到许多挑战,如功率密度、功率因数校正(PFC)、空间受限和可靠性等。

关键字: LED 驱动电源 功率因数校正

在LED照明技术日益普及的今天,LED驱动电源的电磁干扰(EMI)问题成为了一个不可忽视的挑战。电磁干扰不仅会影响LED灯具的正常工作,还可能对周围电子设备造成不利影响,甚至引发系统故障。因此,采取有效的硬件措施来解决L...

关键字: LED照明技术 电磁干扰 驱动电源

开关电源具有效率高的特性,而且开关电源的变压器体积比串联稳压型电源的要小得多,电源电路比较整洁,整机重量也有所下降,所以,现在的LED驱动电源

关键字: LED 驱动电源 开关电源

LED驱动电源是把电源供应转换为特定的电压电流以驱动LED发光的电压转换器,通常情况下:LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: LED 隧道灯 驱动电源
关闭