sizeof的详尽计算结构体字节数⽅法
Sizeof⽤法
本⽂主要包括⼆个部分,第⼀部分重点介绍在VC中,怎么样采⽤sizeof来求结构的⼤⼩,以及容易出现的问题,并给出解决问题的⽅法,第⼆部分总结出VC中sizeof的主要⽤法。
1、 sizeof应⽤在结构上的情况
请看下⾯的结构:
struct MyStruct
{
double dda1;
char dda;
int type
};
对结构MyStruct采⽤sizeof会出现什么结果呢?sizeof(MyStruct)为多少呢?也许你会这样求:
sizeof(MyStruct)=sizeof(double)+sizeof(char)+sizeof(int)=13
但是当在VC中测试上⾯结构的⼤⼩时,你会发现sizeof(MyStruct)为16。你知道为什么在VC中会得出这样⼀个结果吗?
其实,这是VC对变量存储的⼀个特殊处理。为了提⾼CPU的存储速度,VC对⼀些变量的起始地址做了“对齐”处理。在默认情况下,VC规定各成员变量存放的起始地址相对于结构的起始地址的偏移量必须为该变量的类型所占⽤的字节数的倍数。下⾯列出常⽤类型的对齐⽅式(vc6.0,32位系统)。
类型对齐⽅式(变量存放的起始地址相对于结构的起始地址的偏移量)
Char偏移量必须为sizeof(char)即1的倍数
int偏移量必须为sizeof(int)即4的倍数
float偏移量必须为sizeof(float)即4的倍数
double偏移量必须为sizeof(double)即8的倍数
Short偏移量必须为sizeof(short)即2的倍数
各成员变量在存放的时候根据在结构中出现的顺序依次申请空间,同时按照上⾯的对齐⽅式调整位置,空缺的字节VC会⾃动填充。同时VC为了确保结构的⼤⼩为结构的字节边界数(即该结构中占⽤最⼤空间的类型所占⽤的字节数)的倍数,所以在为最后⼀个成员变量申请空间后,还会根据需要⾃动填充空缺的字节。
下⾯⽤前⾯的例⼦来说明VC到底怎么样来存放结构的。
struct MyStruct
{
double dda1;
char dda;
int type
};
为上⾯的结构分配空间的时候,VC根据成员变量出现的顺序和对齐⽅式,先为第⼀个成员dda1分配空间,其起始地址跟结构的起始地址相同(刚好偏移量0刚好为sizeof(double)的倍数),该成员变量占⽤sizeof(double)=8个字节;接下来为第⼆个成员dda分配空间,这时下⼀个可以分配的地址对于结构的起始地址的偏移量为8,是sizeof(char)的倍数,所以把dda存放在偏移量为8的地⽅满⾜对齐⽅式,该成员变量占⽤sizeof(char)=1个字节;接下来为第三个成员type分配空间,这时下⼀个可以分配的地址对于结构的起始地址的偏移量为9,不是sizeof(int)=4的倍数,为了满⾜对齐⽅式对偏移量的约束问题,VC⾃动填充3个字节(这三个字节没有放什么东西),这时下⼀个可以分配的地址对于结构的起始地址的偏移量为12,刚好是sizeof(int)=4的倍数,所以把type存放在偏移量为12的地⽅,该成员变量占⽤sizeof(int)=4个字节;这时整个结构的成员变量已经都分配了空间,总的占⽤的空间⼤⼩为:8+1+3+4=16,刚好为结构的字节边界数(即结构中占⽤最⼤空间的类型所占⽤的字节数sizeof(double)=8)的倍数,所以没有空缺的字节需要填充。所以整个结构的⼤⼩为:sizeof(MyStruct)=8+1+3+4=16,其中有3个字节是VC⾃动填充的,没有放任何有意义的东西。
注意:不仅仅要按照“偏移量为类型倍数”这个原则,⽽且还要在每个元素分配完毕后,再检查“字节边界数”是否正确。有时因为⼤意导致此处少计算⼏个字节。如下例:
下⾯再举个例⼦,交换⼀下上⾯的MyStruct的成员变量的位置,使它变成下⾯的情况:
struct MyStruct
{
char dda;
double dda1;
int type
};
这个结构占⽤的空间为多⼤呢?在VC6.0环境下,可以得到sizeof(MyStruc)为24。结合上⾯提到的分配空间的⼀些原则,分析下VC 怎么样为上⾯的结构分配空间的。(简单说明)
struct MyStruct
{
char dda;//偏移量为0,满⾜对齐⽅式,dda占⽤1个字节;
double dda1;//下⼀个可⽤的地址的偏移量为1,不是sizeof(double)=8
//的倍数,需要补⾜7个字节才能使偏移量变为8(满⾜对齐
//⽅式),因此VC⾃动填充7个字节,dda1存放在偏移量为8
//的地址上,它占⽤8个字节。
int type;//下⼀个可⽤的地址的偏移量为16,是sizeof(int)=4的倍
//数,满⾜int的对齐⽅式,所以不需要VC⾃动填充,type存
//放在偏移量为16的地址上,它占⽤4个字节。
};//所有成员变量都分配了空间,空间总的⼤⼩为1+7+8+4=20,不是结构
//的节边界数(即结构中占⽤最⼤空间的类型所占⽤的字节数sizeof
//(double)=8)的倍数,所以需要填充4个字节,以满⾜结构的⼤⼩为
//sizeof(double)=8的倍数。
所以该结构总的⼤⼩为:sizeof(MyStruc)为1+7+8+4+4=24。其中总的有7+4=11个字节是VC⾃动填充的,没有放任何有意义的东西。
VC对结构的存储的特殊处理确实提⾼CPU存储变量的速度,但是有时候也带来了⼀些⿇烦,我们也屏蔽掉变量默认的对齐⽅式,⾃⼰可以设定变量的对齐⽅式。
VC中提供了#pragma pack(n)来设定变量以n字节对齐⽅式。n字节对齐就是说变量存放的起始地址的偏移量有两种情况:第⼀、如果n⼤于等于该变量所占⽤的字节数,那么偏移量必须满⾜默认的对齐⽅式,第⼆、如果n⼩于该变量的类型所占⽤的字节数,那么偏移量为n 的倍数,不⽤满⾜默认的对齐⽅式。结构的总⼤⼩也有个约束条件,分下⾯两种情况:如果n⼤于所有成员变量类型所占⽤的字节数,那么结构的总⼤⼩必须为占⽤空间最⼤的变量占⽤的空间数的倍数;否则必须为n的倍数。
⼀定要领会以上的意思。
下⾯举例说明其⽤法。
#pragma pack(push) //保存对齐状态
#pragma pack(4)//设定为4字节对齐
struct test
{
char m1;
double m4;
int m3;
};
#pragma pack(pop)//恢复对齐状态
sizeof 指针以上结构的⼤⼩为16,下⾯分析其存储情况,⾸先为m1分配空间,其偏移量为0,满⾜我们⾃⼰设定的对齐⽅式(4字节对齐),m1占⽤1个字节。接着开始为m4分配空间,这时其偏移量为1,需要补⾜3个字节,这样使偏移量满⾜为n=4的倍数(因为sizeof(double)⼤于n),m4占⽤8个字节。接着为m3分配空间,这时其偏移量为12,满⾜为4的倍数,m3占⽤4个字节。这时已经为所有成员变量分配了空间,共分配了16个字节,满⾜为n的倍数。如果把上⾯的#pragma pack(4)改为#pragma pack(16),那么我们可以得到结构的⼤⼩为
24。(?这个地⽅不理解)
2、 sizeof⽤法总结
在VC中,sizeof有着许多的⽤法,⽽且很容易引起⼀些错误。下⾯根据sizeof后⾯的参数对sizeof的⽤法做个总结。
A.参数为数据类型或者为⼀般变量。例如sizeof(int),sizeof(long)等等。这种情况要注意的是不同系统或者不同编译器得到的结果可能是不同的。例如int类型在16位系统中占2个字节,在32位系统中占4个字节。
B.参数为数组或指针。下⾯举例说明.
int a[50]; //sizeof(a)=4*50=200; 求数组所占的空间⼤⼩
int *a=new int[50];// sizeof(a)=4; a为⼀个指针,sizeof(a)是求指针
//的⼤⼩,在32位系统中,当然是占4个字节。
C.参数为结构或类。Sizeof应⽤在类和结构的处理情况是相同的。但有两点需要注意,第⼀、结构或者类中的静态成员不对结构或者类的⼤⼩产⽣影响,因为静态变量的存储位置与结构或者类的实例地址⽆关。
第⼆、没有成员变量的结构或类的⼤⼩为1,因为必须保证结构或类的每⼀个实例在内存中都有唯⼀的地址。
下⾯举例说明,
Class Test{int a;static double c};//sizeof(Test)=4.
Test *s;//sizeof(s)=4,s为⼀个指针。
Class test1{ };//sizeof(test1)=1;
D.参数为其他。下⾯举例说明。
int func(char s[5]); //这个地⽅⼀定要引起注意
{
cout<<sizeof(s);//这⾥将输出4,本来s为⼀个数组,但由于做为函
//数的参数在传递的时候系统处理为⼀个指针,所
//以sizeof(s)实际上为求指针的⼤⼩。
return 1;
}
sizeof(func(“1234”))=4//因为func的返回类型为int,所以相当于
//求sizeof(int).
以上为sizeof的基本⽤法,在实际的使⽤中要注意分析VC的分配变量的分配策略,这样的话可以避免⼀些错误。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论