#include <stdio.h>
#pragma pack(4)
struct test
{
int a;
char b;
// char reserve1;
short c;
char d;
// char reserve2[3];
};
#pragma pack()
int main(void)
{
printf("pack(4)=%d\n", sizeof(test));
struct test t[2];
return 0;
}
上面代码在VC6.0环境中编写,接下来有三个问题:1、加上如代码所示的注释,最终程序输出多少?2、去掉代码中的注释,最终程序输出多少?3、去掉#pragma pack(4)和#pragma pack()语句后再运行程序,最终输出多少?
这里给出最终答案为:pack(4)=12
对!上面3个问题的答案均一样!这是为什么呢?
首先,要明白上述代码要说明的一个问题即是题目所示——“内存对齐”,为什么要进行内存对齐呢?根本原因在于CPU访问数据的效率问题。
对齐跟数据在内存中的位置有关。如果一个变量的内存地址正好位于它长度的整数倍,他就被称做自然对齐。比如在32位cpu下,假设一个整型变量的地址为0x00000004,那它就是自然对齐的。假设某个整型变量的地址不是自然对齐,比如为0x00000002,则CPU如果取它的值的话需要访问两次内存,第一次取从0x00000002-0x00000003的一个short,第二次取从0x00000004-0x00000005的一个short然后组合得到所要的数据,如果变量在0x00000003地址上的话则要访问三次内存,第一次为char,第二次为short,第三次为char,然后组合得到整型数据。而如果变量在自然对齐位置上,则只要一次就可以取出数据。
回到上面问题,首先对于#pragma pack伪指令语句,其意义在于声明在其下定义的结构体变量的对齐方式,程序中#pragma pack(4),即表达下面的结构体中的变量以4字节对齐,而#pragma pack()意思为取消对齐方式,它们是成对出现。当然如果在程序中只使用#pragma pack(4)也是可以的,那就是说明在本源文件中,该伪指令下的结构均为4字节对齐方式。至于第3个问题为什么取消和加上这两个伪指令结果都一样,是因为VC6.0编译器默认为4字节对齐方式。接下来分析下第1个问题,为什么会输出12?首先在x86的32位机器上我们要知道如下信息:
sizeof(char) = 1;
sizeof(short) = 2;
sizeof(int) = 4;
sizeof(float) = 4;
sizeof(double) = 8;
那接下来就好办了,在test结构体中定义了4个变量,第一个int型变量a显然占4个字节,第二个char型变量b占1个字节,第三个short型变量c占2个字节,第四个char型变量d占1个字节。总的字节数为:4+1+2+1 = 8。不对,明明输出是12啊?这里即是内存对齐搞的鬼了,假如该结构地址为0x00000000,那么第一个变量a的由于占4个字节,则其存储的区域为0x00000000~0x00000003对于第二个变量b,其本身占用1个字节,其占用的地址为:0x00000004;第三个变量c,其本身占用2个字节,复核0x00000006%2=0,则其应占用的地址为0x00000006~0x00000007,地址0x00000005需空出;对于第四个变量d,其本身占用1个字节,则其占用地址为0x00000008,因此总的有效占用字节为:4+2+2+1=9,为什么还是不是12呢?注意由于结构与结构之间还存在对齐,因此在该结构体末尾需加上3个字节补齐,即总的字节为12。