结构体,枚举,联合大小的计算规则

目录

1.结构体大小的计算

补充(位段)

2.枚举的大小(4个字节)

3.联合大小的计算


1.结构体大小的计算

(1)结构体内存对齐的规则

1. 第一个成员在与结构体变量偏移量为 0 的地址处。


2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数 = 编译器默认的对齐数 与 该成员大小的 两者之间的较小值
VS 中默认的值为 8     gcc没有默认对齐数,那么对齐数就是该成员的大小


3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。


4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

例如

struct S1
{
	char c1;//大小为1,默认对齐数为8,取其小就是1,所以其对齐数是1
	int a;//大小为4,默认对齐数为8,取其小为4,所以其对齐数就是4
	char c2;
}

int main()
{
	struct S1 s1={0};
	printf("%d\n",sizeof(s1));
}

 其内存分配如图所示

 其中的成员占了9个内存单元,但是结构体的内存大小是其最大对齐数的整数倍,这里的最大对齐数是4,所以在9以上的,最小的,4的倍数是12

再来一例,与上面同理,直接看图

struct S2
{
	char c1;
	char c2;
	int a;
}

int main()
{
	struct S2 s2={0};
	printf("%d\n",sizeof(s2));
}

 成员的内存总共为8个内存单元,刚好也是4的倍数

可以看到两个结构体的成员是相同的,但是后者占用内存比前者少,所以总结起来

将占用内存小的成员写在前面 ,这样能更加高效的利用空间

(2) 那么既然有空间的浪费,为什么不紧密的存放每一个成员呢,内存对齐的意义

1. 平台原因 ( 移植原因 ) :
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2. 性能原因 :
数据结构 ( 尤其是栈 ) 应该尽可能地在自然边界上对齐。
原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
总体来说: 结构体的内存对齐是拿空间 来换取 时间 的做法。
 

 (3)修改默认对齐数

结构在对齐方式不合适的时候,可以自己更改默认对齐数。

#pragma 这个预处理指令,可以改变默认对齐数,例如

#pragma pack(1)设置默认对齐数为1,这样每一个结构体成员都是紧密排列的了

#pragma pack(1)//设置默认对齐数为1
struct S2
{
 char c1;
 int i;
 char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认

补充(位段)

位段的内存分配
1.位段的成员可以是intunsignedint signedint或者是char(属于整形家族)类型

2.位段的空间上是按照需要以4个字节(int)或者1个字节(char)的方式来开辟的

3.位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段

struct S
{
	int a;
	int b;
	int c;
	int d;
};

按照上面的结构体的计算规则,那么这个结构体的大小为16

对于位段是8个字节

struct S
{
	int a:2;//冒号后的数字表示a只需要2个比特位
	int b:5;
	int c:10;
	int d:30;
}
//总共需要47个比特位,那么6个字节足够(6*8)=48,计算的结果却为8个字节

 注意位段是按整型大小的字节进行开辟的

注意:

struct S
{
	int a:2;
	int b:5;
	int c:10;
	int d:33;//不能超过32个比特,报错:d的位域大小无效
}

 本来int类型需要占用32位空间,现在只需要占用2个比特位的空间,位段的使用节省了空间大小

 举例

struct S
{
	char a:3;
	char b:4;
	char c:5;
	char d:4;
}

int main()
{
	struct S s={0};
	
	s.a=10;//二进制序列1010
	s.b=20;
	s.c=3;
	s.d=4;
	
	return 0;
	
}

 首先struct S s={0};所以先给s分配8个比特位

 如上图所示a只有3个比特位,但是s.a=10//二进制序列为1010,所以内存中只能存3个比特位,即1010中的010

s.b=20//二进制序列10100占5个比特,但是b只有4个比特,所以只能传“0100”

s.c=3//二进制序列011占3个比特,但是c有5个比特,前面补0,即“00011”

s.d=4//二进制序列为100,但是d占4个比特,前面补0,即“0100”

 所以总体写下来如图所示

阅读内存时,内存是16进制数字,则四个2进制位为一个16进制,从左往右依次读

0010 0010 0000 0011 0000 0100

2       2       0       3       0       4   

位段的跨平台问题

1. int 位段被当成有符号数还是无符号数是不确定的。
2. 位段中最大位的数目不能确定。
( 16 位机器最大 16 , 32 位机器最大 32 ,写成 27 ,在 16 位机器会出问题。
3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
4. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的

总结

跟结构相比,位段可以达到同样的效果,可以很好的节省空间,但是有跨平台的问题存在

应用:用位段封装网络上传输的数据包等

2.枚举的大小(4个字节)

enum Color//颜色
{
 RED=1,
 GREEN=2,
 BLUE=4
};
enum Color clr = GREEN;//只能拿枚举常量给枚举变量赋值,才不会出现类型的差异。
clr = 5;
enum Sex
{
	MALE,
	FEMALE,
	SECRET
}

int main()
{
	enum Sex s=MALE;
	printf("%d\n",sizeof(s));
	
	return 0;	
} 

结果为4

3.联合大小的计算

(1)计算规则

联合的大小至少是最大成员的大小。

当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍

union Un
{
	int a;//成员大小为4,默认对齐数为8,对齐数为4
	char arr[5]; //成员大小char是1,默认对齐数是8,得到对齐数是1,不要拿数组的大小进行计算
}

int main()
{
	union Un u;
	printf("%d\n",sizeof(u));
	return 0;
}

 如果联合体如下图所示

那么其占用内存总大小为5个内存单元,但是联合的大小

至少是最大成员的大小。

当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍

5(最大成员大小)不是(最大对齐数)4的整数倍,所以应该按下图进行内存分配

如图浪费了3个内存单元,使得总内存为8个字节,为最大字节数(4)的整数倍 

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值