【C语言进阶(9)】自定义数据类型2:位段 + 枚举 + 联合

文章目录

  • 位段
    • 位段的定义
    • 位段的内存分配
    • 分析位段的大小
    • 位段的跨平台问题
  • 枚举
    • 枚举的定义
    • 枚举的默认值与赋值
    • 枚举的使用
    • 枚举的优势
  • 联合(共用体)
    • 联合体的声明
    • 联合体的特点
    • 联合体判断字节序
    • 计算联合体大小

位段

位段的定义

位段与结构体

  • 位段其实使用结构体来实现的。

  • 位段与结构体的语法格式类似,但位段与结构体有3个不同

    1. 位段的成员必须是整型: int、unsigned int、signed int 或 char 类型。
    2. 位段的成员名后有一个冒号和一个数字。
    3. 位段的成员类型必须相同,有一个int就全是Int,有一个char就全是cha

位段的功能

  • 在一定程度上节省不必要的空间。

位段的语法格式

  • 位段的(位)表示的是比特位。
  • 冒号后面的数字表示该成员变量占多少个比特位。
  • 位段的第一个成员类型直接决定整个位段的成员类型。
struct A
{
	int a : 2;	//a 占 2 个比特位
	int b : 5;	//b 占 5 个比特位
	int c : 10;	//c 占 10个比特位
	int d : 30;	//d 占 30个比特位
};

在这里插入图片描述

  • 为何所有的位段成员总共只占用了 47 个比特位,算起来应该 6 个字节就够了,然而位段类型 A 的大小却是 8 个字节?

位段的内存分配

  • 有些值的取值范围返常有效,实际上用不到 1 个字节那么多,就可以使用位段限制这些值的取值范围来节省内存。
    • 比如:int flag,flag 是用来判断真假的,取值范围只有 0 和 1 两种数据,只需要 1 个比特的空间就够了,给他 1 个字节太浪费了。

位段的内存开辟规则

  1. 位段的成员必须是整型: int、unsigned int、signed int 或 char 类型。
  2. 位段的空间上是按照需要以 4 个字节( int )或者 1 个字节( char )的方式来开辟的。
    • 每次固定开辟 4 或 1 字节往里面放成员,空间不够时再开辟下一块 4 或 1 字节的空间。
  3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。

分析位段的大小

  • 分析下列位段所占空间为何是这样。
struct A
{
	int a :  2;	//2  Bit
	int b :  5;	//5  Bit
	int c : 10;	//10 Bit
	int d : 30;	//30 Bit
};

struct B
{
	char a : 3;	//3 Bit
	char b : 4;	//4 Bit
	char c : 5;	//5 Bit
	char d : 4;	//4 Bit
};

在这里插入图片描述

1. 分析 struct A 的大小为何是 8 字节

【C语言进阶(9)】自定义数据类型2:位段 + 枚举 + 联合_第1张图片

2. 分析 struct B 的大小为何是 3 字节

【C语言进阶(9)】自定义数据类型2:位段 + 枚举 + 联合_第2张图片

位段的跨平台问题

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

总结

  • 如果编写的代码 “ 肯定 ” 不会拿到其他平台,代码还是少用位段为好。

枚举

枚举的定义

  • 列举出所有可能的取值称之为枚举。例如:
    • 一周 7 天,可以一一列举出来。
    • 一年 12 月,可以一一列举出来。
  • 枚举类型属于常量,无法被修改

枚举的语法格式

enum 枚举类型名 {枚举值名称1,枚举值名称2, 枚举值名称3, ... 枚举值名称n};

声明枚举类型例子

  • 将一周的可能取值都列举出来。
enum Day//星期
{
	Mon,
	Tues,
	Wed,
	Thur,
	Fri,
	Sat,
	Sun
};
enum Color//颜色
{
 	RED,
 	GREEN,
 	BLUE
};

枚举的默认值与赋值

1. 枚举的默认值

  • 枚举常量为 int 类型的常量。当不对枚举常量赋初始值的时候,枚举常量的值默认是从 0 开始依次往下递增。
enum Color
{
 	RED,	//0:第一个枚举常值默认为 0
 	GREEN,	//1:从前一个常量值往后递增
 	BLUE	//2
};

【C语言进阶(9)】自定义数据类型2:位段 + 枚举 + 联合_第3张图片

2 给枚举常量赋初值

  • 枚举常量也可以被赋予初始值。当前面的枚举常量被赋初值,而后面的没有时,后面的枚举常来值依然为前面的递增 1。
enum Color
{
	RED = 520,	
	GREEN,	
	BLUE = 	100
};

【C语言进阶(9)】自定义数据类型2:位段 + 枚举 + 联合_第4张图片

枚举的使用

根据枚举类型定义枚举变量

  • 使用枚举类型定义的枚举变量只能对枚举变量赋枚举常量。
enum Color
{
	RED = 520,	
	GREEN,	
	BLUE = 	100
};

int main()
{
	//c 只能被赋予 RED、GREEN、BLUE 这三个值 
	enum Color c = RED;//√
	enum Color q = 30; //×
}

使用枚举常量

  • 当需要定义很多同类型的常量的时候,不需要写一大堆的 #define。
  • 假设要定义一堆蔬菜的价格,用 #define 定义就显得很乱。要写一堆的 #define 并且无法一眼看出它们属于同类。
#define potato 		5
#define tomato 		3
#define cucumber 	2
#define onion 		1
......
  • 但是如果放到枚举里面,首先就可以知道这些属于同一属性的数据,其次根据枚举类型名也能直接分别这些属性是什么。
enum price 		//可以判断出列举出的都是价格
{
	potato = 5, //只能赋予整型
	tomato = 3,
	cucumber = 2,
	onion = 1
};

【C语言进阶(9)】自定义数据类型2:位段 + 枚举 + 联合_第5张图片

枚举的优势

相较于 #define 而言 enum 的优势

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

联合(共用体)

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

联合体的声明

  • 声明联合(共用)体的语法格式与结构体一致。
union 联合体名称
{
 	联合体成员1;
 	联合体成员2;
 	联合体成员3;
};
  • 只需要将 struct 换成 union,结构体就变成了共用体。
  • 虽然结构相似,但是共用体的所有成员拥有同一个内存地址。

联合体的特点

  1. 联合体的成员共用同一块内存空间。
  2. 联合体变量的大小,至少是最大成员的大小
    • 例如:联合体内有 int 成员以及 char 成员,联合体的大小就至少得是 4 字节。
  3. 联合体内的成员一次只能使用一个,不能同时使用。
    • 在前面已经对某联合体成员赋值时,继续对其他成员赋值,后面的值会覆盖前面的值。
union Un
{
	char a;
	int  b;
};

1. 联合体共用同一个地址

【C语言进阶(9)】自定义数据类型2:位段 + 枚举 + 联合_第6张图片
【C语言进阶(9)】自定义数据类型2:位段 + 枚举 + 联合_第7张图片

2. 共用体的大小至少是最大成员大小

在这里插入图片描述

3. 对联和体赋值会导致互相覆盖

  • 联合体就像一个 “ 人格分裂者 ” 一样,同一时间这具身体只会出现一个人格,后出现的人格会将前面的人格覆盖。
union Un
{
    int  a;
    char b;
};

int main()
{
    union Un u;

    u.a = 5;
    u.b = 'a';//将 5 覆盖

    printf("u.a = %c\n", u.a);
    printf("u.b = %d\n", u.b);

    return 0;
}

在这里插入图片描述

联合体判断字节序

解题思路

  • 在联合体内放置一个 char 成员,以及一个 int 成员。
  • 因为 char 占据 int 的第一个字节。如果是小端存储,char 存着的就应该是 01,反之为 00。

【C语言进阶(9)】自定义数据类型2:位段 + 枚举 + 联合_第8张图片

int check_sys()
{
    union Un
    {
        char a;
        int  b;
    }u;
    u.b = 1;
    return u.a;
}

int main()
{
    if (check_sys())    //返回 1 为小端
    {
        printf("小端\n");
    }
    else                //返回 0 为大端
    {
        printf("大端\n");
    }

    return 0;
}

计算联合体大小

  • 联合体在计算大小的时候也存在对齐

联合体内存对齐规则

  • 联合体的大小至少是最大成员的大小。
  • 当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。

【C语言进阶(9)】自定义数据类型2:位段 + 枚举 + 联合_第9张图片

分析 Un 的大小

【C语言进阶(9)】自定义数据类型2:位段 + 枚举 + 联合_第10张图片

你可能感兴趣的:(#,C语言进阶篇,c语言,数据库,开发语言)