问题:
最近一直比较忙碌,没来得及写博客,其实也遇到和学到了好多东西,今天终于决定写了写,先写这个内容,这是我在用nehe31课读取ms3d格式文件的时候,看到他的头文件里面写到“# pragma pack( push, packing )”,就上网搜了下,正好找到了这篇介绍结构体数据对其格式的文章,觉得很好,就转载下来了。
解答:
struct
{
char a;
int b;
double c;
} sa;
其中sizeof(char) =1; sizeof(int) = 4; sizeof(double) = 8; 所以sizeof(sa)应该等于1+4+8=13才对,编程输出sizeof(sa)的值却是16,原因是:为了加快CPU的存取速度,C++编译器在处理数据时经常把结构变量中的成员的大小按照4或8的倍数计算,这就叫数据对齐(data alignment)。
但是究竟是怎么对其的呢?别人做了几个实验,并且查了相关的资料,最终得出了结论。,首先开发环境是:WinXP+VC6,因为不同的操作系统和不同的编译器,得出的结果可能不一样。但是不同系统的原理应该是一样的,所以希望我所做的这些努力也能对使用其它开发环境的朋友有所帮助。
struct
{
int a;
char b;
} sa;
struct
{
int a;
char b;
double c;
} sb;
struct
{
int a;
double c;
char b;
} sc;
输出结果:sizeof(sa)=8, sizeof(sb)=16, sizeof(sc)=24;
只看sizeof(sa)的结果,我当时认为,结构体内部的数据是按4个字节一组存放的,所以尽管sizeof(char)=1但是,sa中的char b还是占用了4个字节,使得sizeof(sa)=8,为了验证我的“猜想”,我针对sa又做了一个实验:
struct
{
int a;
char b, c, d, e,;
} sa1;
struct
{
int a;
char b;
int c;
} sa2;
输出结果:sizeof(sa1)=8, sizeof(sa2)=12;
看来输出结果验证了我的刚才的“猜想”,于是,我试着用我推测出的结论解释sizeof(sb)=16, sizeof(sc)=24;
sb中的int a 占4个字节,紧接着的char b占用1个字节,但是char b随后的double c需要占用8个字节,所以char b和double c不能挤在一个4个字节的单位里面。于是在char b和double c之间填了3个字节的空位,实现与其后的数据的对齐,这样就不难理解为什么sizeof(sa1)=8了。
我试图按着同样的思路理解sizeof(sc)=24,但是却遇到了问题。sc中的int a占用4个字节,紧接着的double c占用8个字节,最后char b占用一个字节,但是为了填满4个字节的单位,char b后面应该填补3个字节的空位。这样下来sizeof(sc)应该等于4+8+4=16才对,可是输出的结果却是sizeof(sc)=24。看来我刚才的“猜想”是有问题的。问题究竟出在哪里呢?为什么根据我的“猜想”分析sa和和sb都没有问题,到sc就出现问题了呢?
sb和sc的唯一区别就是char b和double c的定义次序颠倒了,结果使得sizeof(sb)和sizeof(sc)产生了差别。能想到的唯一解释是sb和sc的对齐的最小单位不一样了。于是我又设计了一组对比:
struct
{
int a;
int d;
double c;
char b;
} sc1;
struct
{
int a;
double c;
} sc2;
输出结果:sizeof(sc1)=24, sizeof(sc2)=16;
sc1在sc的基础上在int a后面紧接着加上了int d;但是结果sizeof(sc1)=24= sizeof(sc),这说明在int a的后面存在4个字节的空位。看来这次系统给结构体内的变量分配空间的时候,不再像sa那样以4个字节为一组了。
再看sc2,只是去掉了一个char b,结果使sizeof(sc2)=16,这说明char b后面存在着7个字节的空位。由此可见这次系统是以8个字节为一组给结构体内的变量分配空间的。
综合分析以上的试验结果,sa以4个字节为一组给结构体内的变量分配空间,从而达到数据对齐;sb和sc以8个字节为一组给结构体内的变量分配空间,从而达到数据对齐。为什么会有差别呢?仔细观察不难发现:sa中占用空间最多的类型是int型,而sizeof(int)=4;sb和sc中占用空间最多的类型是double型,而sizeof(double)=8;由此可间,系统是根据结构体内所包含的类型制定分配空间的单位的。
之后我又针对这个问题做了几组试验,得到的结果与以上的分析结论相同。
这样,以后再遇到sizeof的时候,我就不用等到输出才能看到sizeof的值了,只要用看到struct内部的定义格式就能计算出sizeof的实际值了。
得出结论后,我又查阅了与sizeof相关的资料,发现可以分别在程序内部和用编译指令指定结构体内部对齐的具体方式:
1、在程序内部用#pragma pack(n)指定
#pragma pack(1)可以使编译器不在struct内留空位。
#include <iostream>
using namespace std;
struct // packing is 8
{
int a;
char b;
} sa;
#pragma pack(push,1) // packing is now 1
struct
{
int a;
char b;
double c;
} sb;
#pragma pack(pop) // packing is 8
struct
{
int a;
double c;
char b;
} sc;
int main(void)
{
cout << "sizeof(sa) =" << sizeof(sa) << endl;
cout << "sizeof(sb) =" << sizeof(sb) << endl;
cout << "sizeof(sc) =" << sizeof(sc) << endl;
return 0;
}
输出结果:sizeof(sa)=8,sizeof(sb)=13,sizeof(sc)=24
还有一点必须指出:由#pragma pack(n)设定packing后,编译器实际排列结构体内部成员的时候,并不一定是按照#pragma pack(n)中的n指定长度为单位的,还取决于结构体内部占用空间最大的类型的长度。这么做可能是为了节省空间。例如:
struct // packing is 8
{
int a;
char b;
} sa;
在排列成员int a和char b的时候是以4个字节为单位的。因为此时sizeof(int)=4<8,所以排列的时候以4为单位。但是当packing值小于4的时候则以packing值为单位排列。所以:
struct // packing is 8
{
int a;
int c;
char b;
} sa;
输出结果为:sizeof(sa)=12;(以4个字节为单位)
当packing值小于结构体内占用空间最多成员占用的字节数的时候,以packing值为准。
#pragma pack(push,1) // packing is now 1
struct
{
int a;
char b;
double c;
} sb;
输出结果为:sizeof(sb)=13;(以1个字节为单位)