1.结构体内存对齐的原因?
什么是对齐,以及为什么要对齐: 现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定变量的时候经常在特定的内存地址访问,这就需要各类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。 对齐的作用和原因:各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。其他平台可能没有这种情况,但是最常见的是如果不按照适合其平台要求对数据存放进行对齐,会在存取效率上带来损失。比如有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那么一个读周期就可以读出,而如果存放在奇地址开始的地方,就可能会需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该int数据。显然在读取效率上下降很多。这也是空间和时间的博弈。 对齐的实现 通常,我们写程序的时候,不需要考虑对齐问题。编译器会替我们选择时候目标平台的对齐策略。当然,我们也可以通知给编译器传递预编译指令而改变对指定数据的对齐方法。 但是,正因为我们一般不需要关心这个问题,所以因为编辑器对数据存放做了对齐,而我们不了解的话,常常会对一些问题感到迷惑。最常见的就是struct数据结构的sizeof结果,出乎意料。为此,我们需要对对齐算法所了解。
2.字节对齐的算法是如何的?
内存对齐的规则:
原则1 结构的数据成员,第一个数据成员放在offset为零的地方,以后每个数据成员的对齐按照#pragma pack指定的值
和这个数据成员自身长度中,比较小的那个进行。
原则2 结构的整体对齐原则 在数据成员完成各自的对齐以后,结构本身也要进行对齐,对齐将按照pragma pack指定的数值
和结构中最大数据成员长度中,比较小的那个进行。
原则3 结构体作为成员:结构体作为成员:如果一个结构里有某些结构成员,则结构体成员要从其内部最大元素与#pragma pack指定的值
比较小的那个进行。
在使用到原则三的情况下,整体的对齐,还是按照原则二,这时把结构体成员不看成整体,而是看其中的每一个元素。
3.在windows下和linux下有其默认的对齐值,默认的对齐值是
Linux 默认#pragma pack(4)
window 默认#pragma pack(8)
4.例子
下面讲解几个例子,来证明上面的说法是正确的
例子1:首先我来证明下windows下的默认对齐值是8,windows下vs2013环境
struct Student
{
char sex; //1个字节
double score;
int tmp;
};
int main()
{
printf("sizeof student is[%d]\n", sizeof(Student));
return 0;
}
输出值如下:
输出24,就能证明windows下是以8字节对齐的,为什么呢,听听我下面的讲解,24字节是8的整数倍,整个的结构体成员占用的字节情况如下图。
如上图,结构体的第一个元素就放在结构体地址的零偏移地址,占用一个字节,最主要的是到第二个元素了,double占用8个字节,而默认对齐值,只能是8才能保证,按8字节对齐,如果默认对齐值是4的话,取两者中的小者就是4了,
那样的话,sex后面不会占用7个字节,而是 三个字节。然后最后一个元素tmp 4字节,默认的对齐值是8字节,4<8 所以用4,然后结束 这时占用了 20字节,
最好还要按照
原则2 结构的整体对齐原则 在数据成员完成各自的对齐以后,结构本身也要进行对齐,对齐将按照pragma pack指定的数值
和结构中最大数据成员长度中,比较小的那个进行。
所以在加4变成 24字节,是8的三倍。证明完毕。
同样的道理上面的代码在linux下输出的值为 16,那么也就证明了 linux下默认值是4字节对齐。但是不幸运的是我在linux下虚拟机上没有证明,输出结果是24,
然后我在代码中添加完 pragma pack(4)以后才变成16字节,这个问题先放在这里,后期再看。
例子2:
windows下指定字节对齐为 4字节
#pragma pack(4)
struct Student
{
char sex; //1个字节
double score;
int tmp;
};
#pragma pack()
int main()
{
printf("sizeof student is[%d]\n", sizeof(Student));
return 0;
}
输出结果为16字节。不再解释了。
例子3.结构体的嵌套
#pragma pack(4)
struct Student
{
char sex; //1个字节
double score;
int tmp;
};
struct Student1
{
char score;
struct Student tmp1;
};
#pragma pack()
int main()
{
printf("sizeof student is[%d]\n", sizeof(Student));
printf("sizeof student1 is[%d]\n", sizeof(Student1));
return 0;
}
输出结果如下所示:
看第二个的结构体所占用的字节数是 20 分析下:
看Student1 score零偏移值 1字节,然后开始放 Student里的元素,这里一定要注意 看原则三:结构体作为成员:如果一个结构里有某些结构成员,则结构体成员要从其内部最大元素与#pragma pack指定的值
比较小的那个进行。
Student里最大的,是8字节的score,和默认值4比较,小者是4.所以要按照四个字节对齐。
如下图所示: