结构体的大小计算
原则一:结构体的元素按顺序存储,结构体成员的偏移量必须是成员大小的整数倍
原则二:结构体大小是所有成员大小的整数倍(除了内部结构体和数组)
看例子比较快理解,以下是自己的理解
指针的占字节数要看是32还是64位,32占4字节,64位占8个字节
struct X {
char a; //1
char b; //1
int c; //8
};
//这个结构体占8个字节,因为结构体成员中占最大字节数的是int类型,所以成员都要为4的整数倍
//两个char各自占1,因为是顺序存储的,所以两个char可以加起来占2,要和4个字节对其,需补2个字节
//所以最后顺序存储的char、char、int的结构体X占4+4=8个字节
struct X {
char a; //1
int b; //4
char c; //1
};
//结构体占12个字节,因为结构体成员中占最大字节数的是int类型,所以要4的整数倍
//和上面例子的顺序不一样了,第一个char元素补3个字节,最后一个char也要补三个字节
//所以最后顺序存储char、int、char各自都为4字节,即结构体X占4+4+4=12字节
struct X {
char a; //1
int b; //4
char c[10]; //10
};
//举个有数组的例子,这个数组是char型,一个char型占一个字节,这个数组就为10个字节
//根据原则二,数组不算成员最大字节数,
//所以这个结构体的成员占最大字节数的类型还是int型,所以要按4的整数倍补齐
//第一个char补3个字节,数组char[10]占10个字节,要为4的整数倍需要补2个字节,所以为12
//最后顺序存储char、int、char[10],所以最后结构体X占4+4+12=20字节
//接下来的例子有两种情况要分析
struct X {
char a; //1
int b; //4
struct Y {
char a; //1
int b; //4
};
float; //4
};
//这个结构体占12个字节,首先我们要知道这个结构体的内部结构体只是声明,所以计算大小会忽略,不占空间
//知道这个很快就知道,里面只计算了顺序存储的char、int、float,所以结构体X占4+4+4=12字节
struct X {
char a; //1
int b; //4
struct Y {
char a; //1
int b; //4
}temp;
float; //4
};
//这是第二种情况,可以看到temp这个变量了,所以这个内部结构体要占空间了
//但是注意一个问题,因为原则二说过了,内部结构体不能当做成员整数倍
//所以这里单纯把内部结构体看成了一个char和一个int,不看整体,这是非常重要的
//最后char、int、char、int、float顺序存储,算出这个结构体X占4+4+4+4+4=20个字节
struct X
{
char a; //1
int b; //4
double c; //8
};
struct Y
{
char a; //1
struct X b;//16
};
//上个例子说了内部结构体的注意点,但是这个例子有几个地方要注意
//这里的例子要和上个例子区别看清楚,我们要计算结构体Y的大小需要知道X的成员,
//这里先说一下单看结构体X是占16个字节
//如果X的占最大字节数成员有比Y的其他最大字节数的成员大,需要以X的那个为准,比如这里double比char大
//X的成员double是Y里占最大字节数的类型,所以Y的成员都要是8的整数倍
//顺序存储char、X,上个例子的顺序存储可不是这样
//所以总结出,要算结构体Y我们所有成员要以8为整数倍,X看成一个占16字节的成员,这里很重要
//char要补7个字节,而X占16字节已经是8的整数倍了,所以最后结构体Y占8+16=24个字节
//这里如果写成
struct Y
{
struct X b;
char a;
};
//也是24个字节
struct X
{
char a; //1
int b; //4
union Y{
char c;
int d;
};
};
//如果是联合体的话就按联合体里占最大字节数那个算
//这里联合体Y占4个字节
//所以最后结构体X占4+4+4=12个字节
接下来的例子是有#pragma pack指定对其的例子,两个例子即可理解
#pragma pack(4)
struct X
{
char a; //1
int b; //4
float //4
double //8
};
//这个例子开头的#pragma pack(4)意思是指定向4对其
//这是第一个例子,我们第一要看的是结构体成员占最大字节数的是什么类型,是否大于指定的数字,这里是4
//这里最大的成员类型是double,8个字节,比指定的4大,所以要按4字节对其
//所以按4字节对其,我们就知道了怎么算结构体大小了
//最后顺序存储char、int、float、double,所以结构体X占4+4+4+8=20个字节
#pragma pack(10)
struct X
{
char a; //1
int b; //4
float //4
double //8
};
//和上面例子结构体一致,但是指定了向10对其
//说过了我们第一要看的是结构体成员占最大字节数的是什么类型,是否大于指定的数字,这里是10
//最大的是double占8个字节,比指定的10小,那么就要以8个字节对其了
//最后顺序存储char、int、float、double,所以结构体X占8+8+8=24个字节
//不难看出唯一要注意的地方就是看结构体成员占最大字节数的是什么类型
//是否大于pack指定的数字,大于就按pack指定的对其,小于就按它本身对其
位域的大小计算
介绍:有些信息在存储时,并不需要占用一个完整的字节,而只需占几个或一个二进制位。例如在存放一个开关量时,只有 0 和 1 两种状态,用 1 位二进位即可。为了节省存储空间,并使处理简便,C 语言又提供了一种数据结构,称为"位域"或"位段"。所谓"位域"是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。这样就可以把几个不同的对象用一个字节的二进制位域来表示。
struct 位域结构名 { 位域列表 };
type [member_name] : width ; // 位域列表的形式
位域的定义有几点需要说明:
1)宽度必须小于或等于指定类型的位宽度
2)一个位域存储在同一个字节中,如一个字节所剩空间不够存放另一位域时,则会从下一单元起存放该位域。也可以有意使某位域从下一单元开始
3)位域可以是无名位域,这时它只用来作填充或调整位置。无名的位域是不能使用的
struct k{
int a:1;
int :2; /* 该 2 位不能使用 */
int b:3;
int c:2;
};
接下来是计算大小
1.如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;
2.如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍
3.如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式,Dev-C++采取压缩方式;
4.如果位域字段之间穿插着非位域字段,则不进行压缩;
5. 整个结构体的总大小为最宽基本类型成员大小的整数倍。
直接上例子
struct X
{
int a1 : 5;
int a2 : 5;
int a3 : 5;
int a4 : 5;
int a5 : 5;
};
//这里先来看一下都是同类型的例子
//这里先知道int占4个字节,一个字节8位,所以int有32位
//顺序存储的,所以从上往下看
//可以看到a1占5位,小于32位,所以剩下的27可以给下面的使用
//继续往下看,a2是也是和上面同个类型int,所以可以用上面剩下的27给它用
//a2也用了5位,那么剩下22位可以继续给下面的使用
//以此类推,当a5用完之后,还剩下7位没用,我们不管,小于32位就是4个字节
//所以这个X占4个字节
struct X
{
int a : 29;
int b : 29;
int c : 4;
};
//继续来看一个同类型的例子,根据上面例子的方法,直接来看
//a占29位,剩下3位给下面的使用
//往下看b占29位,而上面是同类型的int,可以用它剩下的
//可是上面的只剩下3位,而b占29不够用,需要开辟新的空间,即4个字节32位
//b也还剩下3位可以给下面继续使用,
//继续看c占4位,可是上面的3位不够用,也要开辟新的空间,即4个字节32位
//所以最后X占4+4+4=12字节
struct X
{
int a : 1;
int : 2;
int b : 3;
int c : 2;
int d : 3;
short e : 4;
int f : 1;
};
//这次例子有不同类型了,直接来看和之前不一样的地方
//这里第二个是个空域,单纯补充占大小
//从上往下看,a到d的位算出来1+2+3+2+3=11位,小于32位,剩下21位可以给下面用
//看到e,上面虽然剩下21位够e的4位用,可是类型不一致,所以e要开新的空间,short是4个字节
//同理f和上面的e不是一个类型,开辟新空间
//所以X占4+4+4=12个字节
上面的是个人理解,有说不好的,说错的可以继续改进