世界会向那些有目标和远见的人让路。💓💓💓
目录
•🌙说在前面
🍋基于顺序表的通讯录
• 🌰1.技术要点
• 🌰2.通讯录流程设计
🍋通讯录基本量设计
• 🌰1.定义联系人的数据结构
• 🌰2.定义通讯录顺序表
• 🌰3.通讯录菜单与主函数设计
🍋实现通讯录的基本功能
• 🌰1.通讯录初始化
• 🌰2.判断是否需要申请空间
• 🌰3.通讯录添加联系人
• 🌰4.指定联系人
• 🌰5.通讯录删除联系人
• 🌰6.通讯录修改联系人
• 🌰7.展示联系人数据
• 🌰8.通讯录查找联系人
• 🌰9.通讯录的销毁
🍋main函数构建
• 🌰1.main函数优化
• 🌰2.Contacts.h
• 🌰2.Contacts.c
• 🌰2.test.c
• ✨SumUp结语
•🌙说在前面
亲爱的读者们大家好!💖💖💖,我们又见面了,在上一文章中我们学习了顺序表的相关知识,探讨了如何用C语言实现顺序表增、删、查、改的基本操作。我们这一篇文章需要再顺序表的基础上,利用它的功能来实现通讯录的一个小项目。
如果你没有准备好的话,或者说你还没有办法独立完成顺序表功能的代码,希望你先回去看看顺序表部分的内容,确认自己没有问题之后再来看这篇文章。
博主主页传送门:愿天垂怜的博客
🍋基于顺序表的通讯录
• 🌰1.技术要点
所需知识:顺序表(动态内存管理、指针、结构体...)
基本功能要求:
🎉至少要能够存储100个人的通讯信息
🎉能够保存用户信息:名字、性别、年龄、电话、地址等
🎉删除指定联系人
🎉查找指定联系人
🎉修改指定联系人
🎉显示联系人的信息
• 🌰2.通讯录流程设计
在今后的任何项目,不论项目大小,动手写代码之前我们可以先做个设计流程图,然后按照这个流程写代码,这是个好习惯,可以使项目过程清晰明了,事半功倍。
🍋通讯录基本量设计
这一部分内容我们放在头文件Contacts.h中。
• 🌰1.定义联系人的数据结构
联系人信息至少应该包含:名字、性别、年龄、电话、地址等
#define NAME_MAX 20
#define GENDER_MAX 10//male female
#define TEL_MAX 11
#define ADDRESS_MAX 10typedef struct Person
{char name[NAME_MAX];char gender[GENDER_MAX];int age;char tel[TEL_MAX];char address[ADDRESS_MAX];
}PersonInfo;
• 🌰2.定义通讯录顺序表
基于顺序表实现通讯录,顺序表的元素类型SLDatatype就不再是int,而是包含每个联系人信息的结构体PersonInfo。
typedef PersonInfo SLDataType;//通讯录顺序表
typedef struct SeqList
{SLDataType* arr;int size;int capacity;
}Contact;
• 🌰3.通讯录菜单与主函数设计
我们的通讯录肯定是需要有一个菜单的,这样用户才能选择对通讯录执行什么样的操作,这个菜单必须包含所有通讯录功能,也包含退出。
void menu()
{printf("******************通讯录*****************\n");printf("********1.增加联系人 2.删除联系人******\n");printf("********3.修改联系人 4.查找联系人******\n");printf("********5.展示联系人 0. 退出**********\n");printf("*****************************************\n");
}
至于每一个选项具体是怎么实现的,在我们整个流程的第二大块,我们先把基本量设计完成。
现在已经把通讯录已经通讯录的元素联系人都已经定义好了,menu函数也有了,现在我们可以在test.c的main函数中把基本的框架搭建起来:
do{menu();printf("请输入你要执行的操作->\n");scanf("%d", &input);switch (input){case 1://增加联系人break;case 2://删除联系人break;case 3://修改联系人break;case 4://查找联系人break;case 5://展示联系人break;case 0:printf("退出通讯录...\n");Sleep(500);break;default:printf("输入有误,请重新输入正确的操作:\a\n");Sleep(500);break;}} while (input);
其中退出的情况我们已经可以很好的写出来了,Sleep(500)可以让程序停个0.5s,\a可以让程序发出蜂鸣声。
🍋实现通讯录的基本功能
这一部分内容我们放在文件Contacts.c中。
• 🌰1.通讯录初始化
通讯录的初始化基本和顺序表无异。
//通讯录的初始化
void Contacts_Init(Contact* pcon)
{pcon->arr = NULL;pcon->size = pcon->capacity = 0;
}
• 🌰2.判断是否需要申请空间
和顺序表一样,像我们刚初始化完的通讯录是没有申请到空间的,或者说空间已经用完了,这些情况都需要在堆上申请动态内存,所以依然需要这个函数
static void Contact_CheckCapacity(Contact* pcon)
{if (pcon->size == pcon->capacity){int NewCapacity = pcon->capacity == 0 ? 4 : 2 * pcon->capacity;SLDataType* temp = (SLDataType*)realloc(pcon->arr, NewCapacity * sizeof(SLDataType));if (temp == NULL){perror("realloc");exit(1);}else{pcon->arr = temp;pcon->capacity = NewCapacity;}}
}
需要注意的是,这个函数的功能并不是通讯录需要实现的功能,只在别的函数中判断空间时使用,所以我们用static修饰,让它隐藏在Contacts.c中,并对其他文件不可见。
• 🌰3.通讯录添加联系人
添加联系人需要先获取联系人的数据,并用PersonInfo类型的变量接收,接收后接着要让他作为通讯录的元素,我们可以用顺序表中的尾插SLPushBack。
void Contacts_ADD(Contact* pcon)
{assert(pcon);Contacts_CheckCapacity(pcon);//创建info变量接收联系人数据SLDataType info;printf("请输入要添加的联系人姓名:\n");scanf("%s", info.name);printf("请输入要添加的联系人性别:\n");scanf("%s", info.gender);printf("请输入要添加的联系人年龄:\n");scanf("%d", &info.age);printf("请输入要添加的联系人电话:\n");scanf("%s", info.tel);printf("请输入要添加的联系人住址:\n");scanf("%s", info.address);//让info尾插成为通讯录的一个元素pcon->arr[pcon->size++] = info;
}
• 🌰4.指定联系人
如果我们想先写删除联系人和查找联系人的代码,我们会发现一个问题,就是我们删除的是我们想要删除的联系人,而这个联系人我们没有定位到。所以我们此时需要一个函数用来定位到我们希望删除或者修改的联系人
static int Find_ByName(Contact* pcon, char name[])
{for (int i = 0; i < pcon->size; i++){if (!strcmp(pcon->arr[i].name, name))return i;}return -1;
}
用for循环遍历通讯录,利用名字定位,如果定位到需要操作的联系人姓名,则返回下标i进行后续操作,反之则为-1,表示没有该联系人。由于不是通讯录需要实现的功能,依然用static进行修饰。
• 🌰5.通讯录删除联系人
通过Find_ByName定位到指定联系人后,我们就可以对联系人进行删除操作,对应顺序表中指定位置删除数据的函数SLErase。
void Contacts_DEL(Contact* pcon)
{assert(pcon);char name[NAME_MAX];printf("请输入要添加的联系人姓名:");scanf("%s", name);int find = Find_ByName(pcon, name);assert(find >= 0 && find < pcon->size);if (find < 0){printf("要删除的联系人数据不存在\n");return 1;}else{//指定位置删除数据for (int i = find; i < pcon->size; i++){pcon->arr[i] = pcon->arr[i + 1];}pcon->size--;}
}
• 🌰6.通讯录修改联系人
修改联系人也需要定位到需要修改的对象,所以依然需要用到Find_ByName函数,用名字定位到了之后对其进行修改就可以了。
void Contacts_Modify(Contact* pcon)
{char name[NAME_MAX];printf("请输入要修改的联系人姓名:");scanf("%s", name);int find = Find_ByName(pcon, name);if (find < 0){printf("要修改的联系人数据不存在\n");return 1;}else{printf("请输入新的姓名:\n");scanf("%s", pcon->arr[find].name);printf("请输入新的性别:\n");scanf("%s", pcon->arr[find].gender);printf("请输入新的年龄:\n");scanf("%d", &pcon->arr[find].age);printf("请输入新的电话:\n");scanf("%s", pcon->arr[find].tel);printf("请输入新的地址:\n");scanf("%s", pcon->arr[find].address);printf("修改成功!\n");}
}
• 🌰7.展示联系人数据
理一下我们的基本量,我们的通讯录就是顺序表,联系人就是顺序表的元素,而联系人是一个结构体类型的变量,所以展示联系人数据就以为着把顺序表中的元素全部打印出来就可以了
void Contacts_Show(Contact* pcon)
{printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "电话", "地址");for (int i = 0; i < pcon->size; i++){printf("%s %s %d %s %s\n", pcon->arr[i].name,pcon->arr[i].gender,pcon->arr[i].age,pcon->arr[i].tel,pcon->arr[i].address);}
}
• 🌰8.通讯录查找联系人
想要查找联系人,也需要定位联系人的位置,如果定位到,我们打印出这个联系人的信息
void Contacts_Find(Contact* pcon)
{char name[NAME_MAX];printf("请输入要查找的联系人姓名:");scanf("%s", name);int find = Find_ByName(pcon, name);if (find < 0){printf("要修改的联系人数据不存在!\n");}else{printf("%s %s %d %s %s\n", pcon->arr[find].name,pcon->arr[find].gender,pcon->arr[find].age,pcon->arr[find].tel,pcon->arr[find].address);}
}
• 🌰9.通讯录的销毁
通讯录的销毁和顺序表如出一辙,只需要类比顺序表的销毁就可以了
void Contacts_Destroy(Contact* pcon)
{if (pcon->arr){free(pcon->arr);}pcon->arr = NULL;pcon->size = pcon->capacity = 0;
}
🍋main函数构建
• 🌰1.main函数优化
在第二部分我们已经完成了实现通讯录的基本功能的函数,现在我们需要将他们构建在main函数中。由于功能函数较多,像我们之前那样的switch语句会显得比较冗长,我们可以用函数指针数组的方式优化我们再第一部分构建的基本框架:
int main()
{void(*pf[6])(Contact*) = { 0,Contacts_ADD, Contacts_DEL, Contacts_Modify ,Contacts_Find ,Contacts_Show };Contact con;Contacts_Init(&con);int input = 0;do{menu();printf("请输入你要执行的操作->\n");scanf("%d", &input);switch (input){case 1:case 2:case 3:case 4:case 5:pf[input](&con);Sleep(500);break;case 0:printf("退出通讯录...\n");Sleep(500);break;default:printf("输入有误,请重新输入正确的操作:\a\n");Sleep(500);break;}} while (input);return 0;
}
这样做switch语句就比较简洁了,这样我们的通讯录就基本上完成了,以下给出完整代码
• 🌰2.Contacts.h
#pragma once#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>//定义联系人的数据结构#define NAME_MAX 20
#define GENDER_MAX 10//male female
#define TEL_MAX 11
#define ADDRESS_MAX 10typedef struct Person
{char name[NAME_MAX];char gender[GENDER_MAX];int age;char tel[TEL_MAX];char address[ADDRESS_MAX];
}PersonInfo;typedef PersonInfo SLDataType;//定义通讯录顺序表
typedef struct SeqList
{SLDataType* arr;int size;int capacity;
}Contact;//通讯录的初始化
void Contacts_Init(Contact* pcon);//通讯录的销毁
void Contacts_Destroy(Contact* pcon);//通讯录添加数据
void Contacts_ADD(Contact* pcon);//通讯录删除数据
void Contacts_DEL(Contact* pcon);//通讯录的修改
void Contacts_Modify(Contact* pcon);//通讯录查找
void Contacts_Find(Contact* pcon);//展示通讯录数据
void Contacts_Show(Contact* pcon);
• 🌰2.Contacts.c
#include "Contacts.h"//通讯录的初始化
void Contacts_Init(Contact* pcon)
{pcon->arr = NULL;pcon->size = pcon->capacity = 0;
}//判断是否需要申请空间
static void Contacts_CheckCapacity(Contact* pcon)
{if (pcon->size == pcon->capacity){int NewCapacity = pcon->capacity == 0 ? 4 : 2 * pcon->capacity;SLDataType* temp = (SLDataType*)realloc(pcon->arr, NewCapacity * sizeof(SLDataType));if (temp == NULL){perror("realloc");exit(1);}else{pcon->arr = temp;pcon->capacity = NewCapacity;}}
}//通讯录添加数据
void Contacts_ADD(Contact* pcon)
{assert(pcon);Contacts_CheckCapacity(pcon);//创建info变量接收联系人数据SLDataType info;printf("请输入要添加的联系人姓名:\n");scanf("%s", info.name);printf("请输入要添加的联系人性别:\n");scanf("%s", info.gender);printf("请输入要添加的联系人年龄:\n");scanf("%d", &info.age);printf("请输入要添加的联系人电话:\n");scanf("%s", info.tel);printf("请输入要添加的联系人住址:\n");scanf("%s", info.address);//让info尾插成为通讯录的一个元素pcon->arr[pcon->size++] = info;
}//指定联系人
static int Find_ByName(Contact* pcon, char name[])
{for (int i = 0; i < pcon->size; i++){if (!strcmp(pcon->arr[i].name, name))return i;}return -1;
}//通讯录删除数据
void Contacts_DEL(Contact* pcon)
{assert(pcon);char name[NAME_MAX];printf("请输入要添加的联系人姓名:");scanf("%s", name);int find = Find_ByName(pcon, name);assert(find >= 0 && find < pcon->size);if (find < 0){printf("要删除的联系人数据不存在\n");return 1;}else{//指定位置删除数据for (int i = find; i < pcon->size; i++){pcon->arr[i] = pcon->arr[i + 1];}pcon->size--;}
}//通讯录的修改
void Contacts_Modify(Contact* pcon)
{char name[NAME_MAX];printf("请输入要修改的联系人姓名:");scanf("%s", name);int find = Find_ByName(pcon, name);if (find < 0){printf("要修改的联系人数据不存在\n");return 1;}else{printf("请输入新的姓名:\n");scanf("%s", pcon->arr[find].name);printf("请输入新的性别:\n");scanf("%s", pcon->arr[find].gender);printf("请输入新的年龄:\n");scanf("%d", &pcon->arr[find].age);printf("请输入新的电话:\n");scanf("%s", pcon->arr[find].tel);printf("请输入新的地址:\n");scanf("%s", pcon->arr[find].address);printf("修改成功!\n");}
}//展示通讯录数据
void Contacts_Show(Contact* pcon)
{printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "电话", "地址");for (int i = 0; i < pcon->size; i++){printf("%s %s %d %s %s\n", pcon->arr[i].name,pcon->arr[i].gender,pcon->arr[i].age,pcon->arr[i].tel,pcon->arr[i].address);}
}void Contacts_Find(Contact* pcon)
{char name[NAME_MAX];printf("请输入要查找的联系人姓名:");scanf("%s", name);int find = Find_ByName(pcon, name);if (find < 0){printf("要修改的联系人数据不存在!\n");}else{printf("%s %s %d %s %s\n", pcon->arr[find].name,pcon->arr[find].gender,pcon->arr[find].age,pcon->arr[find].tel,pcon->arr[find].address);}
}//通讯录的销毁
void Contacts_Destroy(Contact* pcon)
{if (pcon->arr){free(pcon->arr);}pcon->arr = NULL;pcon->size = pcon->capacity = 0;
}
• 🌰2.test.c
#include "Contacts.h"void menu()
{printf("******************通讯录*****************\n");printf("********1.增加联系人 2.删除联系人******\n");printf("********3.修改联系人 4.查找联系人******\n");printf("********5.展示联系人 0. 退出**********\n");printf("*****************************************\n");
}int main()
{void(*pf[6])(Contact*) = { 0,Contacts_ADD, Contacts_DEL, Contacts_Modify ,Contacts_Find ,Contacts_Show };Contact con;Contacts_Init(&con);int input = 0;do{menu();printf("请输入你要执行的操作->\n");scanf("%d", &input);switch (input){case 1:case 2:case 3:case 4:case 5:pf[input](&con);Sleep(500);break;case 0:printf("退出通讯录...\n");Sleep(500);break;default:printf("输入有误,请重新输入正确的操作:\a\n");Sleep(500);break;}} while (input);return 0;
}
• ✨SumUp结语
通讯录的项目需要大家深刻理解并掌握顺序表的基本知识,在此基础上实现对顺序表的应用,大家也可以自己试试看着头文件实现其中各种的功能
如果大家觉得有帮助,麻烦大家点点赞,如果有错误的地方也欢迎大家指出~