文章目录
前言
在学完结构体、动态内存管理、文件操作之后,我们可以用C语言来实现一个通讯录,通讯录的功能有:
1.可以存储足够数量的联系人,每个联系人的信息包括:姓名、年龄、性别、住址、电话;
2.功能有:增加、删除、查找、修改、查看、保存、排序联系人。
3.联系人信息保存在电脑文件中,下次程序运行联系人信息仍存在。
一、创建文件
对于这种具有实际应用功能的程序,我们采用分模块的编程思想,创建三个文件来分别存放对应功能的代码。
test.c:主程序,功能的调用
contact.c:通讯录的具体功能实现
contact.h:工程需要的各种头文件、函数声明以及宏定义
这样做的好处:提高代码的可读性,方便后续调试,条理清晰,使主程序看起来简洁
二、test.c文件
menu()打印菜单
0~7选择:退出、增加、删除、查找、修改、查看、手动保存、排序。
void menu()
{
printf("******************************\n");
printf("***** 1.add 2.del ****\n");
printf("***** 3.search 4.modify ****\n");
printf("***** 5.show 6.sort ****\n");
printf("***** 7.save 0.exit ****\n");
printf("******************************\n");
}
main()主函数
int main()
{
int input = 0;
Contact con;
InitCon(&con);
do
{
menu();
printf("请选择->:");
scanf("%d", &input);
switch (input)
{
case EXIT:
SaveCon(&con);
DestroyCon(&con);
printf("已退出通讯录\n");
break;
case ADD:
AddCon(&con);
break;
case DEL:
DelCon(&con);
break;
case SEARCH:
SearchCon(&con);
break;
case MODIFY:
MoldifyCon(&con);
break;
case SHOW:
ShowCon(&con);
break;
case SORT:
SortCon(&con);
break;
case SAVE:
SaveCon(&con);
break;
default:
printf("输入错误,请重新输入->:\n");
break;
}
} while (input);
return 0;
}
分支过多,使用枚举,方便理解
enum Choose
{
EXIT, //0
ADD, //1
DEL, //2
SEARCH,//3
MODIFY,//4
SHOW, //5
SORT, //6
SAVE //7
};
三、contact.h文件
头文件
#pragma once//防止头文件重复调用
#include<stdio.h>
#include<string.h>
#include<assert.h>
#include<errno.h>
定义联系人和通讯录
//存放联系人信息
typedef struct Pepe
{
char name[Name_Max];
int age;
char sex[Sex_Max];
char addr[Addr_Max];
char tele[Tele_Max];
}Pepe;
//通讯录 结构体嵌套
typedef struct Contact
{
Pepe* data; //指向存放人的信息的空间
int sz; //当前已经存放联系人的个数
int capacity;//当前通讯录的最大容量
}Contact;
宏定义变量
方便随时一次性修改,不用再从函数内部修改
#define Name_Max 15
#define Sex_Max 10
#define Addr_Max 30
#define Tele_Max 12
#define Init_Sz 10 //通讯录初始开辟空间大小
#define Inc_Sz 5 //每次空间不够 新增空间大小
主要功能函数的声明
采用址传递,避免结构体过大使得临时拷贝负担过重,从而导致性能下降。
//初始化通讯录
void InitCon(Contact* pc);
//释放空间
void DestroyCon(Contact* pc);
//增加联系人
void AddCon(Contact* pc);
//显示打印通讯录信息
void ShowCon(const Contact* pc);
//删除联系人
void DelCon(Contact* pc);
//查找指定联系人
void SearchCon(const Contact* pc);
//修改指定联系人
void MoldifyCon(Contact* pc);
//按姓名排序通讯录
void SortCon(struct Contact* ps);
//保存通讯录信息到文件中
void SaveCon(Contact* pc);
//加载文件信息到通讯录
void LoadCon(Contact* pc);
四、contact.c文件
初始化通讯录
在写函数的时候,先采用断言处理,防止指针为空从而导致后续操作非法,同时方便找错误。
每次运行程序,通讯录都会自动读取上次保存的内容。
void InitCon(Contact* pc)
{
assert(pc);
pc->sz = 0;//当前联系人个数为0
Pepe* ptr = (Pepe*)calloc(Init_Sz, sizeof(Pepe));//动态开辟空间,并初识化0
if (ptr == NULL)
{
perror("InitCon::calloc\n");
return;
}
pc->data = ptr;
pc->capacity = Init_Sz;
//每次运行程序,加载文件信息到通讯录
LoadCon(pc);
}
释放通讯录空间
每次手动退出通讯录,要将开辟的空间释放掉,防止造成内存泄漏。
void DestroyCon(Contact* pc)
{
free(pc->data);
pc->data = NULL;
pc->sz = pc->capacity = 0;//个数和容量清零
pc = NULL; //pc不是动态开辟,只用置空,不用free
}
增加联系人
每次增加联系人时,首先检查容量是否已满,若已满则新增容量,避免越界访问。
void AddCon(Contact* pc)
{
assert(pc);
check_capacity(pc);//检查容量
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].addr);
printf("请输入电话->:");
scanf("%s", pc->data[pc->sz].tele);
pc->sz++;
}
判断开辟的空间是否已满,若满了则新开辟Inc_Sz个空间,避免浪费。
void check_capacity(Contact* pc)
{
if (pc->sz == pc->capacity)//联系人个数和容量相等
{
//增加容量
Pepe* ptr = (Pepe*)realloc(pc->data, (pc->capacity + Inc_Sz) * sizeof(Pepe));
if (ptr == NULL)
{
perror("check_capacity::realloc\n");
return;
}
pc->data = ptr;
pc->capacity += Inc_Sz;
}
}
删除联系人
后面会用到的子函数:通过联系人姓名找到下标
int Find_Name(Contact* pc, char name[])
{
for (int i = 0; i < pc->sz; i++)
{
if (strcmp(pc->data[i].name, name) == 0)
{
return i;
}
}
return -1;
}
通过名字找到下标,再将该坐标后面的所有联系人往前移一位
void DelCon(Contact* pc)
{
assert(pc);
char name[Name_Max] = { 0 };
if (pc->sz == 0)
{
printf("通讯录为空,无法删除\n");
return;
}
printf("请输入需要删除的联系人姓名\n");
scanf("%s", name);
int pos = Find_Name(pc, name);//通过名字查找联系人,并返回下标
if (pos == -1)
{
printf("要删除的联系人不存在\n");
return;
}
for (int i = pos; i < pc->sz - 1; i++)
{
pc->data[i] = pc->data[i + 1];
}
pc->sz--;
printf("删除成功\n");
}
查找联系人
通过名字找到联系人下标并打印信息。
void SearchCon(const Contact* pc)
{
assert(pc);
char name[Name_Max] = { 0 };
printf("请输入需要查找的联系人姓名\n");
scanf("%s", name);
int pos = Find_Name(pc, name);//通过名字查找联系人,并返回下标
if (pos == -1)
{
printf("要查找的联系人不存在\n");
return;
}
printf("已查找到该联系人\n");
printf("%-15s\t%-4s\t%-10s\t%-20s\t%-12s\n",
"姓名", "年龄", "性别", "地址", "电话");
printf("%-15s\t%-4d\t%-10s\t%-20s\t%-12s\n",
pc->data[pos].name,
pc->data[pos].age,
pc->data[pos].sex,
pc->data[pos].addr,
pc->data[pos].tele);
}
修改联系人
void MoldifyCon(Contact* pc)
{
assert(pc);
char name[Name_Max] = { 0 };
printf("请输入需要修改的联系人姓名\n");
scanf("%s", name);
int pos = Find_Name(pc, name);
if (pos == -1)
{
printf("要修改的联系人不存在\n");
return;
}
printf("请输入修改后联系人的信息:\n");
printf("请输入名字->:");
scanf("%s", pc->data[pos].name);
printf("请输入年龄->:");
scanf("%d", &pc->data[pos].age);
printf("请输入性别->:");
scanf("%s", pc->data[pos].sex);
printf("请输入地址->:");
scanf("%s", pc->data[pos].addr);
printf("请输入电话->:");
scanf("%s", pc->data[pos].tele);
}
查看所有联系人
每增加一个函数模块,记得进行打印调试。
void ShowCon(const Contact* pc)
{
assert(pc);
printf("%-15s\t%-4s\t%-10s\t%-20s\t%-12s\n", //负号代表左对齐,数字代表间距
"姓名", "年龄", "性别", "地址", "电话");
for (int i = 0; i < pc->sz; i++)
{
printf("%-15s\t%-4d\t%-10s\t%-20s\t%-12s\n",
pc->data[i].name,
pc->data[i].age,
pc->data[i].sex,
pc->data[i].addr,
pc->data[i].tele);
}
}
按名字排序通讯录
通过名字的字典序对联系人进行排序并打印
int cmp_name(const void* e1, const void* e2)
{
return strcmp(((Pepe*)e1)->name, ((Pepe*)e2)->name);
}
void SortCon(struct Contact* pc)
{
assert(pc);
if (pc->sz == 0)
{
printf("通讯录中没有联系人,请添加!\n");
return;
}
qsort(pc->data, pc->sz, sizeof(Pepe), cmp_name);
printf("排序成功!\n");
ShowCon(pc);
}
保存和加载联系人
选择7手动保存联系人信息到文件中或者选择0退出通讯录自动保存
void SaveCon(Contact* pc)
{
FILE* pf = fopen("contact.txt", "wb");//以“只写”的方式 打开二进制文件
if (pf == NULL)
{
perror("fopen");
}
else
{
for (int i = 0; i < pc->sz; i++)
{
//将每个联系人信息保存到文件中
fwrite(&pc->data[i], sizeof(Pepe), 1, pf);
}
fclose(pf);
pf = NULL;
printf("保存成功!\n");
}
}
每次运行程序,从文件加载联系人,并存到通讯录中;
因为每次只加载一次,所以放在初始化函数中一起执行。
void LoadCon(Contact* pc)
{
FILE* pf = fopen("contact.txt", "rb");//以“只读”的方式 打开二进制文件
if (pf == NULL)
{
perror("fopen");
}
else
{
Pepe tmp = { 0 };
int i = 0;
while (fread(&tmp, sizeof(Pepe), 1, pf)) //每次从文件中读取一个联系人加载到通讯录中
{
check_capacity(pc);
pc->data[i++] = tmp;
pc->sz++;
}
}
fclose(pf);
pf = NULL;
}
五、功能演示
添加联系人
打印所有联系人
查找联系人
修改联系人
删除联系人
按名字排序联系人
删除联系人
选择7保存联系人或者选择0退出通讯录并自动保存,下次运行程序自动加载联系人信息
联系人信息以二进制文本形式存到电脑文件中
六、完整代码
完整代码上传在gitee
以上就是通讯录的全部功能了,有些地方可能不是很完善,细心的小伙伴可以帮忙指正哦。
对您有用的话可以三连支持博主,您的点赞和关注是我最大的动力,感谢您的观看和支持!