IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> c语言实现通讯录-动态可存储版(新手向多图详细预警) -> 正文阅读

[C++知识库]c语言实现通讯录-动态可存储版(新手向多图详细预警)

我想先简单说明一下,本文是按照本人的思路来走的,不是像那些参考书那样直接给出整个答案。所以我会慢慢引导读者搭构出思路,然后一步一步逻辑清晰地写出来.

1. 代码整体框架

请添加图片描述

2. 功能函数的实现

2.1 添加人员信息函数AddContact()-附带基本框架的搭构

请添加图片描述
请添加图片描述

上述代码是放在Contact.h 头文件中的
你现在如果搞清楚上面的步骤后,需要拉回自己的思维,现在我们是为了实现AddContact函数的功能。
但是还有一点工作要提前准备:我们的程序什么时候才能走到这个函数?

请添加图片描述

AddContact函数的参数是什么也需要考虑?
考虑到其功能——添加信息,那么就肯定涉及修改结构体对象,那么就需要传址。
所以其参数部分应该是Contact结构体类型所创建的对象的地址。
那么接下来根据上述内容就可以写出main函数的基本框架了。

改良的部分有:将switch……case分支语句中的数字,如case 1、case 2 之类的case后面的数字,改成枚举常量,当然,各个枚举常量对应的值要正好对应用户输入的选项。然后这个自定义的枚举类型可以放到Contact.h 头文件中,还有menu函数因为封装的缘故,还是需要把定义放在Contact.c中,函数声明放在Contact.h中。

对于其他功能函数的参数,尽管有些功能函数是不需要对结构体对象进行修改,但是我依旧选择了传址,这是由于传值(没打错字!)会产生的临时拷贝而造成的压栈开销过大,内存利用率降低的原因。

请添加图片描述

所以搞定了整个main函数的框架后终于可以开始逐个击破了,虽然我觉得上述部分应该写在第一个部分,但是这是为了引导读者一步一步来,所产生的必然的逻辑。
如此看来,还需要一件最重要的事,那就是——初始化
这里仍然可以封装一个函数InitContact,其参数仍然为con的地址,实现的功能是需要把sz即代表当前信息个数赋值为0,把整个通讯录中用于存放信息的空间也都赋值为0。
上述操作可以使用memset函数,头文件为string.h,用于将指定内存块区域设置为某一值,有三个参数,第一个为所要修改内存的首地址,第二个是所要修改的值,第三个是所要修改的字节数。

void InitContact(Contact* pc)
{
	pc->sz = 0;
	memset(pc->data, 0, sizeof(pc->data));
}

接下来我们就正式开始完成Addcontact函数
功能如下:

请添加图片描述

以下为AddContact函数的定义,放在Contact.c文件中,其函数声明仍需要放在Contact.h文件中。
关于录入信息的一些难点,首先是字符串的录入,双引号里一定是%s,逗号后的参数部分是一个地址,也就是数组首元素地址,即数组名,所以我们只需要在逗号后写上数组名即可。但是在结构体中就比较复杂了,就拿录入姓名举例,pc->data 代表main函数中所创建的Contact结构体对象con中的成员data的地址,那么在其后面加上[]就可以代表data数组的第几个元素了,那么[]中的pc->sz就是代表con中的成员sz的值了(注意这里是值而不是地址),那么pc->data[pc->sz]就表示在PeoInfo结构体类型所创建的结构体数组对象data的第sz+1个元素,那么录入姓名,就需要加上.name,最终pc->data[pc->sz].name就代表名字数组的首元素地址。
然后是数字的录入,比如年龄,scanf双引号内的参数为%d,逗号后的参数为对应整形的地址,那么需要在在完成找到这个整形后加上&,具体步骤只是和上述录入字符串的步骤增加了最后一步。

请添加图片描述

2.2 删除联系人的信息

请添加图片描述

以下是DelContact函数的定义,同样需要把定义放于Contact.c中,函数声明放置于Contact.h中,接下来的函数就不再赘述有关代码放置于某某文件的详情了。
可以看到,宏定义的常量又一次起到了作用,MAX_NAME代表名字的字符所占最大的长度,当然也包括了\0。
根据输入的名字来查找通讯录中是否包含这个名字的这个功能,也依旧被封装为了一个函数,这个函数有些不同,是static修饰的静态函数,只能在当前头文件中使用,所以无需再把函数声明写在Contact.h的头文件中。这样写的目的也是为了防止函数的随意调用所导致的污染。
查找字符串是遍历了所有的含有有效信息的数组元素,利用strcmp函数进行比较。

请添加图片描述
请添加图片描述

2.3 搜索联系人信息

(1)根据姓名来查找
(2)打印出标题来:姓名 年龄 性别 电话 住址
(3)在标题下打印对应此人的信息

需要简单提及的知识点:printf函数双引号内的参数部分可以在%和字母之间加上数字,-20代表左对齐且占20长度,正数代表右对齐。
匹配名字依然用上一个功能函数DelContact所用的FindByName函数
还有注意标题的打印都是打印字符串,即%s,而信息打印中的数字是%d
请添加图片描述

2.4 修改联系人信息

(1)输入要修改者的名字
(2)查找该名字是否存在
(3)利用返回的下标进行直接scanf录入修改即可
同样需要注意录入年龄时需要去地址,即加上&

请添加图片描述

2.5 根据姓名排序整个通讯录

算法等同与冒泡排序,需要注意两个由相同结构体所创建的对象,是可以进行直接赋值的,比如PeoInfo创建的tmp与data数组中的元素可以进行直接赋值,尽管每个对象都有多个成员(姓名、年龄、性别等)。

请添加图片描述

2.6 清除整个通讯录等信息

清除整个通讯录当然需要两次确认,否则一不小心手滑给全删了,当然还可以改进加上保护密码。
清除整个通讯录,本质上就是把整个通讯录重新初始化一遍。
用InitContact函数

请添加图片描述

2.7 打印整个通讯录

和查找通讯录等功能类似,不过不需要输入名字,而是直接打印。
首先还是需要打印标题,然后再依次打印通讯录中所储存的信息。

请添加图片描述

3. 代码的优化

3.1 动态开辟空间

改进缘由:一次性就开辟了1000个空间,但是可能一开始就用不了这么多空间,那么我们就可以改进为先开辟三个空间,如果空间满了,再开辟2个空间,这样也可以避免1000个空间也不够存放的情况。

动态开辟利用malloc函数,需要引用头文件#include<stdlib.h>
需要新增一个变量capacity来记录当前通讯录的容量,这个变量放置于Contact类型的结构体中。同时Contact结构体中的数组data[MAX]需要改为一个PeoInfo类型的指针变量,因为不必再局限于固定的数组元素个数了。
新增了动态开辟的功能后,自然某些功能函数就需要发生变化了,即那些修改了空间的函数。

初始化通讯录函数InitContact中需要将capacity初始化为0。
动态开辟会在新加入信息时发生,所以malloc的调用是在AddContact函数中,因为是一次性开辟三个空间,满了后再开辟两个空间,依然可以用宏定义3和2,分别为DEFAULT_SZ和INC_SZ,表示初始空间和增加空间数,这样也是利于代码的可读性。

请添加图片描述

这个CheckCapacity函数是在AddContact函数中调用的,当然也可以定义为静态函数,不过这里就没有那样了,所以还需要在Contact.h中声明一下。

请添加图片描述
请添加图片描述
请添加图片描述

DestoryContact的调用是在switch case EXIT分支中,即程序退出时。
动态开辟的内存需要由程序员手动释放,否则会造成内存泄漏的危机。
第一步free代表将pc->data所指向的空间全部清除,但由于这个指针仍然可以找到这块已经被释放的内存,所以还要把这个指针赋值为NULL即空指针。
最后还需要把容量和有效信息数都置为0

请添加图片描述

3.2 将信息储存在文件中

我们想要实现的基本功能为:
1、程序开始时加载已经储存好的信息到通讯录中。
2、在程序结束时更新信息到文件中。
因为是先有保存信息到文件中,才有加载文件到程序中,所以先说一下如何保存信息到文件中。
封装一个函数SaveContact,调用是在main函数的switch case EXIT分支语句中,即程序结束时调用。
fopen函数中的第一个参数为文件的相对路径(很短,就是下面的示例)或者绝对路径(特别长,而且使用的时候需要注意’ \ '是不能单独使用的,因为是转义字符,所以要改成‘ \ \ ’才能表示一个反斜杠 ),当这个文件不存在时,程序会帮助你自动创建一个这样的文件在当前的目录下,你也可以指定你所想要录入的文件位置。

请添加图片描述

那么接下来就是将文件加载到程序中的功能了,封装一个函数LoadContact,在InitContact函数中调用,用于将二进制文件信息转化到程序中来。

首先是打开文件,利用fopen函数。

再是加载文件,创建一个PeoInfo类型的结构体变量,用于暂时存储从文件中读取的信息,fread函数是从指定文件中读取二进制信息到某一地址上。

读取文件类似于增加信息的操作,所以首先需要检测是否有足够空间,再对应地录入信息,最后将有效信息变量sz自增。

最后一步是关闭文件。

请添加图片描述

这个函数是在InitContact函数中调用。

4. 代码的不足之处

随便举几个吧~
比如名字如果相同,那么查找的时候却只能出现一个人的信息,值得修改这个BUG。
又比如只有单调的界面,暑假小学期的时候恰好又学了点EasyX的图形库,可以试着把它美观一点。
还有就是,查找算法和排序算法效率比较低,比如排序算法可以用qsort快排函数代替。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-08-07 11:49:05  更:2021-08-07 11:49:13 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/9 10:56:41-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码