C语言实现——简易通讯录

前言:小伙伴们又见面啦!这几天通过我们对自定义数据类型的学习,我们已经掌握了如何同时对多种数据类型进行管理,那么今天这篇文章,我们就来干一件大事——实现简易的通讯录


一.思路分析

先来想想通讯录有哪些功能:

添加联系人信息

删除联系人信息

查找联系人信息

修改联系人信息

显示联系人信息

排序联系人信息

清空所有联系人

基本上都会有以上的7种功能,而且我们需要一个菜单来让用户进行选择。

制作一个大的项目,往往都要将代码写得规范整洁

所以我们要跟之前写三子棋和扫雷游戏一样,将宏定义常量,函数声明与定义,以及主函数分开来写,方便日后对代码的维护

Contact即为联系人,通讯录。

Contact.h        用来管理宏定义常量、函数的声明。

Contact.c        是管理函数的定义实现。

test.c               是用来进行测试代码的功能。


 二.基本框架实现

下面我们就先来一步一步的实现通讯录的基本框架

1.目录

#include "Contact.h"
void menu()
{printf("*********************************\n");printf("***** 1.add        2.del    *****\n");printf("***** 3.search     4.revize *****\n");printf("***** 5.show       6.sort   *****\n");printf("***** 7.empty      0.exit   *****\n");printf("*********************************\n");
}
int main()
{int input;do {menu();printf("请选择->:");scanf("%d", &input);switch (input){case 0:printf("退出通讯录\n");break;case 1:break;case 2:break;case 3:break;case 4:break;case 5:break;case 6:break;case 7:break;default:printf("输入错误,请重新输入\n");}} while (input);return 0;
}

目录的做法对于现在的我们来说应该算是手到擒来,但是现在我们想要进行一个小小的改进:

如果我们做这个代码要交给其他的程序员看的话,他可能看到Switch-case语句时会很难分辨出各个case语句的数字都代表的是哪一项功能,还得不停地回到菜单函数去查看。

所以我们希望用各项功能的名字取代数字,这样就会更加方便。

那么这时候,就要用到枚举啦:

#include "Contact.h"
void menu()
{printf("*********************************\n");printf("***** 1.add        2.del    *****\n");printf("***** 3.search     4.revize *****\n");printf("***** 5.show       6.sort   *****\n");printf("***** 7.empty      0.exit   *****\n");printf("*********************************\n");
}
enum Function
{EXIT,//默认从0开始ADD,DEL,SEARCH,REVIZE,SHOW,SORT,EMPTY,
};
int main()
{int input;do {menu();printf("请选择->:");scanf("%d", &input);switch (input){case EXIT:printf("退出通讯录\n");break;case ADD:break;case DEL:break;case SEARCH:break;case REVIZE:break;case SHOW:break;case SORT:break;case EMPTY:break;default:printf("输入错误,请重新输入\n");}} while (input);return 0;
}

枚举常量默认从0开始,所以将exit放在第一位,而后逐个递增。

最后再将数字进行替换就好啦。


2.创建联系人

通讯录里会保存联系人的哪些信息呢???

名字、电话、住址等等,这些显然不会是一种数据类型

所以创建联系人变量,就要用到结构体啦。

struct Peomessage
{char name[20];int tele[12];char addr[30];
};

简单创建一个联系人信息的结构体,这时候又会有问题:

一个人的电话的不会超过12个数字,但是名字不会超过20个字符吗???

某一天,我认识了一个外国朋友,它的名字特别长,就要修改name数组的大小,地址同样如此

那么为了方便高效,我们将这两个数组的大小进行宏定义

#define NAME_MAX 20
#define ADDR_MAX 30
typedef struct Peomessage
{char name[NAME_MAX];int tele[12];char addr[ADDR_MAX];
}Peomessage;

 同时我们使用typedef类型重定义关键字将此结构体类型的名字定义为Peomessage,方便我们后续的使用。

创建完联系人类型之后呢,我们就要开始创建联系人列表,这显然需要一个结构体类型的数组

那么数组的大小是多少呢???这个又是一个无法估量的问题,所以我们仍然使用宏定义常量

先默认能够存放100个联系人。

Peomessage data[DATA_MAX];

int sz;

 我们建立了这样一个数组。除此之外,我们还需要一个整型变量来帮助我统计通讯录里有多少个联系人了,所以我们定义sz

不难看出,这两者我们也需要同时管理,所以干脆就将它们两个也用一个结构体来管理

typedef struct Contact
{Peomessage data[DATA_MAX];int sz;
}Contect;

3.初始化数据

我们创建了Contect结构体之后,还需要将其内部数据初始化为0,以免出现异常情况。

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

memset是我们之前讲过的内存函数,可以将任意位置的任意数量的数据设置成我们想要的值

这里要讲的一点是assert函数,这个函数的作用就是帮助我们检查代码是否有错,有错误则会立即终止程序并返回错误信息。

这里主要是帮助我们判断pc指针的可用性,如果pc指针不可用,那么就会造成巨大的问题。

通过上述的步骤,我们已经实现了通讯录的基本框架下面我们开始实现各种功能。 


三.功能实现

每一个功能的实现,必然少不了对于函数的运用。

1.添加联系人信息

再添加信息之前,有一点非常值得注意:那就是我们的通讯录有没有存满

所以我们得先进行一个判断。

void AddContact(Contact* pc)
{assert(pc);//判断是否已存满if (pc->sz == DATA_MAX){printf("通讯录已满,无法添加\n");return;}//没有存满则进行存放printf("请输入名字:");scanf("%s", pc->data[pc->sz].name);printf("请输入电话:");scanf("%s", pc->data[pc->sz].tele);printf("请输入住址:");scanf("%s", pc->data[pc->sz].addr);pc->sz++;printf("添加成功\n");
}

添加过一个联系人之后,我们的sz就需要+1,用来记录我们已经存放了多少个联系人的信息。

来看实践:

添加完联系人之后,我们想马上看一下我们到底有没有存进去

下面我们就实现显示联系人信息


2.显示联系人信息

我们这里要解释一点,显示联系人信息是显示当前已经存放过的所有联系人的信息

而之后要讲的查找联系人信息才是针对某个联系人来显示

那么既然要打印所有人的信息,就必然少不了对于循环的运用。

同样的,在显示之前,我们还要判断一下通讯录是否为空

void ShowContact(Contact* pc)
{assert(pc);if (pc->sz == 0){printf("通讯录为空\n");return;}int i = 0;for (i = 0; i < pc->sz; i++){printf("%s %s %s\n", pc->data[i].name, pc->data[i].tele, pc->data[i].addr);}
}

这样我们就可以显示出我们的联系人信息啦。

但是发现我们的信息上边没有像名字,电话,住址这样的列表名,而且我们每一行的信息排列并不整齐

所以我们进行几处修改和补充:

void ShowContact(Contact* pc)
{assert(pc);if (pc->sz == 0){printf("通讯录为空\n");return;}printf("%-20s%-12s%-30s\n","姓名","电话","住址");int i = 0;for (i = 0; i < pc->sz; i++){printf("%-20s%-12s%-30s\n", pc->data[i].name, pc->data[i].tele, pc->data[i].addr);}
}

这种打印方式不知道小伙伴们了不了解:

%s是我们常规的方式,那么%20s就是指在打印的数据之前先打印20个空格

%-20s则代表着先打印数据,在打印20个空格,这样我们就能实现对齐啦。


3.查找联系人信息

这里我们先讲查找联系人信息

因为删除联系人信息、修改联系人信息实现的前期是:我们必须得先找到这个人才行。

 那么我们该怎么查找呢???显然通过名字是最直接的方法。而且我们需要用到循环

既然使用名字查找这种方式到处都要用到,所以我们干脆给它也写成一个函数

int FindByName(Contact* pc, char* name)
{assert(pc && name);int i = 0;for (i = 0; i < pc->sz; i++){if (strcmp(name, pc->data[i].name) == 0){return i;}}return -1;
}

这里我们这个函数的返回值为什么要是整型呢???

而且要是找到的话还返回它在通讯录列表里的位置,往下看就知道啦。

void SearchContact(Contact* pc)
{assert(pc);char name[NAME_MAX];printf("请输入要查找人的名字:");scanf("%s", name);int ret = FindByName(pc, name);if (ret == -1){printf("找不到该联系人\n");return;}printf("%-20s%-12s%-30s\n", "姓名", "电话", "住址");printf("%-20s%-12s%-30s\n", pc->data[ret].name, pc->data[ret].tele, pc->data[ret].addr);
}

 通过定义ret来接收FindByName函数的返回值,这样我们就能得到要查找的联系人的位置,能够轻松的打印出他的信息啦。

 


4.删除联系人信息

删除某个联系人信息,必然也是要先查找一下这个联系人存不存在

同时想要删除一个联系人信息,必然是通过他的名字来删除

void DelContact(Contact* pc)
{assert(pc);char name[NAME_MAX];printf("请输入要查找人的名字:");scanf("%s", name);int ret = FindByName(pc, name);if (ret == -1){printf("找不到该联系人\n");return;}
}

接下来就是要思考怎么来删除了。

我们使用的通讯录本质上是一个数组,想要删除某个位置的信息,首先想到的就是把它置为0

但是这样一来我们就会浪费一个空间因为你不知道这个位置的数组下标,也不可能把一个新的联系人信息补充到这个位置上来

所以我们采取逐个覆盖的方法,用后边的信息逐一向前一位进行覆盖

void DelContact(Contact* pc)
{assert(pc);char name[NAME_MAX];printf("请输入要删除联系人的名字:");scanf("%s", name);int ret = FindByName(pc, name);if (ret == -1){printf("该联系人信息不存在\n");return;}//删除联系人int i = 0;for (i = ret;i < pc->sz - 1; i++){pc->data[i] = pc->data[i + 1];}pc->sz--;printf("删除成功\n");
}

用 i 接收 FindByName 的返回值 ret ,这样我们就得到了要删除的联系人的位置,从这里开始我们通过循环进行逐一覆盖。

删除一个联系人之后,不要忘记我们的sz要-1

 


5.修改联系人信息

修改联系人信息之前,我们还是要先查找一下这个联系人存不存在,如果存在则进行修改。

修改联系人信息则需要让用户选择出要具体修改哪个信息

所以我们还需要一个小目录来供用户选择,这里的操作就和刚开始的制作目录的方法一样啦。

void menu2()
{printf("*******************************\n");printf("***** 0.exit       1.name *****\n");printf("***** 2.tele       3.addr *****\n");printf("*******************************\n");
}
enum Type
{QUIT,NAME,TELE,ADDR
};

这里为了不发生类型多次重定义,我们将“退出”用它的另一个英文“quit”来定义。

void ReviseContact(Contact* pc)
{assert(pc);char name[NAME_MAX];printf("请输入要修改联系人的名字:");scanf("%s", name);int ret = FindByName(pc, name);if (ret == -1){printf("该联系人信息不存在\n");return;}int input;do{menu2();printf("请选择要修改的信息类型:");scanf("%d", &input);switch (input){case QUIT:printf("退出修改\n");break;case NAME:printf("请输入名字:");scanf("%s", pc->data[ret].name);printf("修改成功\n");break;case TELE:printf("请输入电话:");scanf("%s", pc->data[ret].tele);printf("修改成功\n");break;case ADDR:printf("请输入地址:");scanf("%s", pc->data[ret].addr);printf("修改成功\n");break;default:printf("选择错误,请重新选择\n");break;}} while (input);
}

 来看实战:


 6.排序联系人信息

排序联系人信息我们这里需要用到一个函数——qsort

使用这个函数需要头文件:#include<stdlib.h>

 qsort函数有4个参数,分别是:

base        排序的首地址

num         排序的数据数量

size          排序的数据字节大小

compar    排序的方式函数  

 通讯录的排序都是通过名字的首字母来排序的,所以这里我们要比较的是两个名字的字符串

所以我们强制类型转换之后要调用name成员来比较。

int compare(const void* a, const void* b)
{return strcmp(((Peomessage*)a)->name, ((Peomessage*)b)->name);
}

这里的强制类型转换用法我们在之前的文章——内存函数中已经讲到,不熟悉的小伙伴建议再去补习补习哦。

void SortContact(Contact* pc)
{assert(pc);qsort(pc->data, pc->sz, sizeof(pc->data[0]), compare);printf("排序成功\n");
}

我们要排序的是data这个结构体类型的数组,所以要传入它的首地址、大小、以及每个结构体数据的字节大小,最后传入我们的比较函数compare

来看实战:


7.清空所有联系人

那么最后,如何清空所有的联系人呢???

其实这个超级简单,所谓清空,不就是再初始化一次吗,我们一开始就已经搞定啦。

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


 四.完整代码展示

1.Contact.h

#include<stdio.h>
#include<string.h>
#include<assert.h>
#include<stdlib.h>#define NAME_MAX 20
#define ADDR_MAX 30
#define DATA_MAX 100 
//选项
enum Function
{EXIT,//默认从0开始ADD,DEL,SEARCH,REVISE,SHOW,SORT,EMPTY
};
enum Type
{QUIT,NAME,TELE,ADDR
};
//联系人信息
typedef struct Peomessage
{char name[NAME_MAX];char tele[12];char addr[ADDR_MAX];
}Peomessage;
typedef struct Contact
{Peomessage data[DATA_MAX];int sz;
}Contact;
//初始化通讯录
void InitContact(Contact* pc);
//增加联系人信息
void AddContact(Contact* pc);
//显示联系人信息
void ShowContact(Contact* pc);
//查找联系人信息
void SearchContact(Contact* pc);
//删除联系人信息
void DelContact(Contact* pc);
//修改联系人信息
void ReviseContact(Contact* pc);
//排序联系人信息
void SortContact(Contact* pc);
//清空所有联系人
void EmptyContact(Contact* pc);

2.Contact.c

#include"Contact.h"
//初始化通讯录
void InitContact(Contact* pc)
{assert(pc);pc->sz = 0;memset(pc->data, 0, sizeof(pc->data));
}
//增加联系人信息
void AddContact(Contact* pc)
{assert(pc);//判断是否已存满if (pc->sz == DATA_MAX){printf("通讯录已满,无法添加\n");return;}//没有存满则进行存放printf("请输入名字:");scanf("%s", pc->data[pc->sz].name);printf("请输入电话:");scanf("%s", pc->data[pc->sz].tele);printf("请输入住址:");scanf("%s", pc->data[pc->sz].addr);pc->sz++;printf("添加成功\n");
}
//显示联系人信息
void ShowContact(Contact* pc)
{assert(pc);if (pc->sz == 0){printf("通讯录为空\n");return;}printf("%-20s%-12s%-30s\n","姓名","电话","住址");int i = 0;for (i = 0; i < pc->sz; i++){printf("%-20s%-12s%-30s\n", pc->data[i].name, pc->data[i].tele, pc->data[i].addr);}
}
//通过名字查找
int FindByName(Contact* pc, char* name)
{assert(pc && name);int i = 0;for (i = 0; i < pc->sz; i++){if (strcmp(name, pc->data[i].name) == 0){return i;}}return -1;
}
//查找联系人信息
void SearchContact(Contact* pc)
{assert(pc);char name[NAME_MAX];printf("请输入要查找人的名字:");scanf("%s", name);int ret = FindByName(pc, name);if (ret == -1){printf("找不到该联系人\n");return;}printf("%-20s%-12s%-30s\n", "姓名", "电话", "住址");printf("%-20s%-12s%-30s\n", pc->data[ret].name, pc->data[ret].tele, pc->data[ret].addr);
}
//删除联系人信息
void DelContact(Contact* pc)
{assert(pc);char name[NAME_MAX];printf("请输入要删除联系人的名字:");scanf("%s", name);int ret = FindByName(pc, name);if (ret == -1){printf("该联系人信息不存在\n");return;}//删除联系人int i = 0;for (i = ret;i < pc->sz - 1; i++){pc->data[i] = pc->data[i + 1];}pc->sz--;printf("删除成功\n");
}
void menu2()
{printf("*******************************\n");printf("***** 0.exit       1.name *****\n");printf("***** 2.tele       3.addr *****\n");printf("*******************************\n");
}
//修改联系人信息
void ReviseContact(Contact* pc)
{assert(pc);char name[NAME_MAX];printf("请输入要修改联系人的名字:");scanf("%s", name);int ret = FindByName(pc, name);if (ret == -1){printf("该联系人信息不存在\n");return;}int input;do{menu2();printf("请选择要修改的信息类型:");scanf("%d", &input);switch (input){case QUIT:printf("退出修改\n");break;case NAME:printf("请输入名字:");scanf("%s", pc->data[ret].name);printf("修改成功\n");break;case TELE:printf("请输入电话:");scanf("%s", pc->data[ret].tele);printf("修改成功\n");break;case ADDR:printf("请输入地址:");scanf("%s", pc->data[ret].addr);printf("修改成功\n");break;default:printf("选择错误,请重新选择\n");break;}} while (input);
}
//排序联系人信息
int compare(const void* a, const void* b)
{return strcmp(((Peomessage*)a)->name, ((Peomessage*)b)->name);
}
void SortContact(Contact* pc)
{assert(pc);qsort(pc->data, pc->sz, sizeof(pc->data[0]), compare);printf("排序成功\n");
}
//清空所有联系人
void EmptyContact(Contact* pc)
{assert(pc);pc->sz = 0;memset(pc->data, 0, sizeof(pc->data));printf("已清空通讯录\n");
}

3.test.c

#include "Contact.h"
void menu1()//菜单
{printf("*********************************\n");printf("***** 1.add        2.del    *****\n");printf("***** 3.search     4.revise *****\n");printf("***** 5.show       6.sort   *****\n");printf("***** 7.empty      0.exit   *****\n");printf("*********************************\n");
}
int main()
{Contact con;//初始化通讯录InitContact(&con);int input;do {menu1();printf("请选择->:");scanf("%d", &input);switch (input){case EXIT:printf("退出通讯录\n");break;case ADD:AddContact(&con);break;case DEL:DelContact(&con);break;case SEARCH:SearchContact(&con);break;case REVISE:ReviseContact(&con);break;case SHOW:ShowContact(&con);break;case SORT:SortContact(&con);break;case EMPTY:EmptyContact(&con);break;default:printf("输入错误,请重新输入\n");}} while (input);return 0;
}

五.总结

到这里,对于如何用C语言实现简易通讯录的讲解终于完结撒花啦!!!

喜欢博主文章的小伙伴一定要点个关注不迷路哦。

最后还是要记得一键三连呀!

祝大家中秋节快乐,我们下期再见啦!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/120604.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python 爬虫 / web 面试常见问题

嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 &#x1f447; &#x1f447; &#x1f447; 更多精彩机密、教程&#xff0c;尽在下方&#xff0c;赶紧点击了解吧~ python源码、视频教程、插件安装教程、资料我都准备好了&#xff0c;直接在文末名片自取就可 爬虫面试常见…

No145.精选前端面试题,享受每天的挑战和学习

🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6 🍨 阿珊和她的猫_CSDN个人主页 🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 🍚 蓝桥云课签约作者、已在蓝桥云课上架的前后端实战课程《Vue.js 和 Egg.js 开发企业级健康管理项目》、《带你从入…

卸载无用Mac电脑软件应用程序方法教程

如何在Mac电脑卸载应用程序&#xff1f;Mac OS系统的用户卸载软件时&#xff0c;大部分会选择直接将软件图标拖进废纸篓清倒。这种操作会留下大量程序残余文件占据磁盘空间&#xff0c;手动清理又怕误删文件&#xff0c;有时还会遇到无法移除的恶意/流氓软件。小编今天分享3种可…

Unity 制作登录功能02-创建和链接数据库(SQlite)

国际惯例&#xff1a;先看效果 1.SQlite是一种嵌入型数据库 在Unity开发游戏时使用SQLite有多种原因&#xff0c;以下是其中一些主要原因&#xff1a; 嵌入式数据库&#xff1a;SQLite是一个嵌入式数据库引擎&#xff0c;这意味着它不需要单独的服务器进程。这使得使用SQLite非…

专题五:优先级队列

"你了解我&#xff0c;最干净的轮廓&#xff0c; 握住小小风车和放肆的梦~" 堆是一个不错的数据结构&#xff0c;而在计算机中&#xff0c;无法表示二叉分支结构&#xff0c;因此我们经常会看到使用线性表来作为堆的存储容器。在接触堆的时候&#xff0c;我们是把它…

admin后台管理

admin后台管理 django 提供了比较完善的后台管理数据库的接口&#xff0c;可供开发过程中调用和测试使用 django 会搜集所有已注册的模型类&#xff0c;为这些模型类提拱数据管理界面&#xff0c;供开发者使用 admin配置步骤 创建后台管理帐号- 该账号为管理后台最高权限账号…

【数据结构初阶】六、线性表中的队列(链式结构实现队列)

相关代码gitee自取&#xff1a; C语言学习日记: 加油努力 (gitee.com) 接上期&#xff1a; 【数据结构初阶】五、线性表中的栈&#xff08;顺序表实现栈&#xff09;_高高的胖子的博客-CSDN博客 1 . 队列&#xff08;Queue&#xff09; 队列的概念和结构&#xff1a; 队列…

No147.精选前端面试题,享受每天的挑战和学习

🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6 🍨 阿珊和她的猫_CSDN个人主页 🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 🍚 蓝桥云课签约作者、已在蓝桥云课上架的前后端实战课程《Vue.js 和 Egg.js 开发企业级健康管理项目》、《带你从入…

Docker Dockerfile解析

Dockerfile是什么 Dockerfile是用来构建Docker镜像的文本文件&#xff0c;是由一条条构建镜像所需的指令和参数构成的脚本。 官网&#xff1a;Dockerfile reference | Docker Docs 构建三步骤&#xff1a; 编写Dockerfile文件docker build命令构建镜像docker run依镜像运行容…

Unity之Hololens如何实现3D物体交互

一.前言 什么是Hololens? Hololens是由微软开发的一款混合现实头戴式设备,它将虚拟内容与现实世界相结合,为用户提供了沉浸式的AR体验。Hololens通过内置的传感器和摄像头,能够感知用户的环境,并在用户的视野中显示虚拟对象。这使得用户可以与虚拟内容进行互动,将数字信…

在Windbg中设置断点追踪打开软件远程调试开关的模块

目录 1、Windbg动态调试 2、在Windbg中设置断点 2.1、在函数入口处设置断点 2.2、在函数内部某一行上设置断点 3、设置断点跟踪对打开远程调试开关接口的调用 3.1、编写演示代码 3.2、在Windbg中设置调用SetRemoteDebugOn接口的断点进行跟踪 4、最后 VC常用功能开发汇总…

python tempfile模块:生成临时文件和临时目录

嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! python更多源码/资料/解答/教程等 点击此处跳转文末名片免费获取 tempfile 模块专门用于创建临时文件和临时目录&#xff0c;它既可以在 UNIX 平台上运行良好&#xff0c;也可以在 Windows 平台上运行良好。 tempfile 模块中常用…