数据对齐
来源:互联网 发布:模拟退火算法模型 编辑:程序博客网 时间:2024/04/27 11:28
为什么要数据对齐?
所谓数据对齐是指访问数据的地址要满足一定的条件,能被这个数据的长度所整除。 例如,1字节数据已经是对齐的,2字节的数据的地址要被2整除,4字节的数据地址要 被4整除。
但为什么要数据对齐呢?简单地说,数据对齐是为了读取数据的效率。假如说每一次 读取数据时都是一个字节一个字节读取,那就不需要对齐了,这跟读一个字节没有什 么区别,就是多读几次。但是这样读取数据效率不高。为了提高读取数据的带宽,现 代存储系统都采用许多并行的存储芯片来提高读取效率。以自然字长为4个字节的机器 为例。
memory chip 0 1 2 3offset 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个字节,因此c和d占用8个字节。d
占用1个字节,这样一共占用9个字节,但是A
的对齐模数是4,因此d
后面要填充4字节。 因此A占用12字节。
对于比特域来讲,上面的规则要稍稍修改一下。对于连续的声明类型大小相同的比特域来讲,如果它们申请的宽度加起来不超过它们声明的类型大小,那么它们可以压缩在一个分配单元中。对于声明类型大小不同的比特与来讲,它们分配在不同的分配单元中。
struct A { char a : 3; char b : 5;};
a
和b
的类型长度都为1字节,因此可以考虑合并。它们加 起来为8比特,可以放在一个字节中,由于A
的对齐模数为1,因此不需在后面再填充数 据,所以A
的大小为1字节。
另一个例子.
struct A { char a : 4; char b : 5;};
a
和b
的类型长度都为1字节,因此可以考虑合并,但跟上 例不同的是,它们无法放入一个字节中(加起来为9比特),因此它们要放在各自的分 配单元中,a
和b
之间有4比特的空隙。A
的 大小为2字节。
再考虑如下例子.
struct A { char a : 3; int b : 2; char c : 2;};
a
和b
的类型长度不相等(a
的为1字 节,b
的为4字节),因此不能合并,同样b
和c
也不能合并。因此,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;};
尽管这三个比特域的类型一样,但是由于a
和b
之间有一个宽度为0的匿名比特域,因此 它们不能合并。a
和b
个占用1个字节,所以A
的大小为2字节。
最后,对于具有普通成员和比特域的结构体,它们的对齐规则跟上面的一样。注意,普 通成员与比特域不能合并,只有连续的比特域之间可以考虑合并,每一个成员都要对 齐到自己的对齐模数上。
首先 几个概念:
1.基本数据类型的自身对齐值:char自身对齐值为1,short为2,int,float为4,double为8,单位字节。
2.结构体或者类的自身对齐值:其成员变量中自身对齐值最大的那个值。
3.指定对齐值:#pragma pack (n)时的指定的对齐值n。
那么数据成员、结构体以及类最终按照几字节对齐:自身对齐值和指定对齐值中小的那个值。
然后有个公式: 存放起始地址 % 各个成员的自身对齐值 = 0
现在我们来看这个类
class
A
{
public
:
double
d;
int
a;
char
c;
float
b;
A();
~A();
};
这里没有指定对齐值(就是没有#pragma pack (n))
那么这个类的的最终的对齐值是 8,就是按照8字节对齐
假定 地址空间从0x0000开始排放,
那么double d的地址为 0x0000 (0x0000 % 8 = 0)
然后往后偏8字节之后,能被下一个成员int a这4字节对齐值整除的地址为0x0008
那么int a的地址为 0x0008 (0x0008 % 4 = 0)
然后往后偏4字节之后,能被char c这1字节对齐值整除的地址为0x000C
那么char c的地址为 0x000C (0x000C % 1 = 0)
然后往后偏1字节之后,能被float b这4字节对齐值整除的地址为0x0010(此时char c后面空出来了3字节)
那么float b的地址为 0x0010 (0x0010 % 4 = 0)
部分内容参考:http://blog.csdn.net/tigerscorpio/article/details/5933807
- 数据对齐 字节对齐
- 数据对齐
- 数据对齐
- 数据对齐
- 数据对齐
- 数据对齐
- 数据对齐
- 数据对齐
- 数据对齐
- 数据对齐
- 数据对齐
- 数据对齐
- 数据对齐
- 数据对齐
- 数据对齐
- 数据对齐
- 数据对齐
- 数据对齐
- 两个特殊的流对象(System.in和System.out)及打印流
- ORACLE LOB大对象处理
- ext 如何添加tabpanel
- maven 执行命令mvn help:system命令时报build failure错误的解决办法。
- Qt编译报错 undefined reference to vtable for ...
- 数据对齐
- Exercise 10: 那是什么?
- PHPCMS安装流程图
- IE下Css圆角问题
- UILocalizedIndexedCollation 使用
- HTML中忽略的小问题
- 欧拉计划 第6题
- cocos2d-x 自定义精灵类
- PAT 1073