自定义类型

本文详细介绍了C语言中的自定义类型,包括结构体、联合体和枚举,重点讨论了结构体的创建、初始化、内存对齐规则,以及联合体的共享内存特性。同时,阐述了枚举类型的优势和使用场景。
摘要由CSDN通过智能技术生成

我们知道,C语言中有一些内置类型比如:char(字符)类型、int(整型)类型、float(单精度浮点型)类型和double(双精度浮点型)。但是其实在C语言中还存在着一种数据类型叫做自定义类型,自定义类型包括:struct(结构体)类型、union(联合体)类型和enum(枚举)类型,这篇博客其实就是介绍C语言中的自定义类型。
由于自定义类型涉及的内容有点多且偏,在这里我们只介绍自定义类型中比较重要的知识。

结构体类型

结构体类型其实和我们之前学的C语言中的内置类型是一样的,都是表示一个变量的类型。

结构体类型的创建和初始化

结构体类型的创建

结构体是一些值的集合,这些值都叫做成员变量。结构中的每一个成员都可以是不同的变量。以下是一个简单的结构体类型的实现:

//这里是定义一个名字为学生的结构体
struct Student//这个student是这个结构体类型的名字,可以自己指定
{
	//这里是结构体中的成员
	char name[20];//学生的名字
	int age;   //学生的年龄
	float grade; //学生的成绩
} s4, s5;//这里的s4、s5是全局变量

//以下是一个简单的结构体变量的创建
int main()
{
	struct Student s1, s2, s3;//这里的s1、s2、s3是局部变量
	return 0;
}

以上就是一个简单的结构体类型的创建。
当然,在写代码的过程中我们可能还会见到一种这样创建的结构体类型:

struct 
{
	char name[20];
	int age;
	float grade;
} A ;

我们发现,这个结构体类型在创建的过程中没有名字,我们把这种没有名字的结构体类型叫做匿名结构体类型,这种结构体类型由于是没有名字的,所以在我们写程序的过程中它只能出现一次。

结构体类型的初始化

结构体类型和我们之前学习的C语言的内置类型一样,都是可以进行初始化操作的。以下就是C语言中结构体初始化的示例:

//这里是定义一个名字为学生的结构体
struct Student
{
	char name[20];
	int age;   
	float grade;
};

int main()
{
	// 这里是我们按照我们定义的结构体类型进行的一个按顺序的初始化
	struct Student s1 = {"zhangsan", 18, 97.5};
	//下面这种初始化方式是我们不按照结构体类型的顺序进行初始化
	struct Student s2 = {.age = 18, .name = "zhangsan", .grade = 97.5};
	return 0;
}

结构体的内存对齐

我们通过上面的文章可以知道,在结构体中,我们可以存在多个变量,而这些变量都是需要占用一定的空间大小的,所以我们的结构体也会占用一块空间。那么我们应该如何计算结构体的空间大小呢?请看以下代码示例:

struct Student
{
	char name;
	int age;
};

有些人可能会觉得这个太简单了,这个结构体的空间大小不就是5个字节吗?char类型占一个字节,int类型占四个字节。但是实际上,这个结构体的空间大小是8个字节,这里就是我们的内存对齐所造成的结果。以下是验证结果:
在这里插入图片描述

有些人可能会觉得奇怪,内存对齐明明会导致占用的内存变多,为什么还要存在内存对齐呢?接下来博主就来为大家解答一下内存对齐的优点是什么。

内存对齐的优点:在cpu读取我们这个结构体里面的变量的时候一次会读取4个字节,但是我们的char类型只有一个字节,也就是说在cpu读取我们的name变量的时候会再读取age变量的前三个字节。这就会导致我们的cpu还得继续做一个处理,就是把刚刚读取的三个字节还给age变量,再重新读取。这个时候就会浪费一定的时间去读取数据。但是如果我们让char类型的数据占用四个字节呢?那么cpu只要读取两次就可以取出所有数据并且不需要做任何处理,这就是内存对齐的优点。

内存对齐的规则

以上我们介绍了内存对齐的优点,现在我们来介绍内存对齐的规则是什么。以下就是内存对齐的规则:

  1. 第一个成员在与结构体偏移量为0的地址处。
  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
    注意:对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
  3. 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。
  4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

内存对齐会导致空间的浪费,那么为了尽可能减少空间的消耗我们有没有些应对措施呢?当然有,结构体的位段就是其中一个应对措施,但是由于结构体的位段在结构体中并不属于比较重要的内容,所以在此博主便不再赘述。好奇的小伙伴可以去搜索位段的相关知识。回到正题,除了结构体的位段,我们还有没有节省内存对齐浪费的空间的办法呢?结果是肯定的。请看以下代码:
在这里插入图片描述
有些人可能会觉得这两个结构体占用的空间大小不是一样的吗?这不会节省空间啊。但是其实你如果运用了上面内存对齐的规则的话会发现,其实第一个结构体占用的空间大小是12,第二个结构体占用的空间大小是8。以下是代码运行后的结果:
在这里插入图片描述
由此我们可以得出:在结构体变量中创建的成员尽量把占用内存空间小的变量放前面,这样子可以尽可能地减少内存空间的消耗。以上就是结构体相关的介绍。

联合体类型

联合体类型其实也是一种自定义类型,不过它和结构体不同的是:联合体里面的成员变量使用的都是同一块空间,也就是至少是最大的成员变量的空间。
以下是代码示例:
在这里插入图片描述
下面是验证的方法:
在这里插入图片描述
我们发现不管是name还是age这两个成员变量指向的都是同一个地址,也就是联合体u的地址。所以联合体中的成员变量的地址是同一块内存空间,只是随着成员变量所能访问的内存空间的大小的不同来进行区分。
和结构体一样,我们的联合体也有空间大小,计算联合体的空间大小其实和计算结构体的空间大小非常相似。所以在此不再赘述。以上就是关于联合体类型的介绍。

枚举类型

枚举类型,顾名思义就是把可能的取值列举出来,请看以下代码示例:

enum Sex
{
	//枚举常量
	MALE,
	FEMALE,
	SECRET
};

int main()
{
	printf("%d %d %d\n", MALE, FEMALE, SECRET);
	return 0;
}

运行示例如下:
在这里插入图片描述
从上面这段代码我们可以看出如果我们没有给枚举常量赋值,编译器给枚举常量赋值会自动赋成0/1/2/3···

枚举类型的优点

有些人可能会问我们可以用#define来定义常量,为什么我们一定要用枚举类型来定义常量呢?以下是枚举常量的一点优点:

  1. 枚举类型可以增加代码的可读性和可维护性,因为我们把我们需要定义的常量给它放到一块去了,简单易懂。
  2. 和#define定义的标识符比较,枚举有类型的检查,更加严谨。
  3. 便于调试 ,预处理阶段会删除#define定义的符号。
  4. 使用方便,一次可以定义多个常量。
  5. 枚举常量是遵循作用域和规则的,枚举声明在函数内,只能在函数内使用。

以上就是对于自定义类型的枚举类型的介绍。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值