逐步分析C语言实现通讯录(内有结构体基础讲解和qsort基本使用方法讲解)

文章介绍了如何使用C语言实现静态通讯录,涉及结构体、枚举类型、qsort排序函数的应用。通过详细步骤,从声明结构体、定义枚举、数据存储、添加和显示功能,到排序和删除操作,逐步构建静态通讯录项目。此外,文章强调了结构体指针在函数传参中的作用,以及枚举提高代码可读性的优点。
摘要由CSDN通过智能技术生成

目录

0.前言:

1.静态通讯录需要用到的知识点:

1.1 结构体:

①什么是结构体:

 ②结构体的声明

③结构体重命名:

④结构体的访问

1.2枚举类型:

①什么是枚举?

②枚举类型的定义

③枚举类型的优点

1.3qsort的基础使用方法

①qsort的基本参数

②name_cmp的讲解

2.静态通讯录的实现

2.1文件的创建

2.2菜单的打印,以及主函数框架

2.3通讯录变量定义和数据存储

2.4添加函数与显示函数

检查函数:

2.5清除与退出

2.6删除与修改

2.7名字升序排序

3.代码概览

①源:

②function.h

 ③function.cpp

4.结语


0.前言:

        经过一段时间的C语言学习,相信大家与我一样都学会了很多C语言的知识,当然知识是需要检验滴。所以我今天来讲解一个经典的项目——通讯录。我采用的编译器是VS2022环境是x86,64位系统。

        PS:这次讲解会细化到步(如果已经掌握了可以根据目录进行跳转噢)会从静态存储到动态存储,再写到文件存储。希望大家可以有耐心看完呀。(这里是静态篇其他的后续再补充哩)


1.静态通讯录需要用到的知识点:

1.1 结构体:

①什么是结构体:

结构体是一些值的集合,这些值称为成员变量。结构体的每个成员可以是不同类型的变量。例如下图Peo就是一个结构体,里面放了三个变量。

 ②结构体的声明

结构的声明方式如下图所示。

 PS:这里的struct也属于函数名

 当然其中这个“x”变量可以不在这个地方定义,可以到函数中定义,比如这样:

那么这个Peo结构名能不能省略呢?
答案是可以的。

 若像图中这样写,那么它就变成了一个匿名结构体。但是这样写有一定的弊端,就是如果你要使用匿名结构体,那就必须在结构体最后定义变量,不能在别的地方定义。

③结构体重命名:

PS:这里的struct Peo是原函数名,Peo是新的结构名。 

如若像图中这样写,那么就既完成了结构体的声明也完成了重命名。这个结构体是可以使用的,不需要写两个。但是重命名与声明有点不同,就是最后的Peo不是变量,不能被当成变量使用。所以定义变量要在别处定义。

那么重命名有什么好处呢?重命名可以让后续的定义变得更加精简。在C语言中这样的定义是不被允许的,前面必须加上struct但是重命名后就可以这样定义,从而使代码更加精简。

④结构体的访问

如图我们先建立一个结构体

 这里的Peo a[10]={0};就是在对结构体变量进行初始化,使这个数组中的所有内存都清空,便于后续的操作。

接下来我们进行读入操作,结构体变量读数据与普通变量其实没有多少不同,只需要注意一些语法就行。

 输出呢也是一样滴

当然除了这种用法,还有一种应用的更多的用法。

也就是定义结构体指针,通过指针进行访问。后面写通讯录进行结构体传参的时候,会大量用到哈。为什么要传址调用呢?直接调用不行么?原因如下:函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。 如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的下降。


1.2枚举类型:

①什么是枚举?

枚举顾名思义就是一一列举。 把可能的取值一一列举。例如周一到周日,可以一一列举,通讯录的功能也可以一一列举。所以我们等一下将采用枚举类型来列举功能。(这里就点一下,不细细展开哩)

②枚举类型的定义

 如若像这里这样定义,那么Mon的初始值就是0,后面的项逐个加一,Sun的值就是6。

③枚举类型的优点

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

1.3qsort的基础使用方法

先解释一下为啥要用qsort哈,就是在C语言阶段,如果要进行很多数据的排序,用传统的冒泡、旋转、插入排序……可能会不够快,所以在这里就练习使用一下qsort进行快排。

①qsort的基本参数

PS:需要用到头文件stdlib.h

稍微解释一下这些参数,首先base就是参加排序的数据首地址,num是参与排序的个数 ,size是每个数据的大小,最后那个函数指针这里就不细讲了,我们这里先暂时称为name_cmp后文再展开。

我们这里先制作一下数据便于后续的讲解。(这个数据是指他们的名字分别是9~0)

②name_cmp的讲解

先对排序数据进行一个解释。

 再对应到上面的参数,就可以先把参数都填上去。如图:

那么name_cmp到底是什么呢?其实这个东西就是数据交换的规则。我们先看一下官方对这个参数的解释:

我们这里可以简单的认为,升序升序返回<0,降序>0(当然这个不准确)

直接翻译的话是p1<p2时返回<0;

那么这个函数该怎么写呢?

 我们先根据函数原型写个框架。想必到这里大家也会有些疑问,比如为什么这里是const void*呢?首先加const是因为参加排序的数据,不应该被更改,增加了安全性,总不能说1 3去排序给我排出个2 4出来吧! 指针是void*类型是因为,qosrt适用于所有数据类型的排序,如果不是void*那么这个函数就写死了,就只能为一种数据服务,而void*可以接收所有指针。(先拿过来再强制转化嘛)

函数框架讲完了,那么中间部分该咋写呢?首先要先明确我们的需求,我们的需求是根据名字来排序,那也就是字符串咯,所以我们掌声有请strcmp函数。

PS:如果不清楚strcmp函数,欢迎去C语言字符串&&内存函数的介绍_wsyjpgs的博客-CSDN博客

 初步学习一下。

我们来看strcmp的返回值

 好像完美符合我们的要求,所以就直接大胆怼上去。

我们一步一步来

 

 

 

 

 

 大功告成,我们来使用看一下!

 已经成功排好序了。接下来,就用这些理论开始写静态通讯录8。

2.静态通讯录的实现

2.1文件的创建

为了方便功能的实现以及后续的调试,这里将用3个文件实现。分别是源.cpp(用于调试与运行),function.h(用于头文件的包含,函数、变量的声明等),function.cpp(用于功能的实现)

 

为什么是cpp呢?因为创建的时候忘记改了,不过代码都是用C写的,也没有用到C++无法使用的函数。所以无伤大雅啦!


2.2菜单的打印,以及主函数框架

既然咱们写的是C语言那,就必须要个主函数,这里在源.cpp中写主函数。

 

我们想实现通讯录的功能,那么是不是应该打印一个菜单告诉使用者,咱们可以实现什么功能,

所以我们先写一个菜单函数

 

(为什么不对齐呢?这个是字体原因,打印出来是对齐滴) 

这里是不是用到了printf函数,所以我们这里要去文件function.h中包含一下头文件

 

 那么我们是不是也要在源中引用一下这个文件,像这样:

现在咱们调用试运行一下。

 

 

 成功打印出来哩!

接下来我们用枚举类型定义这些功能。

 现在所有功能就定义好了,接下来是不是要接收操作者的输入。

接下来用选择结构进行分流。

 

 我们这里当然不难让操作者输入错误就退出程序,所以这里采用一个do-while结构

 

 到这里基本框架就已经打完了,我们进行测试一下。

 没有发现问题,我们继续下一步。

2.3通讯录变量定义和数据存储

打好了框架,现在咱们就需要创建一个合适的变量来存储数据。

现在我们到function.h文件中,定义一个结构体。

 我们先不着急定义变量,我们先想想,我们需要放多少数据,我这里定义了1000个。

接下来,就是定义数据的类型,方便后续的存储。不过在定义前我们想想,既然有最大存放量,那么是不是也需要知道,现在已经存放的量,进行判断数据是否还放得下,所以我这里又定义了一个结构体,对这两个变量进行封装。

类型定义好了,我们现在去源文件中创建变量:这里我将变量命名为ps 

 变量建立好了,紧接着是不是要初始化变量。

所以我们先去function.h中声明初始化函数,然后去function.cpp中实现。

既然要在function.cpp中实现,那么就必须包含一下声明,也就是包含一下文件

 实现的函数如下:

 然后我们去主函数里调用

 到这里,我们的前期准备就全部做完啦,可以开始实现功能了。

2.4添加函数与显示函数

我们现在来写第一个功能:添加

注意 :以后的声明都在function.h文件,函数实现都在function.h文件。后面的函数的根据以下步骤进行实现。这样后面可以写的快些。

第一步:声明

为什么这样声明呢?因为结构体传参采用传址调用是最优的,所以这里声明就用结构体指针。函数名ADD就是将上面的add首字母变成大写,这样写函数可以一目了然功能。

 第二步:实现

 这里的逻辑是一个一个输入,全部输入完后,当前数据量加一。

但是这样写是不准确的,因为我们没有判断数据是否满了,所以我们要写一个函数进行判断。


检查函数:

        声明:

        

         实现:

这里的功能应该一目了然,只要当前数据量小于最大存储量就没有慢


 接着我们调用这个函数并且用一个数接收一下返回值进行判断,这样就完成了添加函数的实现。

 

 虽然我们的添加函数写完了,但是我们该怎么测试数据是否成功输入呢?

为了方便调试,我们就把显示函数一起写了。

声明:

 

 实现:

这里的-20是让数据对齐,使得打印出来的数据更加美观。

显示函数写完了,我们来测试运行一下。

我们发现数据显示出来了,说明函数功能实现成功。 


2.5清除与退出

这里比较简单。我们清除数据的话,直接重新初始化一下就实现了,退出的话,是输入0,根据上面的do-while结构,程序是直接退出的,所以我们只要打印一句话告诉操作者退出就行了。这里就不多赘述。

 


2.6删除与修改

 

声明:

 实现:

既然是删除,那么原来就得有数据,所以我们要先检查有没有数据。 

稍微解释一下,就是sz记录的数据量,如果数据量是0说明没有数据。

接下来进行删除操作,我这里实现的是根据姓名删除,大家想用别的也行,就是下面的代码改一改就可以了。

需要实现删除,就要先查找到这个数据,所以这里我定义了一个函数进行实现。

声明:左边是存放的数据ps,右边是需要查找的名字的首地址。

实现:我这里采用的是遍历

如果没有找到,就需要重新输入,直接return 

找到了就进行删除操作

这里我进行解释一下。

所以整个删除函数是这样的:

 

 接下来是修改函数:

声明:

 实现:这里自己看8,修改就是查找到数据进行覆盖,与输入的不同就是sz不加1.


 2.7名字升序排序

声明:

实现:

 

 这里就不多讲啦,上面的用法已经讲述过了


3.代码概览

①源:

②function.h

 ③function.cpp


4.结语

到这里静态实现通讯录就已经完成啦,接下来我会发另一篇文章,对上述代码进行改造,从而实现用动态内存存储,实现用文件存储,感谢大家的浏览啦!

  • 8
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值