内存对齐

对齐:
现代计算机中内存空间都是按着byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定变量的时候经常在特定的内存地址访问,这就是需要各类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。

对齐的作用:
各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存储。其他平台可能没有这种情况,但是最常见的是如果不按照合适其平台的要求对数据进行对齐,会在存取效率上带来损失。比如有些平台每次读都是从偶数地址开始,如果一个int型(假设为32位)如果存放在偶数开始的地方,那么一个读周期就可以读出,而如果存放在奇地址开始的地方,就可能会需要2个读周期,并对两次独处的结果的高低字节进行拼凑才能得到该int数据。显然在读取效率上下降很多。

对齐的实现
通常我们写程序的时候,不需要考虑对齐问题,编译器会替我们选择适合目标平台的对齐策略。当然,我们也可以通知给编译器传递预编译指令而改变对制定数据的对齐方法。缺省情况下,编译器为结构体的每个成员按其自然対界条件分配空间。各个成员按照他们被声明的顺序在内存中顺序存储,第一个成员的地址和整个结构的地址相同。自然対界即默认对齐方式,是指按结构体的成员中size最大的成员对齐。
最常见的就是struct数据结构的sizeof的结果出乎意料。
结构体的sizeof的值并不是简单的将其中各个元素所占的字节相加,而是要考虑到存储空间的字节对齐问题

结构体默认的字节对齐准则:
1. 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
2. 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字符;
3. 结构体的总大小为结构体最宽基本类型成员大小和编译器缺省対界条件大小中比较小得那个值的整数倍,如有需要编译器会在最后一个成员之后加上填充字节;

一 、结构体长度的求法:
A.成员都相同时(或含数组且数组数据类型同结构体其他成员数据类型)
结构体长度=成员数据类型长度*成员个数;
结构体中数组长度=数组数据类型长度*数组元素个数;
B.成员不同
第一步:结构体的首地址有系统自动分配我们不予考虑
第二步:计算第一个成员变量的大小
第三步:计算第二个成员变量的大小,此时要注意此成员变量的偏移量(距离结构体首地址的长度)要保证是此变量大小的整数倍,如果不够则补空位;依次计算所有成员变量,并求和。
第四步:选出所有成员变量中长度最长的变量的值,此时要保证总和是此变量长度的整数倍。如果不是则在最后面补空位。
注意:结构体作为成员时,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。

例题一、

struct  test1
{
    char a;
    int  b;
    double c;
    bool d;
};

则sizeof(test1)值为24
内存结构为 1*** 1111 11111111 1*******(其中*为补空位)

例题二、

struct  test2
{
    char a;
    struct test1  bb;
    int  c;
};

则sizeof(test2)的值为40
首先求a大小为1,在求bb时我们需要考虑偏移量,此时我们使用的bb的对比值并不是24而是test1中的最长值8,因此在字符a后需要补空位7位,然后加上bb长度24,再计算c并加上其长度4.此时一共长36。
最后我们要注意原则中的第三条。在test2中最长的是结构体类型bb中的double,故总长度应该是8的整数倍。所以最后补位4位,得到40
内存结构为 1******** 1*** 1111 11111111 1******* 1111****(其中*为补空位)
例题三、

Struct test3
{
    char  a;
    int b[4];
};

此时计算sizeof(test3)为20
内存结构为1*** 1111 1111 1111 1111

二、对齐规则
每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数32位机上是8)。程序员可以通过预编
译命令#pragma pack(n),n=1,2,4,8,16 来改变这一系数,其中的n 就是你要指定的“对齐系数”。

指定対界:
一般的,可以通过下面的方法来改变缺省的対界条件:
使用伪指令#pragma pack(n),编译器将按照n个字节对齐;
使用伪指令#pragma pack(),取消自定义的字节对齐方式;
注意:如果#pragma pack(n)中指定的n大于结构体中最大的成员的size,则其不起作用,结构体仍然按照size最大的成员进行対界。

规则1:
数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0 的地方,以后每个数据成员的对齐按照#pragma pack 指定的数值和这个数据成员自身长度中,比较小的那个进行。

规则2:
结构(或联合)的整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack 指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。

规则3:
结合1、2 颗推断:当#pragma pack 的n 值等于或超过所有数据成员长度的时候,这个n值的大小将不产生任何效果。

试验
我们通过一系列例子的详细说明来证明这个规则吧!
我试验用的编译器包括GCC 3.4.2 和VC6.0 的C 编译器,平台为Windows XP + Sp2。
我们将用典型的struct 对齐来说明。首先我们定义一个struct:

#pragma pack(n)
struct test_t
{
    int a;
    char b;
    short c;
    char d;
};
#pragma pack(n)

首先我们首先确认在试验平台上的各个类型的size,经验证两个编译器的输出均为:
sizeof(char) = 1
sizeof(short) = 2
sizeof(int) = 4
我们的试验过程如下:通过#pragma pack(n)改变“对齐系数”,然后察看sizeof(struct test_t)的值。
1、1 字节对齐(#pragma pack(1))
输出结果:sizeof(struct test_t) = 8 [两个编译器输出一致]
分析过程:
1) 成员数据对齐

#pragma pack(1)
struct test_t {
    int a;
    char b;
    short c;
    char d;
};
#pragma pack()

成员总大小=8
2) 整体对齐
整体对齐系数= min((max(int,short,char), 1) = 1
整体大小(size)= () (整体对齐系数) 圆整= 8 [注1]

2、2 字节对齐(#pragma pack(2))
输出结果:sizeof(struct test_t) = 10 [两个编译器输出一致]
分析过程:
1) 成员数据对齐

#pragma pack(2)
struct test_t {
    int a;
    char b;
    short c;
    char d;
};
#pragma pack()

成员总大小=9
2) 整体对齐
整体对齐系数= min((max(int,short,char), 2) = 2
整体大小(size)= () (整体对齐系数) 圆整= 10
3、4 字节对齐(#pragma pack(4))
输出结果:sizeof(struct test_t) = 12 [两个编译器输出一致]
分析过程:
1) 成员数据对齐

#pragma pack(4)
struct test_t {
    int a;
    char b;
    short c;
    char d;
};
#pragma pack()

成员总大小=9
2) 整体对齐
整体对齐系数= min((max(int,short,char), 4) = 4
整体大小(size)= () (整体对齐系数) 圆整= 12
4、8 字节对齐(#pragma pack(8))
输出结果:sizeof(struct test_t) = 12 [两个编译器输出一致]
分析过程:
1) 成员数据对齐

#pragma pack(8)
struct test_t {
    int a;
    char b;
    short c;
    char d;
};
#pragma pack()

成员总大小=9
2) 整体对齐
整体对齐系数= min((max(int,short,char), 8) = 4
整体大小(size)= () (整体对齐系数) 圆整= 12
5、16 字节对齐(#pragma pack(16))
输出结果:sizeof(struct test_t) = 12 [两个编译器输出一致]
分析过程:
1) 成员数据对齐

#pragma pack(16)
struct test_t {
    int a;
    char b;
    short c;
    char d;
};
#pragma pack()

成员总大小=9
2) 整体对齐
整体对齐系数= min((max(int,short,char), 16) = 4
整体大小(size)= () (整体对齐系数) 圆整= 12
[注1]
什么是“圆整”?
举例说明:如上面的8 字节对齐中的“整体对齐”,整体大小=9 按4 圆整= 12
圆整的过程:从9 开始每次加一,看是否能被4 整除,这里9,10,11 均不能被4 整除,到12 时可以,则圆整结束。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值