哈喽各位老铁们,今天给大家带来一期通讯录的静态版本的实现,何为静态版本后面会做解释,话不多说,直接开始! 关于通讯录,其实也就是类似于我们手机上的通讯录一样,有着各种各样的功能,小编来带大家实现一部分功能就好啦(1.添加联系人 2. 删除指定联系人 3.查找指定联系人 4.修改指定联系人 5.显示全部联系人 6.排序通讯录 7.清空通讯录)要实现这些功能,首先得要有一个可以存放联系人的一块空间,而要存放的联系人的个人信息有:名字、性别、年龄、电话、家庭地址。这些个人信息类型不同,大小也不同,那该如何去存放,如何查找,又该如何去修改?我们一步一步来通过代码的方式实现这些功能
1.文件的划分
在前面发布的博客中就提到过划分文件进行编写,在之前的三子棋、扫雷的代码中就是通过划分文件,每一个文件实现相对应的功能,互不干扰,那么通讯录也要使用同样的方法
头文件:Contact.h
在头文件里面主要实现函数的声明,变量的定义、头文件的包含
源文件:Contact.c
在这个源文件里面实现通讯录的实现模块
源文件:test.c
这个源文件里面实现测试通讯录相关的功能
2.通讯录
实现一个通讯录: 1.这个通讯录可以存放100个人的信息 2.每个人的信息: 名字、性别、年龄、电话、地址 3.通讯录所包含的功能 添加联系人、删除联系人、查找联系人、修改联系人、显示通讯录、排序通讯录、清空通讯录
首先我们先搭出通讯录的基本框架:
头文件:Contact.h
//头文件的包含 #include <stdio.h>
源文件:test.c
#include "Contact.h" //测试通讯录相关的功能 void menu() { printf("**************************************\n"); printf("**** 1.Add ***** 2.Del ****\n"); printf("**** 3.Search ***** 4.Modify ****\n"); printf("**** 5.Show ***** 6.Sort ****\n"); printf("**** 7.Clear ***** 0.Exit ****\n"); printf("**************************************\n"); } int main() { int input = 0; do { menu(); printf("请选择通讯录的功能:>"); scanf("%d", &input); switch(input) { case 1: //添加联系人 break; case 2: //删除联系人 break; case 3: //查找联系人 break; case 4: //修改联系人 break; case 5: //展示 break; case 6: //排序联系人 break; case 7: //清空 break; case 0: printf("退出通讯录!\n"); break; default: printf("选择错误,请重新选择:>\n"); break; } } while (input); return 0; }
但是如果这样字写,在代码可读性不高,为什么呢?当代码走到switch case 语句中,这里的case1、case2...里面的1、2..都代表什么呢?就得返回到最上面重新查看,因此这里需要完善一下,我们可以使用枚举,将我们选择的全部可能型都一一列举出来,然后进行选择,就比较方便。
优化代码:
enum Option { EXIT, ADD, DEL, SEARCH, MODIFY, SHOW, SORT, CLEAR }; //枚举的可能取值默认是从0开始,在这里刚好对应菜单里面的选项 int main() { int input = 0; do { menu(); printf("请选择通讯录的功能:>"); scanf("%d", &input); switch (input) { case ADD: //添加联系人 break; case DEL: //删除联系人 break; case SEARCH: //查找联系人 break; case MODIFY: //修改联系人 break; case SHOW: //展示 break; case SORT: //排序联系人 break; case CLEAR: //清空 break; case EXIT: printf("退出通讯录!\n"); break; default: printf("选择错误,请重新选择:>\n"); break; } } while (input); return 0; }
基本框架搭建好了之后,就得有一个块空间来存放我们的联系人了,而联系人的信息又有多种,因此就需要一个结构体来存放联系人的信息,这里还存在一个问题,我们存放的联系人从哪里开始存放呢?所以我们还需要一个变量来确定联系人该存放在哪里,而且每当我们存放一个联系人,这个变量就要+1,以便下一个联系人存放在上一个联系人的后面。
注:如果我们将这个结构体创建在源文件中,就得在两个源文件中都要创建,因此为了节省空间,可以直接将结构体变量创建在头文件中
头文件:Contact.h
//表示一个联系人的各种信息 typedef struct PeoInfo { char name[20]; int age; char sex[5]; char tele[12]; char addr[20]; }PeoInfo; //通讯录 typedef struct Contact { PeoInfo data[100]; //用来存放100个人的信息 int sz; //记录通讯录中有效信息的个数 }Contact;
如果这样子写还是有一个弊端,如果后期要修改通讯录中存放联系人的个数和每一个联系人信息中存放的大小,所有有关的数组都得一一修改,因此我们可以直接用#define来定义大小
代码优化:
//大小的定义 #define MAX 100 #define MAX_NAME 20 #define MAX_SEX 5 #define MAX_TELE 12 #define MAX_ADDR 20 //表示一个联系人的各种信息 typedef struct PeoInfo { char name[MAX_NAME]; int age; char sex[MAX_SEX]; char tele[MAX_TELE]; char addr[MAX_ADDR]; }PeoInfo; //通讯录 typedef struct Contact { PeoInfo data[MAX]; //用来存放100个人的信息 int sz; //记录通讯录中有效信息的个数 }Contact;
这样子写在后期需要修改的时候就比较方便
存放联系人的空间也搭建好了,但是创建好的空间里面刚刚开始放的是什么东西呢?我们也不知道,因此我们先得给通讯录进行初始化,让里面一个联系人也没有,让标记的变量也变为0
2.1初始化通讯录
初始化通讯录我们分装一个函数:InitContact
源文件:test.c
int main() { int input = 0; Contact con; //创建通讯录变量 //初始化通讯录 InitContact(&con); //要修改通讯录里面的内容要传递指针 do { menu(); printf("请选择通讯录的功能:>"); scanf("%d", &input); switch (input) { case ADD: //添加联系人 break; case DEL: //删除联系人 break; case SEARCH: //查找联系人 break; case MODIFY: //修改联系人 break; case SHOW: //展示 break; case SORT: //排序联系人 break; case CLEAR: //清空 break; case EXIT: printf("退出通讯录!\n"); break; default: printf("选择错误,请重新选择:>\n"); break; } } while (input); return 0; }
头文件Contact.h
#include <string.h> //函数的声明 //初始化通讯录 void InitContact(Contact* pc);
源文件:Contact.c
//初始化通讯录 void InitContact(Contact* pc) { pc->sz = 0; memset(pc->data, 0, sizeof(pc->data)); //memset函数在使用时要包含头文件<string.h> }
2.2添加联系人
初始化好通讯录的内容之后,接下来就到了我们进行通讯录功能的实现了,首先我们实现添加联系人功能,要添加联系人,我们要确定sz的大小,如果sz等于MAX了,那不就证明通讯录以及满了嘛,就再不能添加联系人了,只有sz小于MAX的时候才可以添加联系人
源文件:test.c
case ADD: //添加联系人 printf("添加联系人\n"); AddContact(&con); //传址调用 break;
头文件:Contact.h
//添加联系人 void AddContact(Contact* pc);
源文件:Contact.c
//添加联系人 void AddContact(Contact* pc) { if (pc->sz == MAX) { printf("通讯录已满,不能添加!\n"); return; //如果满人了,直接返回 } printf("请输入名字:>"); scanf("%s", pc->data[pc->sz].name); printf("请输入年龄:>"); scanf("%d", &(pc->data[pc->sz].age)); printf("请输入性别:>"); scanf("%s", pc->data[pc->sz].sex); printf("请输入电话:>"); scanf("%s", pc->data[pc->sz].tele); printf("请输入地址:>"); scanf("%s", pc->data[pc->sz].addr); pc->sz++; //每一次添加完之后sz都要++ printf("添加成功\n"); }
2.3展示通讯录
要展示通讯录,可以先打印一行标题,有助于展示,打印整个通讯录就可以使用循环,设置一个循环,循环条件只要小于sz的大小就可以
源文件:test.c
case SHOW: //展示 ShowContact(&con); //显示通讯录不需要修改通讯录其实传值调用也可以,但是为了节省空间使用传值调用 break;
头文件:Contact.h
//显示通讯录 void ShowContact(Contact* pc);
源文件:Contact.c
//显示通讯录 void ShowContact(Contact* pc) { //先打印标题 printf("%-10s %-2d %-5s %-10s %-15s\n", "名字", "年龄", "性别", "电话", "地址"); int i = 0; for (i = 0; i < pc->sz; i++) { printf("%-10s %-2d %-5s %-10s %-15s\n", pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].tele, pc->data[i].addr); } }
2.4删除联系人
要删除联系人,首先得判断这个通讯录里面有没有联系人呀,所以要先判断sz是否为0,如果为0就证明没有联系人,判断完之后,需要删除联系人,那么首先得找到所想删除的联系人的位置,然后将这个位置记录下来,通过下标的形式访问,然后进行删除,这里还要注意,如果找完了整个通讯录,没有找到与之匹配的名字,那么就没有这个人,在删除的时候就是将记录的那个位置上的人,用后面一个人的信息进行覆盖,依次类推,然后整个通讯录的总人数减1就行了
源文件:test.c
case DEL: //删除联系人 printf("删除联系人\n"); DelContact(&con);
头文件:Contact.h
//删除联系人 void DelContact(Contact* pc);
源文件:Contact.c
//删除联系人 void DelContact(Contact* pc) { char name[MAX_NAME] = { 0 }; //判断通讯录是否为空 if (0 == pc->sz) { printf("通讯录为空,无法删除!\n"); return; } printf("请输入你要删除联系人的姓名:>"); scanf("%s", name); int i = 0; int pos = 0; for (i = 0; i < pc->sz; i++) { //找到所要删除的联系人所在的位置 if (0 == strcmp(pc->data[i].name, name)) { pos = i; break; } } if (i == pc->sz) { printf("找不到所要删除的联系人\n"); return; } //进行删除 for (i = pos; i < pc->sz - 1; i++) //判断条件这里所要交换的比总数少一个 { pc->data[i] = pc->data[i + 1]; } //删除完之后sz减一 pc->sz--; printf("删除成功\n"); }
2.5查找联系人
查找联系人和删除练习人的基本步骤一样,先判断是否为空通讯录,然后进行查找,记录位置,然后打印
源文件:test.c
case SEARCH: //查找联系人 printf("查找联系人\n"); SearchContact(&con); break;
头文件:Contact.h
//查找联系人 void SearchContact(Contact* pc);
源文件:Contact.c
//查找联系人 void SearchContact(Contact* pc) { char name[MAX_NAME] = { 0 }; if (pc->sz == 0) { printf("通讯录为空,查找不到!"); return; } printf("请输入你要查找联系人的姓名:>"); scanf("%s", name); int i = 0; int pos = 0; for (i = 0; i < pc->sz; i++) { //找到所要查找的联系人所在的位置 if (0 == strcmp(pc->data[i].name, name)) { pos = i; break; } } if (i == pc->sz) { printf("找不到所要查找的联系人\n"); return; } //打印导航栏 printf("%-10s %-4s %-5s %-12s %-30s\n", "姓名", "年龄", "性别", "电话", "地址"); //打印数据 printf("%-10s %-4d %-5s %-12s %-30s\n", pc->data[pos].name, pc->data[pos].age, pc->data[pos].sex, pc->data[pos].tele, pc->data[pos].addr); }
写到这里,我们不难发现这个查找联系人的过程重复了两次,在删除联系人和查找的时候都出现了,因此我们可以简化一下代码,直接将查找的过程分装一个查找函数,然后在使用的时候代码就不显得那么冗余,每次使用查找就直接调用这个函数
代码优化:
static int FindByName(const Contact* pc, char name[]) //使用static修饰, //只在本源文件中使用 { int i = 0; int pos = 0; for (i = 0; i < pc->sz; i++) { //找到所要删除的联系人所在的位置 if (0 == strcmp(pc->data[i].name, name)) { return i; } } return -1; } //删除联系人 void DelContact(Contact* pc) { char name[MAX_NAME] = { 0 }; //判断通讯录是否为空 if (0 == pc->sz) { printf("通讯录为空,无法删除!\n"); return; } printf("请输入你要删除联系人的姓名:>"); scanf("%s", name); int pos = FindByName(pc, name); int i = 0; if (pos == -1) { printf("找不到所要删除的联系人\n"); return; } //进行删除 for (i = pos; i < pc->sz - 1; i++) //判断条件这里所要交换的比总数少一个 { pc->data[i] = pc->data[i + 1]; } pc->sz--; printf("删除成功\n"); } //查找联系人 void SearchContact(const Contact* pc) //使用const修饰更安全 { char name[MAX_NAME] = { 0 }; if (pc->sz == 0) { printf("通讯录为空,查找不到!"); return; } printf("请输入你要查找联系人的姓名:>"); scanf("%s", name); int pos = FindByName(pc, name); int i = 0; if (pos == -1) { printf("找不到所要查找的联系人\n"); return; } //打印导航栏 printf("%-10s %-4s %-5s %-12s %-30s\n", "姓名", "年龄", "性别", "电话", "地址"); //打印数据 printf("%-10s %-4d %-5s %-12s %-30s\n", pc->data[pos].name, pc->data[pos].age, pc->data[pos].sex, pc->data[pos].tele, pc->data[pos].addr); }
2.6修改联系人
要修改联系人,首先的找到联系人的位置,然后输入信息将其修改
源文件:test.c
case MODIFY: //修改联系人 printf("修改联系人\n"); ModifyContact(&con); break;
头文件:Contact.h
//修改联系人 void ModifyContact(Contact* pc);
源文件:Contact.c
//修改联系人 void menu1() { printf("**********************************\n"); printf("******1.姓名*********2.年龄*******\n"); printf("******3.性别*********4.电话*******\n"); printf("*************5.地址***************\n"); printf("**********************************\n"); } void ModifyContact(Contact* pc) { int input = 0; char name[MAX_NAME] = { 0 }; printf("请输入你要修改的联系人姓名:>"); scanf("%s", name); //查找 int pos = FindByName(pc, name); int i = 0; if (pos == -1) { printf("找不到所要查找的联系人\n"); return; } //修改 printf("请输入要修改的具体信息:>\n"); menu1(); scanf("%d", &input); switch (input) { case 1: printf("请输入新的姓名:>"); scanf("%s", pc->data[pos].name); break; case 2: printf("请输入新的年龄:>"); scanf("%d", &pc->data[pos].age); break; case 3: printf("请输入新的性别:>"); scanf("%s", pc->data[pos].sex); break; case 4: printf("请输入新的电话:>"); scanf("%s", pc->data[pos].tele); break; case 5: printf("请输入新的地址:>"); scanf("%s", pc->data[pos].addr); break; default: printf("输入有误,修改失败\n"); return; } printf("修改成功\n"); }
2.7排序联系人
要排序联系人,也就意味着要排序结构体成员,就需要用到qsort排序,如果不清楚可以点击查看-> qsort排序详解
源文件:test.c
case SORT: //排序联系人 SortContact(&con); break;
头文件:Contcat.h
#include <stdlib.h> //排序联系人 void SortContact(Contact* pc);
源文件:Contact.c
void menu2() { printf("***************************\n"); printf("******** 1.NAME **********\n"); printf("******** 2.AGE **********\n"); printf("***************************\n"); } //按照名字排序 int cmp_byname(const void* p1, const void* p2) { return strcmp(((PeoInfo*)p1)->name, ((PeoInfo*)p2)->name); } //按照年龄来排序 int cmp_byage(const void* p1, const void* p2) { return ((PeoInfo*)p1)->age - ((PeoInfo*)p2)->age; } //排序联系人 void SqrtContact(Contact* pc) { int input = 0; if (pc->sz == 0) { printf("通讯录没有联系人,无法排序!\n"); return; } menu2(); printf("请选择排序的对象:>"); scanf("%d", &input); switch (input) { case 1: qsort(pc, pc->sz, sizeof(PeoInfo), cmp_byname); break; case 2: qsort(pc, pc->sz, sizeof(PeoInfo), cmp_byage); break; } printf("排序成功\n"); //排序成功之后打印一下 ShowContact(pc); }
2.8清空联系人
清空联系人本质上就是再将通讯录初始化,我们只需要再进行调用初始化函数就可以了
源文件:test.c
case CLEAR: //清空联系人 ClearContact(&con); break;
头文件:Contact.h
//清空联系人 void ClearContact(Contact* pc);
源文件:Contact.c
//清空联系人 void ClearContact(Contact* pc) { //初始化通讯录 InitContact(pc); printf("清空成功\n"); //初始完之后再打印 ShowContact(pc); }
3.完整代码
头文件:Contact.h
#pragma once //头文件的包含 #include <stdio.h> #include <string.h> #include <stdlib.h> //大小的定义 #define MAX 100 #define MAX_NAME 20 #define MAX_SEX 5 #define MAX_TELE 12 #define MAX_ADDR 20 //表示一个联系人的各种信息 typedef struct PeoInfo { char name[MAX_NAME]; int age; char sex[MAX_SEX]; char tele[MAX_TELE]; char addr[MAX_ADDR]; }PeoInfo; //通讯录 typedef struct Contact { PeoInfo data[MAX]; //用来存放100个人的信息 int sz; //记录通讯录中有效信息的个数 }Contact; //函数的声明 //初始化通讯录 void InitContact(Contact* pc); //添加联系人 void AddContact(Contact* pc); //显示通讯录 void ShowContact(const Contact* pc); //删除联系人 void DelContact(Contact* pc); //查找联系人 void SearchContact(const Contact* pc); //修改联系人 void ModifyContact(Contact* pc); //排序联系人 void SqrtContact(Contact* pc); //清空联系人 void ClearContact(Contact* pc);
源文件:test.c
#define _CRT_SECURE_NO_WARNINGS 1 #include "Contact.h" //测试通讯录相关的功能 void menu() { printf("**************************************\n"); printf("**** 1.Add ***** 2.Del ****\n"); printf("**** 3.Search ***** 4.Modify ****\n"); printf("**** 5.Show ***** 6.Sqrt ****\n"); printf("**** 7.Clear ***** 0.Exit ****\n"); printf("**************************************\n"); } enum Option { EXIT, ADD, DEL, SEARCH, MODIFY, SHOW, SQRT, CLEAR }; int main() { int input = 0; Contact con; //创建通讯录变量 //初始化通讯录 InitContact(&con); //要修改通讯录里面的内容要传递指针 do { menu(); printf("请选择通讯录的功能:>"); scanf("%d", &input); switch (input) { case ADD: //添加联系人 printf("添加联系人\n"); AddContact(&con); //传址调用 break; case DEL: //删除联系人 printf("删除联系人\n"); DelContact(&con); break; case SEARCH: //查找联系人 printf("查找联系人\n"); SearchContact(&con); break; case MODIFY: //修改联系人 printf("修改联系人\n"); ModifyContact(&con); break; case SHOW: //展示 ShowContact(&con); //显示通讯录不需要修改通讯录其实传值调用也可以,但是为了节省空间使用传值调用 break; case SQRT: //排序联系人 SqrtContact(&con); break; case CLEAR: //清空联系人 ClearContact(&con); break; case EXIT: printf("退出通讯录!\n"); break; default: printf("选择错误,请重新选择:>\n"); break; } } while (input); return 0; }
源文件:Contact.c
#define _CRT_SECURE_NO_WARNINGS 1 #include "Contact.h" //通讯录的实现模块 //初始化通讯录 void InitContact(Contact* pc) { pc->sz = 0; memset(pc->data, 0, sizeof(pc->data)); } //添加联系人 void AddContact(Contact* pc) { if (pc->sz == MAX) { printf("通讯录已满,不能添加!\n"); return; } printf("请输入名字:>"); scanf("%s", pc->data[pc->sz].name); printf("请输入年龄:>"); scanf("%d", &(pc->data[pc->sz].age)); printf("请输入性别:>"); scanf("%s", pc->data[pc->sz].sex); printf("请输入电话:>"); scanf("%s", pc->data[pc->sz].tele); printf("请输入地址:>"); scanf("%s", pc->data[pc->sz].addr); pc->sz++; //每一次添加完之后sz都要++ printf("添加成功\n"); } //显示通讯录 void ShowContact(const Contact* pc) { printf("%-10s %-4s %-5s %-12s %-15s\n", "名字", "年龄", "性别", "电话", "地址"); int i = 0; for (i = 0; i < pc->sz; i++) { printf("%-10s %-4d %-5s %-12s %-15s\n", pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].tele, pc->data[i].addr); } } //查找 static int FindByName(const Contact* pc, char name[]) { int i = 0; int pos = 0; for (i = 0; i < pc->sz; i++) { //找到所要联系人所在的位置 if (0 == strcmp(pc->data[i].name, name)) { return i; } } return -1; } //删除联系人 void DelContact(Contact* pc) { char name[MAX_NAME] = { 0 }; //判断通讯录是否为空 if (0 == pc->sz) { printf("通讯录为空,无法删除!\n"); return; } printf("请输入你要删除联系人的姓名:>"); scanf("%s", name); int pos = FindByName(pc, name); int i = 0; if (pos == -1) { printf("找不到所要删除的联系人\n"); return; } //进行删除 for (i = pos; i < pc->sz - 1; i++) //判断条件这里所要交换的比总数少一个 { pc->data[i] = pc->data[i + 1]; } pc->sz--; printf("删除成功\n"); } //查找联系人 void SearchContact(const Contact* pc) { char name[MAX_NAME] = { 0 }; if (pc->sz == 0) { printf("通讯录为空,查找不到!"); return; } printf("请输入你要查找联系人的姓名:>"); scanf("%s", name); int pos = FindByName(pc, name); int i = 0; if (pos == -1) { printf("找不到所要查找的联系人\n"); return; } //打印导航栏 printf("%-10s %-4s %-5s %-12s %-30s\n", "姓名", "年龄", "性别", "电话", "地址"); //打印数据 printf("%-10s %-4d %-5s %-12s %-30s\n", pc->data[pos].name, pc->data[pos].age, pc->data[pos].sex, pc->data[pos].tele, pc->data[pos].addr); } //修改联系人 void menu1() { printf("*********************************\n"); printf("******1.姓名*********2.年龄*******\n"); printf("******3.性别*********4.电话*******\n"); printf("*************5.地址**************\n"); printf("*********************************\n"); } void ModifyContact(Contact* pc) { int input = 0; char name[MAX_NAME] = { 0 }; printf("请输入你要修改的联系人姓名:>"); scanf("%s", name); //查找 int pos = FindByName(pc, name); int i = 0; if (pos == -1) { printf("找不到所要查找的联系人\n"); return; } //修改 printf("请输入要修改的具体信息:>\n"); menu1(); scanf("%d", &input); switch (input) { case 1: printf("请输入新的姓名:>"); scanf("%s", pc->data[pos].name); break; case 2: printf("请输入新的年龄:>"); scanf("%d", &pc->data[pos].age); break; case 3: printf("请输入新的性别:>"); scanf("%s", pc->data[pos].sex); break; case 4: printf("请输入新的电话:>"); scanf("%s", pc->data[pos].tele); break; case 5: printf("请输入新的地址:>"); scanf("%s", pc->data[pos].addr); break; default: printf("输入有误,修改失败\n"); return; } printf("修改成功\n"); } void menu2() { printf("***************************\n"); printf("******** 1.NAME **********\n"); printf("******** 2.AGE **********\n"); printf("***************************\n"); } //按照名字排序 int cmp_byname(const void* p1, const void* p2) { return strcmp(((PeoInfo*)p1)->name, ((PeoInfo*)p2)->name); } //按照年龄来排序 int cmp_byage(const void* p1, const void* p2) { return ((PeoInfo*)p1)->age - ((PeoInfo*)p2)->age; } //排序联系人 void SqrtContact(Contact* pc) { int input = 0; if (pc->sz == 0) { printf("通讯录没有联系人,无法排序!\n"); return; } menu2(); printf("请选择排序的对象:>"); scanf("%d", &input); switch (input) { case 1: qsort(pc, pc->sz, sizeof(PeoInfo), cmp_byname); break; case 2: qsort(pc, pc->sz, sizeof(PeoInfo), cmp_byage); break; } printf("排序成功\n"); //排序成功之后打印一下 ShowContact(pc); } //清空联系人 void ClearContact(Contact* pc) { //初始化通讯录 InitContact(pc); printf("清空成功\n"); //初始完之后再打印 ShowContact(pc); }
关于通讯录的代码就展现在这里了,但是这只是静态版本,还有很多的不足,当退出程序后下一次再进入程序我们之前的信息就回收了,还有各个数组的大小都是固定的,没办法随时随地的变化,在后面也会补发上关于通讯录的动态版本的代码,最后感谢大家的支持!谢谢