位段,结构体+枚举+联合详解

位段

什么是位段

在C语言中有些数据在存储时并不需要占用一个完整的字节,只需要占用一个或几个二进制位即可,基于这种特殊的存储,C语言又提供了一种叫做位域的数据结构。
在结构体定义时,我们可以指定某个成员变量所占用的二进制位数(Bit),这就是位域

位段的声明

位段的声明和结构类似但是有两个不同
e.g.
1.位段的成员必须是 int、unsigned int 或signed int 。
2.位段的成员名后边有一个冒号和一个数字

struct bw{
    unsigned m;
    unsigned n: 4;
    unsigned char ch: 6;
}
struct A
{
	int _a : 2;
	int _b : 5;
	int _c : 10;
	int _d : 30;
};

关于位段的内存分配问题

学会了如何声明之后相信有很多小伙伴是不是在思考,这个东东到底在内存中占多少空间呢??这里我们不妨来讨论一下:

printf("%d\n", sizeof(struct A));

通过我们的编译器(vs2019)能得到如下得结果
在这里插入图片描述
到这里相信很多小伙伴就以及开始犯糊涂了,这个结构体按照内存对齐原则根据偏移量来计算的话(4+4+4+4)应该为16,为什么结果却为8呢?下面我们来了解一下位段的存储规则
位域的具体存储规则如下:

  1. 当相邻成员的类型相同时,如果它们的位宽之和小于类型的 sizeof 大小,那么后面的成员紧邻前一个成员存储,直到不能容纳为止;如果它们的位宽之和大于类型的 sizeof 大小,那么后面的成员将从新的存储单元开始,其偏移量为类型大小的整数倍。
    通俗版:将上述规则简单通俗的说出来其实就是,在vs2019中位段冒号后面的数字决定这个变量类型所开辟出来的空间,变量将要占有的比特位,例如上面的 bw,n 的类型是 unsigned int,长度为 4 个字节,共计 32 位,那么 n 后面的数字就不能超过 32;ch 的类型是 unsigned char,长度为 1 个字节,共计 8 位,那么 ch 后面的数字就不能超过 8(图中所占空间为6bit)。

到这里就不难解释为什么是8而不是16了
struct A中有4个int 类型的变量,第一个int开辟出4个字节也就是32个bit,但是_a只占2个bit位,故_b占的5个bit也能够放入,_c所占10个bit也能够放入,到_c这一共放了17个bit,还剩下15个bit位,但是_d为30bit,显然放不进,故int _d向内存第二次申请4个字节的空间将30个bit全部放入,故最后的答案为8个字节!

结构体

——结构体,它就将不同类型的数据存放在一起,作为一个整体进行处理。

结构体的基础知识介绍

例如对于学生信息登记表,姓名为字符串,学号为整数,年龄为整数,所在的学习小组为字符,成绩为小数,因为数据类型不同,显然不能用一个数组来存放。
在C语言中,可以使用结构体(Struct)来存放一组不同类型的数据。结构体是一种集合,它里面包含了多个变量或数组,它们的类型可以相同,也可以不同,每个这样的变量或数组都称为结构体的成员(Member)例如:

struct Stu
{
char name[20];//名字
int age;//年龄
char sex[5];//性别
char id[20];//学号
}//分号不能丢

接下来给大家介绍一下如何定义变量

#include <stdio.h>
int main() {
    struct {
        char* name;  //姓名
        int num;  //学号
        int age;  //年龄
        
    } stu1;
    //给结构体成员赋值
    stu1.name = "zhoumin";
    stu1.num = 1;
    stu1.age = 18;
    return 0;
}

枚举

在这里给大家介绍一下一个比较好玩的东西(枚举)enum

枚举类型的定义

他的主要系统结构和结构体非常类似,枚举数据表的值都是整数。第一个枚举成员的默认值为整型的0,后续枚举成员的值在前一个成员上加1。也可以人为设定枚举成员的值,从而自定义某个范围内的整数。

enum Day//星期
{
Mon,//打印为0
Tues,//1
Wed,//2
Thur,//3
Fri,//4
Sat,//5
Sun//6
};

以上定义的 enum Day就是一个是枚举类型,{}中的内容是枚举类型的可能取值,也叫 枚举常量

枚举的优点

这时候就有小伙伴要说了,我们可以使用 #define 定义常量,为什么非要使用枚举?
给大家介绍一下枚举的优点:

  1. 增加代码的可读性和可维护性
  2. 和#define定义的标识符比较枚举有类型检查,更加严谨。
  3. 防止了命名污染(封装)
  4. 便于调试
  5. 使用方便,一次可以定义多个常量

枚举的使用

使用枚举可以一次定义多个变量,如果使用#define的方式要从Mon开始一个一个去定义七遍,显得是不是有点太浪费时间了?下面运用了枚举来处理这事情瞬间就变得非常简单了,一次性全部定义了,是不是很香呢?

#include<stdio.h>
 
enum week {Mon=1,Tue,Wed,Thu,Fri,Sat,Sun};
 
int main()
{
    printf("%d",Mon);
    return 0;
}

联合

联合类型的定义

联合也是一种特殊的自定义类型
结构体和共用体的区别在于:结构体的各个成员会占用不同的内存,互相之间没有影响;而共用体的所有成员占用同一段内存,修改一个成员会影响其余所有成员。
共用体也是一种自定义类型,可以通过它来创建变量,例如:

union data{
    int n;
    char ch;
    double f;
};
union data a, b, c;

联合的特点

联合的成员是共用同一块内存空间的,修改一个成员会影响其余所有成员,这样一个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)。

# include<stdio.h>
int main() {

	union Un

	{
		int i;
		char c;
	};
	union Un un;
	// 下面输出的结果是一样的吗?
	printf("%d\n", &(un.i));
	printf("%d\n", &(un.c));
	//下面输出的结果是什么?
	un.i = 0x11223344;
	un.c = 0x55;
	printf("%x\n", un.i);
	return 0;

}

在这里插入图片描述

联合大小的计算

1.联合体union特性
联合体所有成员变量共享内存,相对于联合体首地址偏移量都为0
同一时间只能存储1个被选择的变量,对其他成员变量赋值会覆盖原变量
2.联合体大小计算准则
联合体大小要至少能容纳最大的成员变量
联合体大小要是所有成员变量类型大小的整数倍

#include<stdio.h>
int main() {
	union Un1
	{
		char c[5];
		int i;
	};
	union Un2
	{
		short c[7];
		int i;
	};
	//下面输出的结果是什么?
	printf("%d\n", sizeof(union Un1));
	printf("%d\n", sizeof(union Un2));
	return 0;

}

在清楚了上述的规则之后,我们来尝试运用一下,在union un1中我们可知至少要有51的空间,空间又必须为4的倍数,故sizeof(union un1)=8;再来看看union un2 中我们知道至少要有27的空间,空间又必须为4的倍数,故sizeof(union un2)=16;

在这里插入图片描述

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值