详解自定义类型:结构体、枚举、联合体


前言:C语言中声明了很多的数据类型,比如:char、int、float等等几乎涵盖了所有的单一类型。但是实际运用中不难发现,我们描述一件事物的时候通常会使用多种类型的数据。比如描述一本书:我们要知道书名,页码,和价格,这里就使用了字符型、整型和浮点型数据。但是在C语言中并没有一种类型可以直接储存这三种类型的数据,因此就引入了自定义类型,由使用者自己定义想使用的类型。

一、结构体

结构体类型是不同类型数据的集合,这些数据被称做成员变量

  1. 结构体类型的声明

结构体关键字:struct

struct tag//结构体标签
{
	member-list;//成员列表
}variable-list;//变量列表

结构体的特殊声明

结构体在声明的时候可以进行不完全声明:

struct //缺少结构体标签
{
	name[20];
}s1;

声明缺少结构体标签的结构体,被称做匿名结构体。因为缺少结构体标签,导致我们无法通过上述的结构体类型在创建新的变量。使用场景:同种类型的变量,我们只需要使用一个的情况下会使用匿名结构体,保证类型的唯一性。

结构体的自引用

创建一个结构体类型,将该结构体类型作为自身的结构体成员是否可行?

struct s1
{
	int a;
	struct s1 ss;
}

很显然,这种情况是不可以的。我们知道程序的运行是线性的,当程序运行到struct s1 ss这行语句时,需要知道struct s1的类型,但是struct s1类型里面包含了struct s1这个结构体类型,无限循环下去,会引起程序崩溃。
那我们怎么做才能实现结构体的自引用呢?

struct s1
{
int a;
struct s1* pss;
};

答案是使用指针,创建一个结构体指针类型。

结构体变量的定义和初始化

struct S1
{
	int i;
	char c;
	double d;
}s1;//定义结构体类型的同时,创建一个变量s1
struct S1 s2;//创建结构体变量s2
struct S1 s3 = { 4, 'a', 3.14 };//创建结构体变量s3的同时对s3进行初始化
struct S2
{
char name[20];
int age;
struct S2* ps2;//结构体自引用
}s4 = {"zhangsan", 10, NULL};//初始化结构体类型的同时创建结构体变量s4并对其初始化

结构体内存对齐

计算结构体的大小,需要用到内存对齐:

1.将结构体中的第一个成员变量放在偏移量为0地址处
2.从第二个成员变量开始,将成员大小和成员变量的对齐数进行比较,放置在偏移量为较小数的倍数的地址上。(每个编译器都默认包含一个对齐数,vs默认对齐数是8。成员变量的对齐数:是成员变量和编译器默认对齐数相比较的较小值)
3.结构体整体的大小,要是结构体成员变量中最大的成员变量的对齐数的倍数
4.结构体嵌套结构体,被嵌套的结构体的大小,要是结构体成员的对齐数的倍数。其中嵌套的结构体成员变量的对齐数,是嵌套结构体中成员变量的最大对齐数。

因此,在创建结构体变量的时候,尽量先创建较小的结构体成员,在依次创建较大的结构体成员。避免结构体内部空间的浪费

编译器中的默认对齐数也是可以修改的:

#pragma pack(int)//int表示想要修改的对齐数
#pragma pack()//空表示恢复默认对齐数

结构体的传参

形参是实参的临时拷贝,为了避免有些结构体中的数据过于庞大,尽量选择传递结构体的指针

结构体实现位段

位段必须满足两个条件:
1、结构体成员类型必须是整型类型
2、结构体成员变量名后面必须要有 :冒号数字

struct S
{
	char c:3;
	int i:3;
};

特点:1.位段可以操作内存中的二进制位,成员名后面的数字表示他能操作的二进制位数。2.位段的出现就是为了节省空间,计算位段大小是不需要内存对齐的,但是位段开辟空间的大小是根据成员变量类型确定的,char开辟一个字节,in开辟4个字节。3、C语言中并没有规定位段开辟空间的细节,由编译器自己决定。跨平台使用涉及很多不确定因素,注重可移植的程序应该避免使用位段。

二、枚举

描述事物的时候将事物的类型一一列举出来。例如描述性别时:男性、女性。

枚举类型的声明

枚举关键字:enum

enum SEX
{
	male,
	unmale = 200
};

枚举的成员变量是有值的,第一个成员变量的默认值为0,其他的值依次递增1。当然我们也可以在定义枚举类型的时候,将成员变量进行初始化,但不能对已经定义的值进行修改。因此枚举也被称为枚举常量

枚举类型的优点

我们知道,可以使用#define定义常量,那么为什么还要使用枚举呢?正是因为枚举有define所不具有的一些优点
1.增加代码的可读性和可维护性
2. 和#define定义的标识符比较枚举有类型检查,更加严谨。
3. 防止了命名污染(封装)
4. 便于调试
5. 使用方便,一次可以定义多个常量

三、联合体

联合体是一种特殊的自定义类型,所有的成员变量公用一个块空间,因此也被称为共用体

联合体的定义

联合体关键字:union

union Un
{
	char c;
	int i;
};

联合体的特点

1.联合体共用一块空间,因此联合体的大小最小也是成员变量中最大的大小。
2.由于联合体共用一块空间,因此修改其中一个成员变量的值,另一个成员变量的值也会发生改变。这也是联合体的使用场景

联合体大小的计算

联合体的大小也存在内存对齐,当联合体的大小不是成员变量的对齐数的整数倍时,要对齐到整数倍。
例如:

union Un1
{
 char c[5];
 int i;
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ljiyu0506

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值