位段,枚举,联合的讲解

##文章具体包括:
位段的介绍,存储方式,计算大小
枚举的介绍,使用举例
联合的介绍,存储方式,大小计算

一、位段

位段:度娘给出的解释是,C语言允许在一个结构体中以位为单位来指定其成员所占内存长度,这种以位为单位的成员称为“位段”或称“位域”( bit field) 。利用位段能够用较少的位数存储数据。

位段的声明和结构体很相似,但不同的是:
①位段的成员可以是int,unsigned int,signed int,char,short,long,long long(但不能是浮点float,double)类型
②位段的成员名后边有一个冒号和一个数字,这个数字表示位段所占的二进制位数。
如:

struct A {
	int _a : 25;
	int _b : 6;
	char _c : 7;
	unsigned int _d : 30;
	short _e : 13;
	signed int _f : 6;
	short _g : 13;
}bitSegement;

那么位段A多大呢?在计算之前,先说下位段怎么存储

简单说就是看变量后面的数字。先看第一个变量类型,开辟对应类型大小的字节,然后数字是多少就占多少bit,剩余bit留给下一个变量用。不够的话再开辟对应类型大小字节数。

细说的话,对于位段结构,编译器会自动进行存储空间的优化,要遵循这些原则原则:
1)如果一个位段存储单元能够存储得下位段结构中的所有成员,那么位段结构中的所有成员只能放在一个位段存储单元中,不能放在两个位段存储单元中;如果一个位段存储单元不能容纳下位段结构中的所有成员,那么从剩余的位段从下一个位段存储单元开始存放。
2)下一个成员要想使用上一个成员剩余的空间,必须和它类型相同,且占用空间不能超出剩余空间。否则,需单独占用一块空间。
3)如果一个位段结构中只有一个占有0位的无名位段,则只占1或0字节的空间(C语言中是占0字节,而C++中占1字节);否则其他任何情况下,一个位段结构所占的空间至少是一个位段存储单元的大小;
3)位段成员存储也遵循内存对齐原则。

  1. 再看上面的例子,int类型首先开辟4字节。_a实际占用25bit,_b实际占用6bit,剩余1bit不够_c使用。因此_c申请使用1byte,实际占用7bit,剩余1bit不够_d使用。_d需新申请空间。
  2. 此时_a,_b,_c共使用9byte,偏移量是9。_d会进行结构体对齐,在偏移量为自己类型大小整数倍的位置开始。因此,_d会在偏移量12的位置开始申请使用4byte,实际占用30bit,剩下2bit仍被浪费了。_e申请使用2byte。
  3. _f进行内存对齐,从偏移量20位置开始,申请使用4byte。_g申请2byte。
  4. 最后,总占用22byte,但结构体空间大小要是最大对齐数整数倍,所以是24byte。

使用位段需注意以下几点:
1)位段占的二进制位数不能超过该基本类型所能表示的最大位数,否则编译提示:“位域类型对位数太小”。
2)无名位段不能被访问,但是会占据空间;
3)不能对位段进行取地址操作;
4)若位段占的二进制位数为0,则这个位段必须是无名位段,下一个位段从下一个位段存储单元(这里的位段存储单元经测试在VC环境下是4个字节)开始存放;
5)若位段出现在表达式中,则会自动进行整型升级,自动转换为int型或者unsigned int。
6)对位段成员赋值时,不要超过位段所能表示的最大范围,否则会截取后面有效位数。
7)位段不能出现数组的形式。

二、枚举

就是一一列举出相类似的东西,如星期,班级,商品等。
enum Day // 星期
{ Mon,
Tues,
Wed,
Thur,
Fri,
Sat,
Sun
}D1,D2;
enum Class//班级
{
c1,
c2,
c3,
c4
}C1,C2;
enum Day, enum Class都是枚举类型,{}中的是枚举常量,不是变量,不能在程序中用赋值语句再对它赋值。枚举常量它们如果没有初始化的话,从第一个默认是0,后面的依次加1。如果初始化了的话,被初始化的枚举常量之前的不变,其他的根据枚举常量的值依次加1。{}后面的D,C类似结构体,是枚举变量。
需要注意的是,可以把枚举常量赋给变量,但不能把枚举常量的数值赋给枚举变量。如D1=Mon,D2=Tues;是正确的,而D1=0,D2=1;是错误的。如果非要把数值赋给枚举变量的话需要通过强制类型转换,D1=(enum Day)0;

#include<stdio.h>
int main()
{
	int i, j;
	int mon[32];
	enum Day // 星期
	{
		Mon = 1,
		Tues,
		Wed,
		Thur,
		Fri,
		Sat,
		Sun
	};
	j = Mon;
	for (i = 1; i < 32; i++)
	{
		mon[i] = j;
		j++;
		if (j > Sun)
		{
			j = Mon;
		}
	}
	for (i = 1; i < 32; i++)//打印31天内的周一到周日
	{
		switch (mon[i])
		{
		case 1: printf(" %2d", mon[i]); break;
		case 2: printf(" %2d", mon[i]); break;
		case 3: printf(" %2d", mon[i]); break;
		case 4: printf(" %2d", mon[i]); break;
		case 5: printf(" %2d", mon[i]); break;
		case 6: printf(" %2d", mon[i]); break;
		case 7: printf(" %2d", mon[i]); break;
		default:break;
		}

		if (0 == i % 7)
		{
			printf("\n");
		}
	}
	printf("\n");
}

三、联合

联合是一种特殊的自定义类型,这种类型定义的变量包含一系列的成员,特征是这些成员公用同一块空间(所以联合也叫共用体)。

union Un
 {    
int a;   
char b;
short c;
}; 

因为联合的成员是共用同一块内存空间的,所以一个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)。
当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。

int main()
{
	union Un
	{
		int i;
		char c;
	};
	union Un un;

	//因为两个成员占用相同内存,所以地址相同
	printf("%p\n", &(un.i));
	printf("%p\n", &(un.c));

	un.i = 0x11111111;

	//因为char类型是1字节,16进制表示,char类型只能用2个数字,如果多于2个数字会发生类型截断
	un.c = 0x22;

	//‭输出286331170,因为共用一块内存且小端存储,所以本应0x11111111变为0x11111122,十进制表示‬286331170
	printf("%d\n", un.i);
}

根据上个程序可知,联合可以判断当前计算机的大小端存储。如果计算机是大端存储,则0x11111111会变为0x22111111,输出结果是‭571543825‬。
自己仿照上例写个简单的

int main()
{
	union Un
	{
		int a;
		char b;
	}u;
	
	u.a = 0x11;
	u.b = 0x01;

	//小端存储,结果是1;如果是大端存储则是16
	printf("%d\n", u.a);
}

计算联合结构的大小

int main()
{
	union Un1 
	{
		char a[5];  
		int b; 
	}; 

	union Un2 
	{
		short a[7];  
		int b; 
	};

	//下面输出的结果是什么? 
	printf("%d\n", sizeof(union Un1));//8
	printf("%d\n", sizeof(union Un2));//16

}

再算一题


int main()
{
	union ip_addr
	{
		unsigned long addr;   
		struct 
		{
		unsigned char c1;     
		unsigned char c2;    
		unsigned char c3;      
		unsigned char c4; 
		}ip;
	};

	union ip_addr my_ip; 
	// 176238749转换为16进制0xA 81 30 9D,因为是联合结构
	//add和结构体变量ip中的成员共用一块内存,则c1=9D,c2=30,c3=81,c4=A
	//十进制表示为10 129 48 157
	my_ip.addr = 176238749; 
	printf("%d.%d.%d.%d\n", my_ip.ip.c4, my_ip.ip.c3, my_ip.ip.c2, my_ip.ip.c1);//10.129.48.157
}
   

最后,还是那句,欢迎大家在评论区进行提问,提建议给我,谢谢

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值