一、结构体
1.什么是结构体,以及为什么会出现结构体?
假设要统计一个班上同学的身高和姓名 , 使用两个数组一个存放同学姓名,另一个存放同学身高,两个数组的类型分别为char 和 float类型 , 每个同学对应自己的身高 ,对身高进行排序,然后在一个个核对每个同学的身高,这样就会变得很麻烦,如果数据不止这两种,有体重、血型、臂展等等 , 这样数组与数组之间的下标就不再具有关联性。于是C语言就定义了一个不同类型数据的集合的数据结构 ————结构体。
准确来说,结构体(struct)指的是一种数据结构,是C语言中聚合数据类型(aggregate data type)的一类。结构体可以被声明为变量、指针或数组等,用以实现较复杂的数据结构。结构体同时也是一些元素的集合,这些元素称为结构体的成员(member),且这些成员可以为不同的类型,成员一般用名字访问。
2.如何定义一个结构体?
要想使用一个自定义结构体,首先要声明结构体类型,其次在创建结构体变量,之后才能使用此结构体。如下所示(结构体类型的声明):
struct StuInfo{ //结构体的声明 char name[20];//学生姓名 int age;//年龄 char sex[5];//性别 float height;//身高 };
创建结构体变量:
#include<stdio.h> #include<string.h> #include<stdlib.h> #include<assert.h> struct StuInfo{ char name[20];//学生姓名 int age;//年龄 char sex[5];//性别 float height;//身高 }s1,s2; //s1,s2为StuInfo的结构体变量 int main() { struct StuInfo s3 , s4; //创建学生信息的结构体变量 return 0; }
由此可见,定义结构体需要先声明后使用,且结构体分为结构体成员,结构体参数列表,其中结构体成员存放自定义数据类型 , 参数列表创建结构体变量 ,在通过结构体变量对结构体成员进行访问,结构体变量有两种定义方式,一种是在结构体声明后面直接创建变量,如果结构体在外部声明,需要此种方式创建的结构体变量为全局变量,在函数内部创建结构体变量需要 : 结构体类型 + 变量;的方式来创建变量。
此外,还有一种特殊的结构体类型———匿名结构体,顾名思义匿名结构体就是没有给结构体赋予名字,那么若想创建匿名结构体变量只能在结构体声明后才能创建,在函数内部不可创建,当然也不推荐大家用匿名结构体类型,毕竟能使用匿名结构体类型的场景很少,且能用其他方法代替。
#include<stdio.h> struct//匿名结构体类型 { char name[10]; int age; }s1,s2; //只能在结构体声明后创建变量 int main() { s1.name = "xiaoming";//访问结构体变量 s1.age = 10;//访问结构体变量 return 0; }
3.结构体的访问
我们知道了如何声明结构体类型,创建结构体变量 ,那么说到头我们该如何访问结构体成员呢?其实访问结构体成员非常简单使用'.'运算符来对结构体成员进行访问,可以对结构体成员赋值,打印等, 假设有个男同学叫小明,今年18,身高1.83m。
代码如下:
#include<stdio.h> #include<string.h> #include<stdlib.h> #include<assert.h> struct StuInfo { char name[20];//学生姓名 int age;//年龄 char sex[5];//性别 float height;//身高 }; //s1,s2为StuInfo的结构体变量 int main() { struct StuInfo s1; //创建学生信息的结构体变量 char sex[5] = "男"; strcpy(s1.name, "xiaoming");//将姓名拷贝到数组中 s1.age = 18; strcpy(s1.sex, "男"); s1.height = 1.83; printf("姓名:%s 年龄:%d 性别:%s 身高:%lf" , s1.name, s1.age, s1.sex, s1.height); return 0; }
执行结果如下:
以上就是结构体成员点运算符访问, 其实除了点运算符访问外还有一种特殊的访问方式,这种访问方式是基于结构体指针来的,因为使用(*)解引用运算符有些冗余 ,可能会造成认知上的错误,C语言定义了结构体指针的特殊成员访问方式'->'访问,访问方式为: 结构体指针变量 + '->' +结构体成员 的方式来访问的,如果你是学过数据结构的小伙伴,那么你一定不会陌生,没错就是链表,链表的就是以结构体指针来实现的,没学过的也不要紧 ,并不是很难理解。
#include<stdio.h> struct Node{ int data; struct Node *next;//结构体指针 }; int main() { struct Node *p = (Node *)malloc(sizeof(Node));//开辟节点 p -> data = 1; //结构体成员赋值 p -> next = NULL; printf("%d",p -> data);//结构体成员访问 return 0; }
如上述代码演示,结构体指针的访问是利用'->'访问的,是不是觉得很方便呢,其实在以后我们学习数据结构的时候,会大量用到结构体指针来进行实践,或许现在你还是比较陌生,但总会相遇。
4.typedef 与结构体
struct + 结构体名称这种写法有时候会不会太过冗长了呢,C语言提供了一个很好的解决这种冗长代码的关键字 ,typedef 关键字可以将类型进行重命名 ,类如typedef int size_t,那么size_t就与int是同样功能,同理typedef出现在结构体前面,对结构体进行重命名,可以避免冗长的代码给人更好的阅读体验 。
#include<stdio.h> #include<string.h> #include<stdlib.h> #include<assert.h> typedef struct StuInfo { char name[20];//学生姓名 int age;//年龄 char sex[5];//性别 float height;//身高 }Stu;//对结构体类型进行重定义 int main() { Stu p;//用重命名的结构体创建结构体变量 strcpy(p.name, "xiaoming"); p.age = 18; strcpy(p.sex, "男"); p.height = 1.83; printf("姓名:%s 年龄:%d 性别:%s 身高:%lf" , p.name, p.age, p.sex, p.height); return 0; }
可以看得出来typedef 关键字搭配结构体是一种减少代码冗长的好手段,在这里需要注意的是typedef关键字要写在结构体声明之前,重命名要写在大括号之后分号前,之后在使用重命名+结构体变量是不是方便许多了呢?
二、联合体(共用体)
1.什么是联合体,以及如何定义联合体?
在进行某些算法的C语言编程的时候,需要使几种不同类型的变量存放到同一段内存单元中。也就是使用覆盖技术,几个变量互相覆盖。这种几个不同的变量共同占用一段内存的结构,在C语言中,被称作“共用体”类型结构,简称共用体,也叫联合体。
—— 摘自百度百科
准确来说,联合体是一种特殊的数据类型,允许在相同的内存位置存储不同的数据类型。程序中可以定义带有多个成员的联合体,但是任何时候只能有一个成员带有值。共用体提供了一种使用相同的内存位置的有效方式。用到的关键字是 union。
2.联合的声明
联合体的声明使用union关键字来声明联合体,联合体的声明与结构体类似:
成员表中含有若干成员,成员的一般形式为: 类型说明符 成员名
。其占用的字节数与成员中最大数据类型占用的字节数。
与结构体(struct)
、枚举(enum)
一样,联合体也是一种构造类型。
联合的声明访问跟结构体类似,访问跟结构体也相似都是用点运算符来进行对联合体成员的访问。
union 联合体名称{ 参数列表 };
3.联合的特点
要想了解一个数据结构最好的方法当然是观察它的内存,我们不妨设计一个联合体打印出联合各个成员的地址来观察:
#include<stdio.h> union S{//联合声明 char ch[6]; int n; }; int main() { union S s;//创建联合体变量 printf("%d",sizeof(s));//打印联合体大小 printf("s的地址: %p\n",&s);//打印联合体地址 printf("ch的地址: %p\n",s.ch);//打印联合体中ch成员地址 printf("n的地址: %p\n",&s.n);//打印联合体中n成员地址 return 0; }
从所设计的共用体来看,成员ch与成员n是不同类型的成员,但是他们的地址以及共用体的地址却是如出一辙,这是为什么呢?其实,我上面已经说了,联合体的成员是指向同一片内存空间的,与结构体不同,结构体是每个成员有自己的单独开辟的空间,所以,联合体在使用的时候尽量不要多个成员一起使用,除非使用的成员占用内存大小要小于等于最大成员所占内存空间大小。
由sizeof(s)可以看出来这个联合体占用八个字节,我们来分析这个联合体,由一个字符数组与整型变量,其中字符数组大小为6个字节,又联合体的默认对齐数为4个字节,所以在成员列表中最大成员占用6个字节,又成员变量占用字节数必须是默认对齐数的整数倍,所以只能是8个字节,浪费了两个字节。(如果这部分不懂可以看我接下来的文章,也包括结构体共用体大小的计算,都在持续更新中!)
三、枚举
顾名思义,枚举的意思就是列举,类如对一周七天的天气列举,对一个系统的不同模式进行列举就是枚举。
1.枚举类型的定义
枚举类型的定义非常简单,跟结构体非常相似,使用关键字enum来对枚举进行声明,大括号内的内容为被逗号隔开的枚举的可能取值,这样就完成了枚举类型的声明。
enum Week//枚举类型的声明 { //括号内皆为枚举的可能取值 Mon, Tue, Wed, Thu, Fri, Sat, Sun };
那么枚举有什么用呢?别急,让我们看一下下面的代码 :
#include<stdio.h> enum Week { Mon, Tue, Wed, Thu, Fri, Sat, Sun }; int main() { printf("%d ",Mon); printf("%d ",Tue); printf("%d ",Wed); printf("%d ",Thu); printf("%d ",Fri); printf("%d ",Sat); printf("%d ",Sun); return 0; }
将枚举的类型给打印出来会发生什么呢?
我们不难发现,打印出来的值是从0到6,其实枚举的作用就是对所枚举的内容顺序常量化,默认第一个常数为0,往后依次递增,当然也可以对常量进行赋值,我们来看看有什么结果
#include<stdio.h> enum Week { Mon = 3,//对首常量赋值为3 Tue, Wed, Thu, Fri, Sat, Sun }; int main() { printf("%d ",Mon); printf("%d ",Tue); printf("%d ",Wed); printf("%d ",Thu); printf("%d ",Fri); printf("%d ",Sat); printf("%d ",Sun); return 0; }
可以发现当把首常量赋值为其他值,后面的常量也会依照你所赋的值依次递增,除非你在后面的常量继续赋值,总的来说,枚举常量可以在范围内任意赋值。
2.枚举的优点与使用
你有没有思考过这样一个问题:明明可以直接用#define来定义常量,但为什么还非要用枚举类型呢?总的来说有四个优点:
1.增加代码的可读性和可维护性
2.与#define定义的标识符进行比较,枚举具有类型检查,更加严谨
3.方便调试
4.方便使用,可以一次性定义多个常量
你刚刚懂了枚举的概念与优点,你是不是有个很大的疑问,枚举到底有什么用,应用场景是什么啊,虽然会了但感觉没什么用啊?等等,其实啊枚举的应用非常的广泛,我们在用C语言写的中型大型项目里面常常会用到枚举类型,比如在写植物大战僵尸的游戏,现在要求你把植物卡槽写满,你该怎么办,难道用1代表这个植物2代表那个植物?这样以来代码的可读性就变得非常的差了,然而枚举在这用场景下得到了非常好的应用,将所需要的植物用大写英文表示,写在枚举类型里,这样一来使用起来不就简单清晰明了了吗。
以上就是全部内容啦,如果觉得有帮助的话,还请大佬动动手指点点关注点点赞啦,更多作品还在持续更新中~~