C语言实现通讯录管理系统(结构体+枚举+动态内存开辟+文件操作+线性表存放数据)
本篇文章将用C语言代码实现一个通讯录管理系统,本片文章博主将会运用到架构提,枚举,动态内> 存开辟和文件操作等。这里存放数据的结构是线性表。先给大家展示一张效果图
博主码云gitee链接:https://gitee.com/byte-binxin(需要源码自取)
文章图片
文章目录
- 通讯录菜单栏实现
- 线性表的创建
-
- 初始化通讯录
- main函数内部结构搭建
- 实现功能函数
- 添加联系人
- 显示通讯录
- 删除联系人
- 查找联系人
- 更改联系人信息
- 对通讯录进行排序
- 保存通讯录
- 实现加载数据功能
- 销毁通讯录
- main函数内部进行系统更改
- 总结
通讯录菜单栏实现 我们要实现的通讯录应该具备增删查改四种基本功能,还有显示,排序,退出和保存的功能。所以我们会有个功能。实现如下:
void menu()
{ printf("--------------------------------------\n");
printf("------------通讯录管理系统------------\n");
printf("------------1.添加联系人--------------\n");
printf("------------2.删除联系人--------------\n");
printf("------------3.查找联系人--------------\n");
printf("------------4.更改联系人信息----------\n");
printf("------------5.显示通讯录--------------\n");
printf("------------6.对联系人进行排序--------\n");
printf("------------7.保存通讯录--------------\n");
printf("------------0.退出通讯录--------------\n");
printf("--------------------------------------\n");
}
打印效果如下:
文章图片
线性表的创建 我们可以先创建一个结构体,里面包含联系人的各种信息。
#define NAME_MAX 20
#define TELE_MAX 12
#define SEX_MAX5
#define ADDR_MAX 50typedef struct PeoInfo
{ char name[NAME_MAX];
int age;
char sex[SEX_MAX];
char tele[TELE_MAX];
char address[ADDR_MAX];
}PeoInfo;
然后创建一个结构体,其中有用来存放通讯录的大小信息,通讯录的容量和一个结构体指针指向PeoInfo这个结构体。这个指针就可以通过动态开辟内存来调整存放信息的大小。
文章图片
typedef struct Contact
{ PeoInfo* data;
int size;
int capacity;
}Contact;
初始化通讯录 为了让通讯录能够存放数据,我们需要初始化一下通讯录,首先动态开辟一个3个(根据自己来)PepInfo结构体大小的空间,然后后续根据需求再扩大容量。还要设计一个加载函数,把上一次保存的信息加载进来,这个函数我们后面部分讲解。代码实现如下:
//初始化通讯录
void InitContact(Contact* pc)
{ assert(pc);
//开辟三个大小的空间
pc->data = https://www.it610.com/article/(PeoInfo*)malloc(sizeof(PeoInfo)* 3);
if (pc->data=https://www.it610.com/article/=NULL)
{printf("%s", strerror(errno));
exit(-1);
}
pc->size = 0;
pc->capacity = 3;
//加载数据到通讯录上
LoadContact(pc);
}
main函数内部结构搭建 我们要让用户选择对应的操作,我们应该提供对应的实现,考虑到多种选择,我们可以用switch语句,还有为了让用户多次选择,我们可以把这个选择放到一个do-while循环里面,根据用户的选择来进行相应的操作。
为了方便之后自己更好的了解哪个对应哪个选择对功能,我们可以创建一个枚举类型,这样我们就可以一目了然哪里该实现哪个功能。
enum Option
{ EXIT,
ADD,
DEL,
SEARCH,
MODIFY,
SHOW,
SORT,
SAVE
};
int main()
{ //创建一个通讯录
Contact con;
//初始化通讯录
InitContact(&con);
int input = 0;
do
{//清屏
system("cls");
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 SAVE:
//保存联系人函数
break;
case EXIT:
//销毁通讯录函数防止内存泄漏
printf("退出通讯录\n");
break;
default:
printf("选择错误\n");
system("pause");
break;
}
} while (input);
return 0;
}
【C语言实现通讯录管理系统(结构体+枚举+动态内存开辟+文件操作+线性表存放数据)】很显然,当用户选择0是就可以退出这个循环了,也就退出了这个程序。
实现功能函数
//初始化通讯录
void InitContact(Contact* pc);
//增加联系人
void AddContact(Contact* pc);
//删除联系人
void DelContact(Contact* pc);
//查找联系人
void SearchContact(Contact* pc);
//更改联系人信息
void ModifyContact(Contact* pc);
//显示联系人
void ShowContact(Contact* pc);
//对通讯录进行排序
void SortContact(Contact* pc);
//保存通讯录
void SaveContact(Contact* pc);
//销毁通讯录
void DestoryContact(Contact* pc);
添加联系人 添加联系人要考虑通讯录容量的问题,所以每次添加联系人的时候就要检查一下通讯录的先有容量,看是否需要扩容,如果通讯录现有容量和size相同,说明通讯录满了,就要扩容了。扩容需要用到realloc函数来扩大data指针的指向空间的大小。
void CheckCapacity(Contact* pc)
{ if (pc->capacity == pc->size)
{PeoInfo* tmp = (PeoInfo*)realloc(pc->data, sizeof(PeoInfo)*(pc->capacity * 2));
if (tmp == NULL)
{printf("%s", strerror(errno));
exit(-1);
}
pc->data = https://www.it610.com/article/tmp;
pc->capacity *= 2;
printf("扩容成功\n");
}
}
这样就实现了检查通讯录是否需要扩容的函数。
void AddContact(Contact* pc)
{ assert(pc);
//检查容量
CheckCapacity(pc);
printf("请输入你要添加人的姓名:>");
scanf("%s", pc->data[pc->size].name);
printf("请输入你要添加人的性别:>");
scanf("%s", pc->data[pc->size].sex);
printf("请输入你要添加人的年龄:>");
scanf("%d", &(pc->data[pc->size].age));
printf("请输入你要添加人的电话号码:>");
scanf("%s", pc->data[pc->size].tele);
printf("请输入你要添加人的家庭住址:>");
scanf("%s", pc->data[pc->size].address);
pc->size++;
printf("增加成功\n");
system("pause");
}
显示通讯录 为了让我们更好的观察添加联系人这个功能和其他的功能是否很好的实现了,我们先来实现一个显示通讯录函数来观察一下。
先判断参数,如果通讯录为空就打印一下通讯录为空,然后退出,否则就显示联系人,先打印通讯录信息。
打印之前还要寻找用户要查找的联系人是存在,我们先实现查找联系人的函数。
int FindPeopleByName(const Contact* pc, char name[])
{ int i = 0;
for (i = 0;
i < pc->size;
i++)
{if (strcmp(pc->data[i].name, name) == 0)
{return i;
}
}
return -1;
}
我们为了好看,可以搭建一个表头,通过循环来控制,可以自己根据训练次数来调节
文章图片
可以通过格式化输出的"-""%x"来对齐控制,具体看一下如下代码。
//显示联系人
void ShowContact(Contact* pc)
{ assert(pc);
if (pc->size == 0)
{printf("通讯录为空\n");
system("pause");
return;
}
//打印横线
int j = 0;
for (j = 0;
j < 21;
j++)
{printf("——");
}
printf("\n");
printf("|%42s\t\t\t\t\t\t\b\b\b\b\b|\n", "通讯录");
for (j = 0;
j < 21;
j++)
{printf("——");
}
printf("\n");
//打印标题
printf("|%-15s|%-5s|%-5s|%-12s|%-42s\b|\n", "姓名", "性别", "年龄", "电话", "家庭住址");
//打印信息
int i = 0;
for (i = 0;
i < pc->size;
i++)
{for (j = 0;
j < 21;
j++)
{printf("——");
}
printf("\n");
printf("|%-15s|%-5s|%-5d|%-12s|%-42s\b|\n",
pc->data[i].name,
pc->data[i].sex,
pc->data[i].age,
pc->data[i].tele,
pc->data[i].address);
}
for (j = 0;
j < 21;
j++)
{printf("——");
}
printf("\n");
system("pause");
}
下面我们先来看一下添加联系人功能的效果:
文章图片
删除联系人 删除联系人的第一步首先要检查参数,看通讯录是否为空,空就返回,无法删除,不为空就要查找联系人,看这个联系人是否存在,在哪个位置,这些都是我们需要考虑的,查找联系人的函数在上面我们已经实现过来,这里就不过多介绍。
接下来就是删除了,有两种方式:
第一种相对来说比较简单,就是就通讯录中最后一个人挪动到要删除的那个人的位置,但这样就有一个缺点就是通讯录的顺序发生了改变。
另一种方法相对来说比较麻烦,但其实也很简单,就是把钥删除的位置后面的联系人都往前挪动一个就可以了,具体实现如下:
//删除联系人
void DelContact(Contact* pc)
{ assert(pc);
if (pc->size == 0)
{printf("通讯录为空\n");
system("pause");
return;
}
else
{char name[NAME_MAX];
printf("请输入你要删除联系人的姓名:>");
scanf("%s", name);
//查找联系人
int pos = FindPeopleByName(pc,name);
if (pos == -1)
{printf("该联系人不存在\n");
printf("删除失败\n");
system("pause");
}
else
{//删除
int i = 0;
for (i = pos;
i < pc->size - 1;
i++)
{pc->data[i] = pc->data[i + 1];
}pc->size--;
printf("删除成功\n");
system("pause");
}
}
}
动态效果图如下:
文章图片
查找联系人 查找联系人就是由要用到上面的查找函数,找到后把这个信息打印一遍就可以了。
//查找联系人
void SearchContact(Contact* pc)
{ assert(pc);
if (pc->size == 0)
{printf("通讯录为空\n");
system("pause");
return;
}
else
{char name[NAME_MAX];
printf("请输入你要查找联系人的姓名:>");
scanf("%s", name);
//查找联系人
int pos = FindPeopleByName(pc, name);
if (pos == -1)
{printf("该联系人不存在\n");
printf("查找失败\n");
system("pause");
}
else
{printf("查找成功\n");
//打印
int j = 0;
for (j = 0;
j < 21;
j++)
{printf("——");
}
printf("\n");
//打印标题
printf("|%-15s|%-5s|%-5s|%-12s|%-42s\b|\n", "姓名", "性别", "年龄", "电话", "家庭住址");
for (j = 0;
j < 21;
j++)
{printf("——");
}
printf("\n");
printf("|%-15s|%-5s|%-5d|%-12s|%-42s\b|\n",
pc->data[pos].name,
pc->data[pos].sex,
pc->data[pos].age,
pc->data[pos].tele,
pc->data[pos].address);
for (j = 0;
j < 21;
j++)
{printf("——");
}
printf("\n");
system("pause");
}
}
}
动态效果演示如下:
文章图片
更改联系人信息 我们先创建一个简易的菜单栏来提示用户要对哪个人的哪个信息进行修改。
void ModifyMenu()
{ printf("---------------------------\n");
printf("-----1.姓名2.性别-----\n");
printf("-----3.年龄4.电话-----\n");
printf("-----5.家庭住址 0.退出-----\n");
printf("---------------------------\n");
}
然后我们可以在ModifyContact函数里面调用,与主函数内部的框进搭建有相似之处,我们来一起看看代码是如何实现的:
//更改联系人信息
void ModifyContact(Contact* pc)
{ assert(pc);
if (pc->size == 0)
{printf("通讯录为空\n");
system("pause");
return;
}
else
{char name[NAME_MAX];
printf("请输入你要更改的联系人的姓名:>");
scanf("%s", name);
//查找联系人
int pos = FindPeopleByName(pc, name);
if (pos == -1)
{printf("该联系人不存在\n");
printf("更改信息失败\n");
system("pause");
}
else
{int input = 0;
do
{system("cls");
ModifyMenu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{case 1:
printf("请输入修改后的姓名:>");
scanf("%s", pc->data[pos].name);
printf("修改成功\n");
system("pause");
break;
case 2:
printf("请输入修改后的性别:>");
scanf("%s", pc->data[pos].sex);
printf("修改成功\n");
system("pause");
break;
case 3:
printf("请输入修改后的年龄:>");
scanf("%d", &(pc->data[pos].age));
printf("修改成功\n");
system("pause");
break;
case 4:
printf("请输入修改后的电话:>");
scanf("%s", pc->data[pos].tele);
printf("修改成功\n");
system("pause");
break;
case 5:
printf("请输入修改后的家庭住址:>");
scanf("%s", pc->data[pos].address);
printf("修改成功\n");
system("pause");
break;
case 0:
printf("退出修改\n");
system("pause");
break;
default:
printf("选择错误\n");
system("pause");
break;
}
} while (input);
}
}
}
修改的过程其实都很简单,就是对要修改的信息进行重新录制一遍。
一起来看看动态演示图:
文章图片
对通讯录进行排序 这里也可以创建一个小的菜单栏,让用户选择是通过年龄来进行排序还是通过名字进行排序。
void SortMenu()
{ printf("-------------------------\n");
printf("-----1.姓名2.年龄-----\n");
printf("-------------------------\n");
}
这里排序我们用到的是qsort函数来进行排序,之前我讲过一篇关于这个函数的博客qsort函数排序,大家可以看一下。
int cmp_contact_by_name(const void*e1, const void*e2)
{ return strcmp(((struct PeoInfo*)e1)->name, ((struct PeoInfo*)e2)->name);
}int cmp_contact_by_age(const void*e1, const void*e2)
{ return ((struct PeoInfo*)e1)->age - ((struct PeoInfo*)e2)->age;
}//对通讯录进行排序
void SortContact(Contact* pc)
{ assert(pc);
if (pc->size == 0)
{printf("通讯录为空,无法进行排序\n");
system("pause");
return;
}
else
{int input = 0;
do
{system("cls");
SortMenu();
printf("请选择你要用什么信息进行排序:>");
scanf("%d", &input);
switch (input)
{case 1:
qsort(pc->data, pc->size, sizeof(PeoInfo), cmp_contact_by_name);
break;
case 2:
qsort(pc->data, pc->size, sizeof(PeoInfo), cmp_contact_by_age);
break;
default:
printf("选择错误\n");
system("pause");
break;
}
} while (input != 1 && input != 2);
}
printf("排序成功\n");
system("pause");
}
我还是给大家演示一下动图:
文章图片
保存通讯录 保存通讯录的实现我用到了文件操作,我们可以先在项目目录底下先创建一个“contact.dat”文件来存储数据,保存数据时还要用到一个fwrite函数,将通讯录中的信息写入到文件中。
文章图片
int i = 0;
for (i = 0;
i < pc->size;
i++)
{ fwrite(&(pc->data[i]), sizeof(PeoInfo), 1, pfWrite);
}
我们写入方式是从data指向的线性表中每次读取一个数据到文件中。
//保存通讯录
void SaveContact(Contact* pc)
{ assert(pc);
FILE* pfWrite = fopen("contact.dat", "wb");
if (pfWrite == NULL)
{printf("SaveContact::%s\n", strerror(errno));
exit(-1);
} int i = 0;
for (i = 0;
i < pc->size;
i++)
{fwrite(&(pc->data[i]), sizeof(PeoInfo), 1, pfWrite);
} fclose(pfWrite);
pfWrite = NULL;
printf("保存成功\n");
system("pause");
}
动态演示一下:
文章图片
实现加载数据功能 为了让我们下一次打开程序是可以看到上一次存进去的数据,我们要把文件上的数据读写到通讯录中,所以我们要在初始化通讯录的时候把这些数据加载进去。
这里要用到一个fread函数,
文章图片
while (fread(&tmp, sizeof(PeoInfo), 1, pfRead))
{ CheckCapacity(pc);
pc->data[pc->size] = tmp;
pc->size++;
}
把文件上的数据一个一个地读取到通讯录中,由于fread函数的返回类型是size_t,读取了几个信息就返回几,当此次没有读取任何信息是,会返回一个0,所以我们可以利用fread函数的这个特性来作为循环结束的标志,读取的过程相当于在添加联系人,所以我们要不断的检查通讯录容量,看是否需要扩容。
//加载数据
void LoadContact(Contact* pc)
{ PeoInfo tmp;
FILE* pfRead = fopen("contact.dat", "rb");
if (pfRead == NULL)
{printf("LoadContact::%s\n", strerror(errno));
return;
}
while (fread(&tmp, sizeof(PeoInfo), 1, pfRead))
{CheckCapacity(pc);
pc->data[pc->size] = tmp;
pc->size++;
}
fclose(pfRead);
pfRead = NULL;
}
销毁通讯录 为了防止内存泄漏,我们要在程序结束的时候把开辟内存空间释放一下。
//销毁通讯录
void DestoryContact(Contact* pc)
{ assert(pc);
free(pc->data);
pc->data = https://www.it610.com/article/NULL;
}
简单的几行代码就能实现了。
main函数内部进行系统更改 把销毁函数和保存放在退出程序的那,就可以防止内存泄漏了,还可以避免用户使用通讯录后忘记保存数据造成数据丢失。
int main()
{ //创建一个通讯录
Contact con;
//初始化通讯录
InitContact(&con);
int input = 0;
do
{system("cls");
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{case ADD:
AddContact(&con);
break;
case DEL:
DelContact(&con);
break;
case SEARCH:
SearchContact(&con);
break;
case MODIFY:
ModifyContact(&con);
break;
case SHOW:
ShowContact(&con);
break;
case SORT:
SortContact(&con);
break;
case SAVE:
SaveContact(&con);
break;
case EXIT:
SaveContact(&con);
DestoryContact(&con);
printf("退出通讯录\n");
break;
default:
printf("选择错误\n");
system("pause");
break;
}
} while (input);
return 0;
}
总结 这篇博客就先介绍到这了,希望大家有所收获。欢迎大家点赞支持和指正~
文章图片
推荐阅读
- 关于QueryWrapper|关于QueryWrapper,实现MybatisPlus多表关联查询方式
- MybatisPlus使用queryWrapper如何实现复杂查询
- python学习之|python学习之 实现QQ自动发送消息
- 【生信技能树】R语言练习题|【生信技能树】R语言练习题 - 中级
- 孩子不是实现父母欲望的工具——林哈夫
- 一起来学习C语言的字符串转换函数
- C语言字符函数中的isalnum()和iscntrl()你都知道吗
- opencv|opencv C++模板匹配的简单实现
- C语言浮点函数中的modf和fmod详解
- Node.js中readline模块实现终端输入