目录
前言
模块介绍
测试模块
1.测试文件
功能实现模块
1.头文件
2.函数实现文件
附加功能
1.读写通讯录数据
在我们的手机上,无论诺基亚还是iPhone xs,总是有这样三个初始app---电话、联系人、信息,这些app的使用都离不开通讯录的功能,如图,我们可以在里面保存我们亲人朋友的联系方式,向他们打电话,发送信息等,那么在本文,笔者就大家用c语言去实现简易的通讯录程序,几乎包含了大部分c语言知识点,可以用来巩固自己所学的知识,快来看看吧。
在整个编写开始前,建议可以先写好测试文件,写完一个功能函数测试一个函数,保证思路的顺利展开,否则,若一下全写完再测试,如果出现问题,就不清楚错从何处来,会影响到自己的思路。
主函数使用了一个do while循环以及嵌套一个switch语句,可以保证程序可以在ta的选择下一直进行。
代码:
#include "contact.h"
int main()
{
int input = 0;
Contact con;
InitContact(&con);
do
{
menu();
printf("请输入选项:");
scanf("%d", &input);
switch (input)
{
case 0:
DestroyContact(&con);
printf("退出成功\n");
break;
case 1:
AddContact(&con);
break;
case 2:
DeleteContact(&con);
break;
case 3:
SearchContact(&con);
break;
case 4:
ModifyContact(&con);
break;
case 5:
SortContact(&con);
break;
case 6:
PrintContact(&con);
break;
default:
printf("输入错误\n");
break;
}
} while (input);
return 0;
}
在头文件中,可以看到,实现一个通讯录我么需要两个结构体,一个是联系人结构体,用以记录联系人的详细信息,通过预处理可以调整信息所需字符个数,另一个是通讯录结构体,用以保存多个联系人,以及已经保存的人数(haven)。
目前,我们先实现通讯录的一些基本功能,包括前端的功能,有添加删除联系人,修改联系人数据,查找联系人等,以及后端功能,有初始化、扩容、销毁通讯录功能等。
代码:
#include
#include
#include
#include
//联系人信息字符大小
#define NAME_MAX 20
#define SEX_MAX 5
#define TELE_MAX 12
//默认通讯录容量
#define DEFAULT 3
//单个联系人
typedef struct PeoInfo
{
char name[NAME_MAX];
int age;
char sex[SEX_MAX];
char tele[TELE_MAX];
}PeoInfo;
//通讯录
typedef struct Contact
{
PeoInfo* arr;
int haven;
int capacity;
}Contact;
//菜单
void menu();
//初始化通讯录
void InitContact(Contact* con);
//扩容通讯录
void CheckCapacity(Contact* con);
//销毁通讯录
void DestroyContact(Contact* con);
//打印联系人
void PrintContact(Contact* con);
//增加联系人
void AddContact(Contact* con);
//通过姓名查找指定联系人
int Find_byname(Contact* con);
//删除联系人
void DeleteContact(Contact* con);
//查找联系人
void SearchContact(Contact* con);
//修改联系人信息
void ModifyContact(Contact* con);
//排序联系人
void SortContact(Contact* con);
1)菜单
菜单的制作依旧很简单,是读者展示自己个性的地方,可以根据自己对游戏的理解装点菜单界面,让ta可以对程序的功能一目了然。
代码:
void menu()
{
printf("******************************\n");
printf("**** 0.exit 1.add ****\n");
printf("**** 2.delete 3.search ****\n");
printf("**** 4.modify 5.sort ****\n");
printf("**** 6.print ****\n");
printf("******************************\n");
}
2) 初始化通讯录
初始化通讯录,在于将人数归为0、通讯录容量归为默认(通过预处理可以调节),根据容量使用malloc为联系人表开辟地址空间。
代码:
void InitContact(Contact* con)
{
assert(con);
con->haven = 0;
con->capacity = DEFAULT;
con->arr = (PeoInfo*)malloc(sizeof(PeoInfo) * con->capacity);
if (con->arr == NULL)
{
perror("InitContact::malloc");
return;
}
}
3)扩容通讯录
在每一次添加联系人时都要检查已经存放的联系人是否达到了容量上限,判断达到上限使用realloc函数增加两个联系人的容量。
代码:
void CheckCapacity(Contact* con)
{
assert(con);
if (con->haven == con->capacity)
{
PeoInfo* tmp = (PeoInfo*)realloc(con->arr, (con->capacity + 2) * sizeof(PeoInfo));
if (tmp != NULL)
{
con->arr = tmp;
con->capacity += 2;
}
else
{
perror("CheckCapacity::realloc");
return;
}
}
}
4)销毁通讯录
因为联系人表的空间是动态开辟的,所以需要我们手动释放,并将相关数据置为空或0,当然,这里的联系人表也可以是通过数组的方式来开辟,这样就不需要自己释放了。
代码:
void DestroyContact(Contact* con)
{
free(con->arr);
con->arr = NULL;
con->capacity = 0;
con->haven = 0;
}
5)打印联系人
在结构体定义中看到,联系人的信息包括姓名,年龄等,通过循环打印即可,注意对齐等美观因素。
代码:
void PrintContact(Contact* con)
{
assert(con);
int i = 0;
printf("%-20s %-5s %-5s %-12s\n", "姓名", "年龄", "性别", "电话");
for (i = 0; i < con->haven; i++)
{
printf("%-20s %-5d %-5s %-12s\n", con->arr[i].name, con->arr[i].age, con->arr[i].sex, con->arr[i].tele);
}
}
测试:
6)增加联系人
增加联系人首先调用checkcontact函数检查容量问题,再scanf输入相应数据,注意莫忘人数要加一。
代码:
void AddContact(Contact* con)
{
assert(con);
CheckCapacity(con);
printf("请输入联系人信息:(name、age、sex、tele)\n");
scanf("%s%d%s%s", &con->arr[con->haven].name, &con->arr[con->haven].age, &con->arr[con->haven].sex, &con->arr[con->haven].tele);
con->haven++;
printf("添加成功\n");
}
测试:
7)通过姓名查找指定联系人
在以下的功能中,我们需要通过一个关键字来检索所需要的联系人,假如不重名,可以将姓名作为关键字,比如说删除某个联系人,可以通过检索其姓名找到这个人的信息将其删除,这个函数也是为了方便其他函数专门封装的函数用来检索联系人,不属于此程序的主要功能。
函数实现也是很简单,输入需要检索联系人的姓名,使用字符串比较的库函数去遍历检查,返回此联系人再联系人表中的下标,未检索到则返回-1。
代码:
int Find_byname(Contact* con)
{
assert(con);
int i = 0;
char arr[NAME_MAX];
printf("请输入联系人姓名:\n");
scanf("%s", arr);
for (i = 0; i < con->haven; i++)
{
if (strcmp(arr, con->arr[i].name) == 0)
{
return i;
}
}
return -1;
}
8)删除联系人
删除联系人,首先判断联系人表是否还有联系人,没有则直接结束,其次就是通过姓名使用Find_byname函数找到联系人在表中的位置,将其之后的所有联系人向前移
这里注意两点,①删除一个联系人之后莫忘人数减一;②在将所有之后的联系人向前移的循环中,循环结束条件为while (i < con->haven - 1),这是因为,haven-1是最后一个联系人,haven没有联系人,如果进入循环体,就会越界访问,那如果要删除haven-1的联系人怎么办?其实直接haven减一就行了,直接就排出联系人表了。
代码:
void DeleteContact(Contact* con)
{
assert(con);
if (con->haven == 0)
{
printf("通讯录内无联系人,删除失败\n");
return;
}
int pos = Find_byname(con);
if (pos != -1)
{
int i = pos;
while (i < con->haven - 1)
{
con->arr[i] = con->arr[i + 1];
i++;
}
con->haven--;
printf("删除成功\n");
}
else
printf("未查找到此联系人,删除失败\n");
}
测试:
9)查找联系人
查找联系人直接调用Find_byname函数检索打印即可。
代码:
void SearchContact(Contact* con)
{
assert(con);
int pos = Find_byname(con);
if (pos != -1)
{
printf("%-20s %-5s %-5s %-12s\n", "姓名", "年龄", "性别", "电话");
printf("%-20s %-5d %-5s %-12s\n", con->arr[pos].name, con->arr[pos].age, con->arr[pos].sex, con->arr[pos].tele);
printf("查找成功\n");
}
else
printf("未查找到此联系人\n");
}
测试:
10)修改联系人信息
修改联系人也是需要通过Find_byname函数找到此联系人在表中的位置,再次输入需要修改此联系人的什么信息,输入新的信息即可,使用while(1)就是为了用户输入了一个非法的数字能够重新输入。
代码:
void ModifyContact(Contact* con)
{
assert(con);
int pos = Find_byname(con);
if (pos != -1)
{
int i = 0;
while( 1 )
{
printf("请输入修改联系人的信息序号:\n");
printf("1.姓名 2.年龄 3.性别 4.电话\n");
scanf("%d", &i);
if (i == 1)
{
printf("请输入新名字:\n");
scanf("%s", &con->arr[pos].name);
printf("修改成功\n");
break;
}
else if (i == 2)
{
printf("请输入新年龄:\n");
scanf("%d", &con->arr[pos].age);
printf("修改成功\n");
break;
}
else if (i == 3)
{
printf("请输入新性别:\n");
scanf("%s", &con->arr[pos].sex);
printf("修改成功\n");
break;
}
else if (i == 4)
{
printf("请输入新电话:\n");
scanf("%s", &con->arr[pos].tele);
printf("修改成功\n");
break;
}
else
printf("输入错误\n");
}
}
else
printf("未查找到此联系人,修改失败\n");
}
测试:
11)排序联系人
排序联系人是所有功能里算是比较难实现的功能了,但是不要慌,难点主要在于联系人的信息什么样的数据类型都有可能有,面对不同的数据类型,该如何排序呢,这里我们使用qsort库函数(在回调函数中讲过,不熟悉的小伙伴可以去复习一下,链接:http://t.csdn.cn/ZqacA),这个函数就是针对于不同数据类型排序所研制的。
这里我们设置了可以通过联系人的姓名、年龄、性别来排序,如果还有其他数据,也可以考虑用来排序。
代码:
int com_byname(const void* p1, const void* p2)
{
return strcmp(((PeoInfo*)p1)->name, ((PeoInfo*)p2)->name);
}
int com_byage(const void* p1, const void* p2)
{
return ((PeoInfo*)p1)->age - ((PeoInfo*)p2)->age;
}
int com_bysex(const void* p1, const void* p2)
{
return strcmp(((PeoInfo*)p1)->sex, ((PeoInfo*)p2)->sex);
}
void SortContact(Contact* con)
{
assert(con);
int i = 0;
while (1)
{
printf("请输入排序联系人的信息序号:\n");
printf("1.姓名 2.年龄 3.性别\n");
scanf("%d", &i);
if (i == 1)
{
qsort(con->arr, con->haven, sizeof(con->arr[0]), com_byname);
printf("排序成功\n");
break;
}
else if (i == 2)
{
qsort(con->arr, con->haven, sizeof(PeoInfo), com_byage);
printf("排序成功\n");
break;
}
else if (i == 3)
{
qsort(con->arr, con->haven, sizeof(PeoInfo), com_bysex);
printf("排序成功\n");
break;
}
else
printf("输入错误\n");
}
}
测试: (按年龄排序)
(按姓名排)
以上功能属于通讯录的一些常见功能实现,除此之外,当我们一次程序对联系人表做出更新,也就是增删改查之后,我们退出程序再次打开时,之前保存的联系人数据就不见了,那如何解决这一问题呢?对,就是每次关闭程序之前将联系人数据保存到文件当中,下一次打开时,将保存的数据在导入进去就可以再次操作上一次的联系人数据了,这一功能用到了文件的知识点,没有学过的小伙伴可以先去补习一下哦。
12)保存通讯录数据
在退出程序之前,用户给出是否保存指令,若保存,则以"wb"的形式打开一个.txt文件,使用fwrite函数将联系人表的数据存入文件中,注意莫忘关闭文件。
代码:
void SaveContact(Contact* con)
{
int i = 0;
while(1)
{
printf("是否保存:(0/1)\n");
scanf("%d", &i);
if (i == 1)
break;
else if (i == 0)
return;
else
printf("输入错误\n");
}
FILE* pf = fopen("Contact.txt", "wb");
if (pf == NULL)
{
perror("SaveContact::fopen");
return;
}
fwrite(con->arr, sizeof(PeoInfo), con->haven, pf);
fclose(pf);
pf = NULL;
}
测试:
主函数中的参考位置:
13)上传通讯录数据
在每一次打开程序初始化通讯录之后,就要上传文件中存储的联系人数据,以"rb"的方式打开文件,使用fread函数读取文件中数据到通讯录之中。注意每导入一个联系人数据就要用checkcontact函数检查容量问题,同时注意要关闭文件。
代码:
void UploadContact(Contact* con)
{
FILE* pf = fopen("Contact.txt", "rb");
if (pf == NULL)
{
perror("UploadContact::fopen");
return;
}
PeoInfo tmp = {0};
while (fread(&tmp, sizeof(PeoInfo), 1, pf) == 1)
{
CheckCapacity(con);
con->arr[con->haven] = tmp;
con->haven++;
}
fclose(pf);
pf = NULL;
}
主函数中的参考位置:
以上就是通讯录简易实现的全部过程了,有不懂或者有疑问的小伙伴可以私我或者评论区,以及想要源码的小伙伴的也可以联系我,三连支持哦!