数据对齐
首先得明确几个知识点【看不懂的话,可以先跳过,看完后面再看这里的话就更加清楚了】:
1、数据对齐的方式是可以通过指定对齐方式修改的,具体怎么改,稍后说明
2、结构体的自身对齐方式是指当不考虑指定对齐方式的情况下,结构体自身应有的对齐方式
3、结构体的自身对齐方式跟结构体内部变量的数据类型的对齐方式是不一样的,结构体自身对齐方式是结构体中的所有变量的数据类型的对齐方式的最大值。
4、结构体的真正对齐方式=min{结构体自身对齐方式,指定对齐方式}
5、系统数据类型类型的对齐方式等于该数据类型所占字节数(注意:这里是系统数据类型,而不是自定义数据类型,自定义数据类型由其内部的变量的数据类型决定)
6、由于指定对齐方式默认值为8,而系统变量类型中最多占8个字节,所以结构体自身对齐方式最大值也是8,因此当没有显式地指定对齐方式时,结构体的真正对齐方式就等于结构体的自身对齐方式。
7、一般指定运行环境作用是明确数据类型所占的字节数。32位和64位的区别(相同的就忽略了):在32位中,long是4个字节,指针类型(无论指向何种数据类型)是4个字节;在64位的情况下,这两种都是8个字节。
用实例来说明(32位的环境):
struct P1{ char a;//偏移量为0,占1个字节,但是由于后面跟了一个int类型的变量b,int要求4对齐,所以a后面3个字节会被跳过 int b;//偏移量为0+1+3=4,占4个字节,int型要求偏移量是4的倍数(4对齐) short c;//偏移量为4+4=8,占2个字节,short型要求偏移量是2的倍数(2对齐) char d;//偏移量为8+2=10,占1个字节(1对齐) char e;//偏移量为10+1=11,占1个字节,下一个偏移量是12,不满足long long 要求的8对齐(即偏移量是8的倍数),所以后面4个字节会被跳过 long long f;//偏移量为11+1+4=16,占8个字节 char g;//偏移量为16+8=24,占1个字节,下一个偏移量是25,不满足float要求的4对齐,所以后面3个字节会被跳过 float h;//偏移量为24+1+3=28,占4个字节 char i;//偏移量为28+4=32,占1个字节,下一个偏移量是33,不满足char*(指针)要求的4对齐,所以后面3个字节被跳过 char* j;//偏移量为32+1+3=36,占4个字节 char k;//偏移量为36+4=40,占一个字节。由于这个结构体中最大的数据类型是long long,其对齐方式是8,所以这个结构体的对齐方式为8,因此此处要浪费7个字节,使得下一个偏移量为8的倍数(48) };
所以P1定义的变量所应分配的空间为48个字节
如果指定对齐方式为4,则k后面只需要补3个字节,此时P1定义的变量所应分配的空间为44个字节。
另外一个示例:
struct P2{ int a; long long b; char c; }; struct P3{ char a; int b; }; struct P4{ union g{ int a1[3]; char b1[11]; }a;//偏移为0,占3*4=12个字节,由于P2的对齐方式是8,所以应跳过4个字节 P2 b;//偏移为0+12+4=16,占24个字节 char c;//偏移为16+24=40,占一个字节,由于P3的对齐方式是4,所以应跳过3个字节 P3 d[3];//偏移为40+1+3=44,占3*8=24个字节 P3 *p;//偏移为44+24=68,占4个字节 int e;//偏移为68+4=72,占4个字节 char f;//偏移为72+4=76,占一个字节,但由于P2的对齐方式是8,所以P4的对齐方式也是8,因此此处需要浪费3个字节 };
指定对齐方式的方法见博客