关于数据对齐(Data Alignment)

来源:互联网 发布:淘宝子账号怎么添加 编辑:程序博客网 时间:2024/05/29 16:08

关于数据对齐 About Data Alignment

数据对齐的由来

1、平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2、性能原因:为了访问未对齐的内存,处理器需要作多次内存访问,然而,对齐的内存访问仅需要一次访问。这对于效率而言有一定的下降。比如有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)存放在偶地址开始的地方 ,那么读一个周期就可以读出这32bit,而如果存放在奇地址开始的地方,就需要读2个周期,并对两次读出的结果的高低字节进行拼凑才能得到该32bit数据。

相关概念

1)数据类型自身的对齐值:基本数据类型的自身对齐值,等于sizeof(基本数据类型)。(在不同的平台下有可能不同,如Windows下的数据对齐即为任何K字节基本对象的地址都必须是K的整数倍,而在linux32位下,沿用的对齐策略是,2字节的数据类型的地址必须是2的倍数,而较大的数据类型如 int int* float double 的地址是4的倍数。)

2)指定对齐值:#pragma pack (value)时的指定对齐值value。

3)结构体或者类的自身对齐值:其成员中自身对齐值最大的那个值。

4)数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中较小的那个值。

有效对齐值N是最终用来决定数据存放地址方式的值,最重要。有效对齐N,就是表示“对齐在N上”,也就是说该数据的”存放起始地址%N=0”.而数据结构中的数据变量都是按定义的先后顺序来排放的。第一个数据变量的起始地址就是数据结构的起始地址。结构体的成员变量要对齐排放,结构体本身也要对齐对自身的对齐值,就是结构体成员变量占用总长度需要是对结构体有效对齐值的整 数倍。

数据对齐测试

保持数据对齐可以提高效率,其代价就是损耗了一部分的内存空间,典型的是通过空间来换取时间的案例。
数据对齐和平台是紧密相关的,不同平台下的对齐原则也不尽相同,首先介绍Windows下的数据对齐:
1.默认情况下,任何K字节基本对象的地址都必须是K的整数倍。
通常情况下,基本数据类型的字节数目为下:(然而并不是绝对的,如int类型,C标准只要求其最小拥有16位,即两个字节)
char 1
short 2
int 4
unsigned 4
long 8
unsigned long 8
float 4
doule 8
void * 4
long double 8
同时需要注意的是,在许多平台下long double实际上需要10个字节,但实际中会分配12(32位下)或者是16(64位下)个字节,这是为了读取效率。但在vs2010下测试时发现long double是8字节。

在vs2010下进行测试,选定”结构成员对齐”为默认设置,此时则按照默认对齐规则进行字节对齐。即任何K字节基本对象的地址都必须是K的倍数。假设下面每个结构体的起始地址均为0。

    struct s            //偏移量       //数据内容字节数       //填充的字节数            //数据对齐值    {        char c;         //0             //1                     //7                     //1        double d;       //8             //8                     //0                     //8    };                  //结构体字节大小:16                    结构体对齐值:8    struct s1           //偏移量       //数据内容字节数       //填充的字节数            //数据对齐值    {        char c;         //0             //1                     //7                     //1        double d;       //8             //8                     //0                     //8        int i;          //16            //4                     //4                     //4    };                  //结构体字体大小:24  结构体对齐值:8 (整个结构体的对齐为8,所以需要最后需要填充4个字节))    struct s2           //偏移量       //数据内容字节数       //填充的字节数            //数据对齐值    {        char c;         //0             //1                     //3                     //1        int d;          //4             //4                     //0                     //4        char i;         //8             //1                     //3                     //1     };                  //结构体字体大小:12  结构体对齐值:4 (整个结构体的对齐为4,所以需要最后需要填充3个字节))    struct s3           //偏移量       //数据内容字节数       //填充的字节数            //数据对齐值    {         char c;         //0             //1                     //3                     //1        int i;          //4             //4                     //0                     //4        double d;       //8             //8                     //0                     //8    };                  //结构体字节大小:16                    结构体对齐值:8    struct s4           //偏移量       //数据内容字节数       //填充的字节数            //数据对齐值    {        char c;         //0             //1                     //7                     //1        long double d;  //8             //8                     //0                     //8    };                  //结构体字节大小:16                    结构体对齐值:8

上述结构体对齐情况是在没有指定对齐值的情况下测试的,有效对齐值就是基本数据类型的字节数,如果指定了对齐值,有效对齐值则为自身对齐值和指定对齐值之间最小的那个。如指定对齐值为4,对于 char(1字节),short(2字节),int(4字节),以及指针(32位下指针为4字节大小),均无影响,但是对于double(8字节大小),long int(8字节大小)等,其有效对齐值为4,不再是自身对齐值的8。
在此情况下,上述结构体的大小如下

    struct s            //偏移量       //数据内容字节数       //填充的字节数            //数据对齐值    {        char c;         //0             //1                     //3                     //1        double d;       //4             //8                     //0                     //4    };                  //结构体字节大小:12                    结构体对齐值:4    struct s1           //偏移量       //数据内容字节数       //填充的字节数            //数据对齐值    {        char c;         //0             //1                     //3                     //1        double d;       //4             //8                     //0                     //8        int i;          //12            //4                     //0                     //4    };                  //结构体字体大小:16                    //结构体对齐值:4     struct s2           //偏移量       //数据内容字节数       //填充的字节数            //数据对齐值    {        char c;         //0             //1                     //3                     //1        int d;          //4             //4                     //0                     //4        char i;         //8             //1                     //3                     //1     };                  //结构体字体大小:12  结构体对齐值:4 (整个结构体的对齐为4,所以需要最后需要填充3个字节))    struct s3           //偏移量       //数据内容字节数       //填充的字节数            //数据对齐值    {         char c;         //0             //1                     //3                     //1        int i;          //4             //4                     //0                     //4        double d;       //8             //8                     //0                     //4    };                  //结构体字节大小:16                    结构体对齐值:4    struct s4           //偏移量       //数据内容字节数       //填充的字节数            //数据对齐值    {        char c;         //0             //1                     //3                     //1        long double d;  //4             //8                     //0                     //4    };                  //结构体字节大小:12                    结构体对齐值:4

两种情况下,只有结构体s3的信息完全一致,其他结构体都有着一定的差别。

其他

上述介绍的情况是在32位下,在64位下,默认的对齐规则(无论linux或者是windows下均是任何K字节的基本数据类型的对齐值均为K),若指定了对齐值,实际对齐值仍是自身对齐值和指定对齐值的最小值。

除此之外,有两点需要注意:
1. 指针的数据大小为8字节,而不是4字节
2. long double的数据大小为16字节。

结构体对齐的实例可以根据32位下的情况进行类似的分析。

0 0
原创粉丝点击