现在的位置: 首页 > 综合 > 正文

字节对齐

2018年04月03日 ⁄ 综合 ⁄ 共 1822字 ⁄ 字号 评论关闭

为什么要字节对齐,用意何在?

  如果一个变量的内存地址正好位于它长度的整数倍,他就被称做自然对齐。比如在32cpu下,假设一个整型变量的地址为0x00000004,那它就是自然对齐的。需要字节对齐的根本原因在于CPU访问数据的效率问题。假设之前整型变量的地址不是自然对齐,比如为0x00000002,则CPU如果取它的值的话需要访问两次内存,第一次取从0x00000002-0x00000003的一个short,第二次取从0x00000004-0x00000005的一个short然后组合得到所要的数据,如果变量在0x00000003地址上的话则要访问三次内存,第一次为char,第二次为short,第三次为char,然后组合得到整型数据。而如果变量在自然对齐位置上,则只要一次就可以取出数据。所以字节对齐可以提高效率。当然对齐的工作编译器会帮我自动完成。

字节对齐在结构体中比较常见,

struct stu{

   char sex;

   int length;

   char name[10];

};

一般GCC或者其他编译器默认结构体中所占内存最大的字节数对齐(例如上面结构体length最大占4个字节,所以结构体按照4字节对齐,如果int换成short则是按照2字节对齐,换成double则是按照8字节对齐,注意数组则是按照总体大小是否能被对齐大小整除,如果不能则填充直到能被整除),它会在sex后面跟name后面分别填充三个和两个字节使length和整个结构体对齐。于是我们sizeof(my_stu)会得到长度为20,而不是15

那么如何制定对齐的大小呢?

 struct stu{

   char sex;

   int length;

   char name[10];

}__attribute__ ((aligned (1))); 

GNU使用__attribute__选项来设置,理论上上面的结构体这样定义的话大小应该是15而不是20,但实际测试发现仍然是20(??),但是换成_attribute__ ((packed))或者下面所说的pack指令则起了作用使得变量或者结构体成员使用最小的对齐方式。(理论上_attribute__ ((packed))等价于__attribute__ ((aligned (1))))

也可以使用使用伪指令#pragma pack (n),使得C编译器将按照n个字节对齐。

然后在结尾处使用伪指令#pragma pack (),取消自定义字节对齐方式。

注意无论是哪种方法,对于结构体而言对齐大小都只针对1和偶数对齐有效,并且设置超过元素所占最大内存也会无效,例如在上面的结构体设置8字节对齐,但实际仍然还是4字节对齐。3字节对齐仍然还是4字节对齐。

注意:实际测试发现__attribute__ ((aligned (n)))这种表达式效果不是很好,例如同样设置2字节对齐,使用__attribute__ ((aligned (2)))后上面结构体大小仍然为20字节而使用pack则为预想的16字节。

既然编译器已经帮我完成了字节对齐的工作,那么我们为什么还要注意呢?其答案在与节约内存。

struct A {

    char b;

short c;

int a;

};

struct B {
    char b;
    int a;
    short c;
};

 

同样的成员变量,但是不同的写法,所占的内存不一样,结构体A大小为8个字节,而结构体B大小为12个字节。

所以针对字节对齐,我们在编程中如何考虑?

如果在编程的时候要考虑节约空间的话,那么我们只需要假定结构的首地址是0,然后各个变量按照上面的原则进行排列即可,基本的原则就是把结构中的变量按照类型大小从小到大声明,尽量减少中间的填补空间.还有一种就是为了以空间换取时间的效率,我们显示的进行填补空间进行对齐,比如:有一种使用空间换时间做法是显式的插入reserved成员:

             struct A{

               char a;

               char reserved[3];//使用空间换时间

               int b;

}

reserved成员对我们的程序没有什么意义,它只是起到填补空间以达到字节对齐的目的,当然即使不加这个成员通常编译器也会给我们自动填补对齐,我们自己加上它只是起到显式的提醒作用。

【上篇】
【下篇】

抱歉!评论已关闭.