这篇文章是对模拟实现通讯录的补充
在之前的模拟实现通讯录中升级了两个功能
1.实现通讯录内存的动态管理
2.以文件的形式保存了通讯录,保证在下一次运行代码时能找到上一次保存的联系人
功能一、通讯录内存的动态管理
在实现动态管理时,就需要使用malloc和realloc函数来实现内存的灵活运用
我们假设初始化通讯录时,通讯录只能存储3个联系人,如果容量不够,就使用realloc函数实现增容。
因为涉及到增容,那么在创建通讯录结构体时就需要改变
//动态通讯录的版本
//创建结构体 (通讯录),成员变量是联系信息和联系人个数
typedef struct Contact
{
PeoInfo* data;//联系人类型的结构体指针,指向的是calloc函数开辟的空间
int num;//用来标记存储的联系人个数
int sz;//用来标记容量
}Contact;
那么初始化通讯录同样做出改变
// 动态版本初始化通讯录
void InitContact(Contact* pc)
{
pc->num = 0;//开始时通讯录中的联系人个数为0
pc->sz = 3;//通讯录初始容量为3
pc->data = (PeoInfo*)calloc(3, sizeof(PeoInfo));//向内存申请3个PeoInfo类型的空间
if (pc == NULL)
{
perror("InitContact->calloc");
return;
}
//初始化通讯录之后,把之前文件中的联系人添加到通讯录中
LoadContact(pc);
}
在通讯录的功能中涉及到容量文体的还有增加联系人
//检查通讯录是否需要增容
void CheckCapacity(Contact* pc)
{
//现在的通讯录不会满,但是要判断联系人个数和容量是否相等,相等就增容
if (pc->num == pc->sz)
{
PeoInfo* ptr = (PeoInfo*)realloc(pc->data, sizeof(PeoInfo) * (pc->sz + 2));
if (ptr != NULL)
{
pc->data = ptr;
}
else
{
perror("CheckCapacity->realloc");
return;
}
pc->sz = (pc->sz) + 2;
printf("增容成功\n");
}
}
//动态版本增加联系人
void AddContact(Contact* pc)
{
//检查通讯录是否需要增容
CheckCapacity(pc);
printf("请输入联系人姓名:");
scanf("%s", pc->data[pc->num].name);
//pc->data 找到存储联系人信息的数组[pc->num]找到这个数组要添加的联系人的下标
//在.name访问结构体成员变量
printf("请输入联系人年龄:");
scanf("%d", &pc->data[pc->num].age);
printf("请输入联系人性别:");
scanf("%s", pc->data[pc->num].sex);
printf("请输入联系人电话:");
scanf("%s", pc->data[pc->num].tele);
printf("请输入联系人住址:");
scanf("%s", pc->data[pc->num].addr);
pc->num++;
printf("添加成功\n");
}
因为涉及到动态内存的申请,所以在退出通讯录之前也要先释放内存
//销毁通讯录
void DestroyContact(Contact* pc)
{
free(pc->data);
pc->data = NULL;
pc->num = 0;
pc->sz = 0;
}

功能二、文件的形式保存通讯录
如果要以文件的形式保存,那么就要在退出通讯录时,也就是销毁通讯录之前先把通讯录保存在文件中
//把通讯录信息保存在文件上
void SaveContact(Contact* pc)
{
//打开文件
FILE* pf = fopen("contact.txt", "wb");
if (pf == NULL)
{
perror("SaveContact fopen");
return;
}
//向文件写入信息
size_t ch = 0;
int i = 0;
for (i = 0; i < pc->num; i++)//向文件中一个一个的写入信息
{
fwrite(pc->data+i, sizeof(PeoInfo), 1, pf);
}
//关闭文件
fclose(pf);
pf = NULL;
}
同时我们也要对初始化通讯录做出改变,在以保证在下一次运行代码时,已经把文件中的信息读到通讯录当中
//把文件中的联系人添加到通讯录中
void LoadContact(Contact* pc)
{
//先检查是否需要增容
CheckCapacity(pc);
//打开文件
FILE* pf = fopen("contact.txt", "rb");
if (pf == NULL)
{
perror("LoadContact->fopen");
return;
}
//从文件读出
size_t ch = 0;
PeoInfo ex={0};
while (ch = fread(&ex, sizeof(PeoInfo), 1, pf) != 0)
{
//先检查是否需要增容
CheckCapacity(pc);
pc->data[pc->num] = ex;
pc->num++;
}
//关闭文件
fclose(pf);
pf = NULL;
}
// 动态版本初始化通讯录
void InitContact(Contact* pc)
{
pc->num = 0;//开始时通讯录中的联系人个数为0
pc->sz = 3;//通讯录初始容量为3
pc->data = (PeoInfo*)calloc(3, sizeof(PeoInfo));//向内存申请3个PeoInfo类型的空间
if (pc == NULL)
{
perror("InitContact->calloc");
return;
}
//初始化通讯录之后,把之前文件中的联系人添加到通讯录中
LoadContact(pc);
}
如图
在第一次运行时添加了四位联系人胡英俊、牛爷爷、胡图图、和壮壮,
并输入0退出通讯录以保证四位联系人被写到文件中

关闭运行窗口,第二次运行时直接输入5,显示联系人就可以看到联系人,说明联系人被从文件读出

总体代码如下
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("*** 0.exit ****\n");
printf("************************\n");
}
enum Option //用枚举列举出所有的功能,并且EXIT的默认值为0,依次往上递增,与菜单一致
{
EXIT,//退出通讯录
ADD,//添加联系人
DEL,//删除联系人
SEARCH,//查找联系人
MODIFY,//修改联系人
SHOW,//显示所有联系人
SORT//对所有联系人排序
};
int main()
{
int input = 0;
//创建通讯录变量
Contact con;
//对通讯录变量初始化
InitContact(&con);
//当文件中写入了联系人后,在下一次加载通讯录时一个把文件中的联系人读到通讯录中,所以在初始化时加载出来
do
{
menu();
printf("请输入选择\n");
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 EXIT:
//在释放内存之前,要先把信息保存到文件上
SaveContact(&con);
//释放内存
DestroyContact(&con);
printf("退出通讯录\n");
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
return 0;
}
contact.h文件
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
//创建结构体(联系人),成员变量是联系人的各种信息
typedef struct PeoInfo
{
char name[20];
int age;
char sex[5];
char tele[15];
char addr[30];
}PeoInfo;
////静态通讯录的版本
////创建结构体 (通讯录),成员变量是联系信息和联系人个数
//typedef struct Contact
//{
// PeoInfo data[100];//结构体数组,
// int num;//用来标记存储的联系人个数
//}Contact;
//动态通讯录的版本
//创建结构体 (通讯录),成员变量是联系信息和联系人个数
typedef struct Contact
{
PeoInfo* data;//联系人类型的结构体指针,指向的是calloc函数开辟的空间
int num;//用来标记存储的联系人个数
int sz;//用来标记容量
}Contact;
//初始化通讯录
void InitContact(Contact* pc);
//增加联系人
void AddContact(Contact* pc);
//显示联系人
void ShowContact(Contact* pc);
//删除联系人
void DelContact(Contact* pc);
//查找联系人
void SearchContact(Contact* pc);
//修改联系人
void ModifyContact(Contact* pc);
//给联系人排序
void SortContact(Contact* pc);
//将通讯录保存在文件上
void SaveContact(Contact* pc);
//销毁通讯录
void DestroyContact(Contact* pc);
contact.c文件
#include"contact.h"
//// 静态版本初始化通讯录
//void InitContact(Contact* pc)
//{
// pc->num = 0;
// memset(pc->data, 0, sizeof(pc->data));//pc->data就是数组名data,是指针
//}
//检查通讯录是否需要增容
void CheckCapacity(Contact* pc);
//把文件中的联系人添加到通讯录中
void LoadContact(Contact* pc)
{
//先检查是否需要增容
CheckCapacity(pc);
//打开文件
FILE* pf = fopen("contact.txt", "rb");
if (pf == NULL)
{
perror("LoadContact->fopen");
return;
}
//从文件读出
size_t ch = 0;
PeoInfo ex={0};
while (ch = fread(&ex, sizeof(PeoInfo), 1, pf) != 0)
{
//先检查是否需要增容
CheckCapacity(pc);
pc->data[pc->num] = ex;
pc->num++;
}
//关闭文件
fclose(pf);
pf = NULL;
}
// 动态版本初始化通讯录
void InitContact(Contact* pc)
{
pc->num = 0;//开始时通讯录中的联系人个数为0
pc->sz = 3;//通讯录初始容量为3
pc->data = (PeoInfo*)calloc(3, sizeof(PeoInfo));//向内存申请3个PeoInfo类型的空间
if (pc == NULL)
{
perror("InitContact->calloc");
return;
}
//初始化通讯录之后,把之前文件中的联系人添加到通讯录中
LoadContact(pc);
}
//销毁通讯录
void DestroyContact(Contact* pc)
{
free(pc->data);
pc->data = NULL;
pc->num = 0;
pc->sz = 0;
}
////静态版本增加联系人
//void AddContact(Contact* pc)
//{
// //增加前判断通讯录是否满了
// if (pc->num == 100)
// {
// printf("通讯录满了,无法增加\n");
// return;
// }
// //没满就开始增加
// printf("请输入联系人姓名:");
// scanf("%s", pc->data[pc->num].name);
// //pc->data 找到存储联系人信息的数组[pc->num]找到这个数组要添加的联系人的下标
// //在.name访问结构体成员变量
//
// printf("请输入联系人年龄:");
// scanf("%d", &pc->data[pc->num].age);
//
// printf("请输入联系人性别:");
// scanf("%s", pc->data[pc->num].sex);
//
// printf("请输入联系人电话:");
// scanf("%s", pc->data[pc->num].tele);
//
// printf("请输入联系人住址:");
// scanf("%s", pc->data[pc->num].addr);
// pc->num++;
// printf("添加成功\n");
//}
//检查通讯录是否需要增容
void CheckCapacity(Contact* pc)
{
//现在的通讯录不会满,但是要判断联系人个数和容量是否相等,相等就增容
if (pc->num == pc->sz)
{
PeoInfo* ptr = (PeoInfo*)realloc(pc->data, sizeof(PeoInfo) * (pc->sz + 2));
if (ptr != NULL)
{
pc->data = ptr;
}
else
{
perror("CheckCapacity->realloc");
return;
}
pc->sz = (pc->sz) + 2;
printf("增容成功\n");
}
}
//动态版本增加联系人
void AddContact(Contact* pc)
{
//检查通讯录是否需要增容
CheckCapacity(pc);
printf("请输入联系人姓名:");
scanf("%s", pc->data[pc->num].name);
//pc->data 找到存储联系人信息的数组[pc->num]找到这个数组要添加的联系人的下标
//在.name访问结构体成员变量
printf("请输入联系人年龄:");
scanf("%d", &pc->data[pc->num].age);
printf("请输入联系人性别:");
scanf("%s", pc->data[pc->num].sex);
printf("请输入联系人电话:");
scanf("%s", pc->data[pc->num].tele);
printf("请输入联系人住址:");
scanf("%s", pc->data[pc->num].addr);
pc->num++;
printf("添加成功\n");
}
//显示联系人
void ShowContact(Contact* pc)
{
//先判断通讯录是否有联系人
if (pc->num == 0)
{
printf("通讯录无联系人,无法显示\n");
return;
}
//有联系人,显示
int i = 0;
printf("%-20s %-4s %-5s %-15s %-30s\n", "name", "age", "sex", "telephone", "address");
for (i = 0; i < pc->num; i++)
{
printf("%-20s %-4d %-5s %-15s %-30s\n",
pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].tele, pc->data[i].addr);
}
}
int Find_by_name(Contact* pc, char name[])
{
int i = 0;
for (i = 0; i < pc->num; i++)
{
if (*name == *(pc->data[i].name))
{
return i;
}
}
//出了循环就是没有找到
return -1;
}
//删除联系人
void DelContact(Contact* pc)
{
//先判断通讯录有没有联系人
if (pc->num == 0)
{
printf("通讯录无联系人,无法删除\n");
return;
}
char name[20];
printf("请输入想要删除的联系人姓名\n");
scanf("%s", name);
//先遍历通讯录,看有没有这个人的名字
int rst1 = Find_by_name(pc, name);
if (rst1 == -1)
{
printf("通讯录无该联系人\n");
return;
}
// 为了不改变通讯录联系人顺序,要把后面的联系人依次往前放
int i = 0;
//这样只能改变存在的前num-1个联系人的,如果要删除联系人是最后一个,也就不用改变顺序了
for (i = rst1; i < pc->num - 1; i++)
{
pc->data[i] = pc->data[i + 1];
}
//删除后联系人数量减一
pc->num--;
printf("删除成功\n");
}
//查找联系人
void SearchContact(Contact* pc)
{
//判断通讯录不为空
if (pc->num == 0)
{
printf("通讯录无联系人,无法查找\n");
return;
}
char name[20];
printf("请输入要查找的联系人姓名");
scanf("%s", name);
//先遍历通讯录,看有没有这个人的名字
int rst = Find_by_name(pc, name);
if (rst == -1)
{
printf("通讯录无该联系人\n");
return;
}
//有这个联系人就显示这个联系人的所有信息
printf("%-20s %-4s %-5s %-15s %-30s\n", "name", "age", "sex", "telephone", "address");
printf("%-20s %-4d %-5s %-15s %-30s\n",
pc->data[rst].name, pc->data[rst].age, pc->data[rst].sex, pc->data[rst].tele, pc->data[rst].addr);
}
//修改联系人
void ModifyContact(Contact* pc)
{
//判断通讯录不为空
if (pc->num == 0)
{
printf("通讯录无联系人,无法查找\n");
return;
}
char name[20];
printf("请输入要修改的联系人姓名");
scanf("%s", name);
//先遍历通讯录,看有没有这个人的名字
int rst = Find_by_name(pc, name);
if (rst == -1)
{
printf("通讯录无该联系人\n");
return;
}
//有这个联系人就开始修改
printf("请输入联系人姓名:");
scanf("%s", pc->data[rst].name);
printf("请输入联系人年龄:");
scanf("%d", &pc->data[rst].age);
printf("请输入联系人性别:");
scanf("%s", pc->data[rst].sex);
printf("请输入联系人电话:");
scanf("%s", pc->data[rst].tele);
printf("请输入联系人住址:");
scanf("%s", pc->data[rst].addr);
printf("修改成功\n");
}
//e1和e2是数组的相邻的两个元素,结构体数组的一个元素就有多个成员变量
int my_compar(const char* e1, const char* e2)
{
return strcmp(((Contact*)e1)->data->name, ((Contact*)e2)->data->name);
}
//给联系人排序
void SortContact(Contact* pc)
{
//判断通讯录不为空
if (pc->num == 0)
{
printf("通讯录无联系人,无法查找\n");
return;
}
//按名字的首字母的顺序排序
size_t sz = sizeof(pc->data[0]);
qsort(pc->data, pc->num, sz, my_compar);
}
//把通讯录信息保存在文件上
void SaveContact(Contact* pc)
{
//打开文件
FILE* pf = fopen("contact.txt", "wb");
if (pf == NULL)
{
perror("SaveContact fopen");
return;
}
//向文件写入信息
size_t ch = 0;
int i = 0;
for (i = 0; i < pc->num; i++)//向文件中一个一个的写入信息
{
fwrite(pc->data+i, sizeof(PeoInfo), 1, pf);
}
//关闭文件
fclose(pf);
pf = NULL;
}
4656

被折叠的 条评论
为什么被折叠?



