struct结构体和union联合体:字节对齐下的sizeof返回

首先,明白什么是struct结构体,什么事union联合体,以及sizeof

struct和union都属于复合数据结构,其中可以包含多种数据类型,包括int,short,double,甚至数组和struct,union。

既然是数据结构,那么一定需要开辟内存空间用来存储数据,接下来的一切计算过程都是在32windows系统环境进行的。

struct和union的存储是有区别的,前者的内存是成员变量的累加,后者是最大的成员变量所需的空间(这些都要遵循下面讲到的规则),而且前者改变一个成员变量不会影响到另一个,但后者只在开辟的内存中存储最近一次写入的值。另外,struct内的成员变量是连续分布的,这一点也很重要。


基于上面关于struct和union的区别,两者计算sizeof时的规则有些许出入。

接着,说明一下sizeof的用法。sizeof的主要功能就是计算对象或者类型所对应的内存开销,也就是字节个数。在32系统中,char,short,int,long,double对应的内存开销分别是1,2,4,4,8个字节,这也是后面例子中主要用到的基本数据类型。

如果是计算这些基本类型的sizeof的话,应该不难,问题是计算复合数据结构struct和union就有点难度了,因为涉及到一个字节对齐的概念。利用字节对齐的概念,可以在保证合理的空间浪费的前提下,尽可能地提高CPU对数据的访问速度。具体概念请查阅msdn手册。


接下来给出上文提到的计算struct和union的sizeof时要遵守的规则,这些规则是从成员变量地址的角度出发。

对于struct    

特有规则 :

s1. 第一个成员的地址被定义为零。

s2. 除第一个成员的其他成员的首地址都必须可以被自己所对应的数据类型的内存开销(并非成员的大小,而是规则a,b,c的结果)整除,即可能需要在前一个成员变量后补充空字节(也就是被“浪费”的字节).

s3. 所有成员计算完毕后,还要是整个struct的总大小(sizeof结果)可以整除所有成员类型对应的内存开销中的最大值且尽量保持结果尽量小。

对于union

特有规则:

u1. 找出成员中占用字节数最大的,以它作为下限

u2. 找出内存开销最大的成员,然后求其正整数倍数以保证大于条件1中的下限值,同时尽量小。


可以看出两者的基本规则是不同的,但都同时提到两个量:每个成员的内存开销和所有成员开销中最大的值。他们都跟自己的类型有关系,且遵循同一条规则,

共同规则:

a. 无论成员类型是基本的int,char,double,还是复合的struct,union和数据,都只取数据体中的最大的基本数据对应的内存开销。对于int,char这些本来就是基本数据的,取其本身对应的开销值,但是对于struct,union和数据,他们就要进入数据体内分析,应该取数据体中的所有基本类型对应的最大开销值。这里有两点特殊:

b. 数据体还有数据体,那么应该继续深入进去,也就是递归。

c. 基本类型的数组,就当做单独的基本类型看待。


另外,如果有#pragma pack(n)的预处理命令,有规则

t1: 取a,b和c中的求值结果和n的最小者作为结果。


下面给出例子:

<span style="font-size:18px;">#include<iostream>
#define Offset(type,field) ((size_t)&(((type*)0)->field)) //计算struct体内成员的偏移地址()
using namespace std;


struct example{
	double a;
};

struct example1
{
    short a;
	example b;
};

struct example2 
{
    char a;
    example1 b;
    short c;
};


struct st1{
	double a;
	short b;
	char c;
};

struct st2{
	char a;
	st1 b;
	int c;
};

struct st3
{
	char a;
	int b[3];
	char c;
};

typedef union{
	double b;
	int a;
} unionhelper;
struct st4{
	unionhelper a;
	int b;
};

typedef union{
	char a;
	int b[3];
	double c;
} un1;

typedef union{
	st4 a;
	int b[5];
} un2;

#pragma pack(4)
struct st5{
	short a;
	double b;
};

int main()
{
	//struct
	//eg1
	cout<<Offset(st1,a)<<" "<<Offset(st1,b)<<" "<<Offset(st1,c)<<endl; //偏移地址:0,8,10. (对应规则s1和s2)
	cout<<sizeof(st1)<<endl; //st1的总大小等于16(是8的倍数,对应规则s3)
	//eg2
	cout<<Offset(st2,a)<<" "<<Offset(st2,b)<<" "<<Offset(st2,c)<<endl; //偏移地址:0,8,24. (对应规则s1,s2和b)
	cout<<sizeof(st2)<<endl; //st1的总大小等于32(是8的倍数,对应规则s3和b)
	//eg3
	cout<<sizeof(example2)<<endl; //st1的总大小等于32(是8的倍数,对应规则s1,s2,s3和b)
	//eg4
	cout<<Offset(st3,a)<<" "<<Offset(st3,b)<<" "<<Offset(st3,c)<<endl; //偏移地址:0,4,16. (对应规则s1,s2和c)
	cout<<sizeof(st3)<<endl; //st1的总大小等于20(是4的倍数,对应规则s3和c)
	//eg5
	cout<<Offset(st4,a)<<" "<<Offset(st4,b)<<endl; //偏移地址:0,8. (对应规则s1,s2和b)
	cout<<sizeof(st4)<<endl; //st1的总大小等于16(是8的倍数,对应规则s3和b)

	//union
	//eg6
	cout<<sizeof(un1)<<endl; //un1的总大小等于16(是8的倍数,对应规则u1和u2,以及规则a和c)
	//eg7
	cout<<sizeof(un2)<<endl; //un1的总大小等于24(是8的倍数,对应规则u1和u2,以及规则a,b,c)
	
	//#pragma pack(n)预处理命令
	cout<<Offset(st5,a)<<" "<<Offset(st5,b)<<endl; //偏移地址:0,4. (对应规则t1)
	cout<<sizeof(st5)<<endl; //st1的总大小等于12(是4的倍数,对应规则t1)
	return 0;
}</span>



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值