数据对齐

数据对齐是为了提高CPU读取数据的效率,确保数据地址符合特定规则,例如4字节数据的地址需被4整除。在Visual C++中,结构体的对齐模数取决于成员的对齐模数最大值。基本数据类型的对齐值与它们的大小有关,可通过/Zp选项设置。比特域的对齐规则略有不同,连续大小相同的比特域可以合并。结构体成员与比特域对齐规则遵循各自对齐值和指定对齐值的最小值。
摘要由CSDN通过智能技术生成

为什么要数据对齐?

所谓数据对齐是指访问数据的地址要满足一定的条件,能被这个数据的长度所整除。 例如,1字节数据已经是对齐的,2字节的数据的地址要被2整除,4字节的数据地址要 被4整除。

但为什么要数据对齐呢?简单地说,数据对齐是为了读取数据的效率。假如说每一次 读取数据时都是一个字节一个字节读取,那就不需要对齐了,这跟读一个字节没有什 么区别,就是多读几次。但是这样读取数据效率不高。为了提高读取数据的带宽,现 代存储系统都采用许多并行的存储芯片来提高读取效率。以自然字长为4个字节的机器 为例。

memory chip     0       1       2       3
offset
  
  0             0       1       2       3
  1             4       5       6       7
  2             8       9      10      11
  N            4N    4N+1    4N+2    4N+3

如上图所示,地址0-3的4字节数据都存储在4个不同的芯片上。CPU的bits 0-7连上芯 片0,bits 8-15连上芯片1,bits 16-23连上芯片2,bits 24-31连上芯片4。

如果从地址0读取4字节数据,4个存储芯片都从各自的地址0读出数据,而且位置也是 正确的。这4个字节的读取可以并行地进行,因此带宽是一次读取一个字节的4倍。

总结一下为什么需要数据对齐。为了增加CPU读取数据的带宽,内存系统通常都采用并 行结构使得可以并行传输数据。这样的并行结构使得访问对齐的数据速度快,但是若 要使访问不对奇的数据也一样快会使CPU与内存系统的接口变得更复杂,而这是划不来 的。经过权衡之后,最终的结果是:访问对齐的数据速度快,访问不对奇的数据速度 慢(需要2次访问)或干脆禁止访问不对奇数据。

关于访问对齐数据和不对齐数据的速度差异请 看这里。关于为什么使访问不对齐数据需要多次访问请 看这 里。关于哪些CPU禁止访问不对奇数据以及在这些CPU上如何解决访问不对奇 数据的问题请 看这里

Visual C++的结构体对齐规则

每一个数据对象都有一个对齐限制,通常称为对齐模数。对于基本 类型(不包括结构体,联合体和数组)不同的数据类型的对齐模数通常等于这个数据 类型的大小,比如char的对齐模数为1,short的对齐模 数为2,int的对齐模数为4,float的对齐模数是 4,double的对齐模数为8。当然,这个对齐模数每一个编译器可能有差 异,并且可以设置。在Visual C++中可以通过/Zp选项 获#pragma设置。综合起来,基本类型的对齐模数是这个类型的大小和 设置的对齐限制的较小者。对于结构体,联合体和数组,对齐模数是它的成员的对齐 模数的最大值。

现举几个例子说明一下。(下面例子都在Visual C++ 2008 Express Edition上测试 过。)

struct A {
    char c;
    int i;
};

c的对齐模数是1,i的对齐模数是4,因此结构 体A的对齐模数为4。c本身占用1个字节,由 于i的对齐模数为4,因此c后面填充3个字节以 使i的起始地址对齐。这样加起来是8个字节,A已经对齐 了,后面不用再填充了,因此A占用8个字节。

另一个例子。

struct A {
    char c;
    int i;
    char d;
};
跟上面一样,c的对齐模数是1,i的对齐模数,d的对齐模数是1,因此结构体A的对齐 模数为4。c占用1字节,为使i对齐,c后面要填充3个字节,因此cd占用8个字节。d 占用1个字节,这样一共占用9个字节,但是A的对齐模数是4,因此d后面要填充4字节。 因此A占用12字节。

 

对于比特域来讲,上面的规则要稍稍修改一下。对于连续的声明类型大小相同的比特域来讲,如果它们申请的宽度加起来不超过它们声明的类型大小,那么它们可以压缩在一个分配单元中。对于声明类型大小不同的比特与来讲,它们分配在不同的分配单元中。

struct A {
    char a : 3;
    char b : 5;
};

 

ab的类型长度都为1字节,因此可以考虑合并。它们加 起来为8比特,可以放在一个字节中,由于A的对齐模数为1,因此不需在后面再填充数 据,所以A的大小为1字节。

另一个例子.

struct A {
    char a : 4;
    char b : 5;
};
ab的类型长度都为1字节,因此可以考虑合并,但跟上 例不同的是,它们无法放入一个字节中(加起来为9比特),因此它们要放在各自的分 配单元中,ab之间有4比特的空隙。A的 大小为2字节。

 

再考虑如下例子.

struct A {
    char a : 3;
    int b : 2;
    char c : 2;
};
ab的类型长度不相等(a的为1字 节,b的为4字节),因此不能合并,同样bc也不能合并。因此,a单 独占用1字节,后面填充3字节以使b对齐,b占用4字节,c占用1字节。由于A的对齐模 数是4字节,所以c后面要填充3字节,A的大小为12字节。

 

下面再考虑匿名比特域的例子。匿名比特域主要目的是在两个比特域之间加入一段空隙。

struct A {
    char a : 2;
    char   : 2;
    char b : 2;
};
匿名比特域对对齐规则没有影响,它唯一的不同是无法引用这个域。由于这3个域的类 型大小是一样的,且可以放在一个字节之中,因此可以合并,A的大小为1字节。

 

下面再考虑匿名比特域宽度为0的,它的作用是使它后面的比特域对齐到下个比特域的对齐模数上,也就是说,不允许它跟上一个比特域合并。

struct A {
    char a : 2;
    char   : 0;
    char b : 2;
};
尽管这三个比特域的类型一样,但是由于ab之间有一个宽度为0的匿名比特域,因此 它们不能合并。ab个占用1个字节,所以A的大小为2字节。

 

最后,对于具有普通成员和比特域的结构体,它们的对齐规则跟上面的一样。注意,普 通成员与比特域不能合并,只有连续的比特域之间可以考虑合并,每一个成员都要对 齐到自己的对齐模数上。

首先 几个概念:

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值