前面的文章中我们介绍了好几种小游戏,比如猜数字、井字棋、扫雷等等,这些都是我们实实在在通过自己的努力学习收获而来的,也是我们向非计算机专业同学“炫技”的好帮手。但是,这些东西,好像无法拿到一些家长面前和他们分享我们的成功和喜悦,因为部分家长可能会不太认可这些小玩意儿,所以,今天带着大家实现一个有用的、拿的出手的小程序——手机通讯录,希望大家能够从中有所收获,更希望你的付出和努力能够得到家长的认可和鼓励。
目录
虽然随着社会的进步和科技的发展,传统的电话通讯逐渐被微信、QQ等新型通讯软件替代,但电话通讯目前仍是我们手机不可缺少的必备功能。而对于这种手机必备功能,相信大家对于其功能都不会陌生,至于我们今天要实现的手机通讯录简易版。
它有以下有些功能:
①添加联系人
②删除联系人
③打印通讯录
④查找联系人
⑤修改联系人
⑥分类联系人
⑦保存联系人信息至文件
?上述是今天简易实现的手机通讯录的全部功能,接下来让我们一步一步看如何实现吧!
还记得我在猜数字游戏的实现中说过的一句话吗?没错,我们在写编程题或者小游戏代码时,最重要的就是理清实现思路——主体是什么?为了实现目的要创建哪些函数?函数的功能都是什么?只有当我们心中有了一个大体的框架,知道该做些什么时,我们才能更高效地编写代码,完成程序设计。
手机通讯录的算法实现如下:
1.提供菜单并提示用户选择要进行的功能;
2.完成对应选项的功能;
3.再次提供菜单询问;
根据上面的简易思路,我们可以先写出大体的框架的代码,具体如下:
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");
}
int main()
{
int input=0;
//创建通讯录
struct Contact con;//con就是通讯录,里边包含:data指针,size和capacity
//初始化通讯录
InitContact(&con);
do
{
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:
break;
case EXIT:
//销毁通讯录-释放动态开辟的内存
SaveContact(&con);
DestroyContast(&con);
printf("退出通讯录\n");
break;
case SAVE:
SaveContact(&con);
break;
default:
printf("选择错误\n");
break;
}
}while(input);
return 0;
}
上面的代码完成了通讯录的最基本功能——提供菜单,但是对于每一项具体功能没有给出实现代码,只是起了个名字放在哪里——我称之为空壳函数,那接下来我们就逐步实现这些函数即可。
对于每一位联系人,我们肯定要存储其很多相关信息,比如姓名、性别、年龄、电话、地址等等信息,而在已知C语言提供的数据类型中没有能够满足这样需求的,所以,我们就不难想到要定义结构体类型来存放联系人信息。
结构体类型定义代码如下图所示:
typedef struct PeoInfo
{
char name[MAX_NAME];
int age;
char sex[MAX_SEX];
char tele[MAX_TELE];
char addr[MAX_ADDR];
}PeoInfo;
相信聪明的你能够轻易看出这里我们的联系人包含了姓名、年龄、性别、电话、地址这五项基本信息,当然大家也可以根据需求自行添加别的信息。
既然是通讯录,我们肯定要存放很多联系人的信息,那么我们应该建立多大的数组呢?建立的太大,我们可能用不到,反而过多占用系统资源;建立的太少,又可能不够我们使用。为了避免上面两种情况的出现,我们这里建议使用动态开辟内存,这样就可以根据所需开辟空间,提高资源利用率。
代码如下所示:
typedef struct Contact
{
struct PeoInfo *data;//存放1000个信息struct PeoInfo data[1000]
int size;//记录当前已有个数
int capacity;//当前通讯录的最大容量
}Contact;
?上面定义的结构体类型变量就是一个通讯录了,所以我们只需要定义一个结构体变量就是建立一个通讯录了。
这里并没有太多讲究,只是大家要记住要动态开辟空间。
初始化函数InitContact的代码如下:
void InitContact(struct Contact* ps)
{
ps->data=(struct PeoInfo*)malloc(DEFAULT_SZ*sizeof(struct PeoInfo));
if(ps->data==NULL)
{
return;//无法成功申请,就退出
}
ps->size=0;//设置通讯录最初有0个元素
ps->capacity=DEFAULT_SZ;//这里相当于初始时能存放的联系人个数
}
当然我这里初始化开辟的空间比较小,只能存放三个联系人,大家可以酌情增加初始大小。?
有些小伙伴可能会疑惑为什么通讯录要增容,这是干什么的?其实,在我们提供的菜单中是没有这一项功能,它是实际上是我们进行添加联系人的一个前置操作——增加通讯录容量。因为我们是动态开辟的空间,所以,在添加联系人的过程中,可能会出现空间不够用的情况,这个时候继续添加联系人,就是非法占用空间了,故我们需要在添加联系人前面加上增加容量这一操作。
增容函数CheckCapacity代码如下:
void CheckCapacity(struct Contact*ps)
{
if(ps->size == ps->capacity)
{
//增容
struct PeoInfo*ptr=realloc(ps->data,(ps->capacity+2)*sizeof(struct PeoInfo));//这里申请空间时很妙地用ps->capacity+2表示个数
if(ptr!=NULL)
{
ps->data=ptr;
ps->capacity+=2;
printf("增容成功\n");
}
else
{
printf("增容失败\n");
}
}
}
前面铺垫完毕,接下来就开始实现菜单中的各项功能,第一当仁不让是添加联系人。
添加联系人函数AddContact如下:
void AddContact(struct Contact*ps)
{
//检测当前通讯录的容量
//1.如果满了,就增加空间
//2.如果不满,啥事都不干
CheckCapacity(ps);
//增加数据
printf("请输入名字:>");
scanf("%s",ps->data[ps->size].name);
printf("请输入年龄:>");
scanf("%d",&(ps->data[ps->size].age));
printf("请输入性别:>");
scanf("%s",ps->data[ps->size].sex);
printf("请输入电话:>");
scanf("%s",ps->data[ps->size].tele);
printf("请输入地址:>");
scanf("%s",ps->data[ps->size].addr);
ps->size++;
printf("添加成功\n");
}
打印通讯录函数ShowContact如下:
void ShowContact(const struct Contact*ps)
{
if(ps->size==0)
{
printf("通讯录为空格\n");
}
else
{
int i=0;
printf("%-20s\t%-4s\t%-5s\t%-12s\t%-20s\n","名字","年龄","性别","电话","地址");
for(i=0;i<ps->size;i++)
{
printf("%-20s\t%-4d\t%-5s\t%-12s\t%-20s\n",
ps->data[i].name,
ps->data[i].age,
ps->data[i].sex,
ps->data[i].tele,
ps->data[i].addr);
}
}
}
我们在通讯录功能中有查找联系人、删除联系人、修改联系人信息等功能,但这些功能的使用前提是我们要判断该联系人是否存在,所以这里又引入一个新的铺垫性函数。
判断联系人存在函数Findbyname代码如下:
static int Findbyname(struct Contact*ps,char name[MAX_NAME])
{
static int i=0;
for(i=0;i<ps->size;i++)
{
if(0==strcmp(ps->data[i].name,name))
{
return i;
}
}
return -1;//找不到的情况
}
删除联系人函数DelContact代码如下:
void DelContact(struct Contact*ps)
{
char name[MAX_NAME];
int pos=0;
printf("请输入要删除的联系人的名字:>");
scanf("%s",name);
//1.查找要删除的人在什么位置
//找到了返回名字所在的下标
//找不到返回-1
pos=Findbyname(ps,name);
//2.删除
if(pos=-1)
{
printf("要删除的人不存在\n");
}
else
{
int j=0;
for(j=pos;j<ps->size-1;j++)
{
ps->data[j]=ps->data[j+1];
}
ps->size--;
printf("删除成功\n");
}
}
查找联系人函数SearchContact代码如下:
void SearchContact(const struct Contact*ps)
{
char name[MAX_NAME];
int pos=0;
printf("请输入要查找人的名字:>");
scanf("%s",&name);
pos=Findbyname((struct Contact*)ps,name);
if(pos==-1)
{
printf("要查找的人不存在\n");
}
else
{
printf("%-20s\t%-4s\t%-5s\t%-12s\t%-20s\n","名字","年龄","性别","电话","地址");
printf("%-20s\t%-4d\t%-5s\t%-12s\t%-20s\n",
ps->data[pos].name,
ps->data[pos].age,
ps->data[pos].sex,
ps->data[pos].tele,
ps->data[pos].addr);
}
}
修改联系人信息函数ModifyContact代码如下:
void ModifyContact(struct Contact*ps)
{
char name[MAX_NAME];//名字是字符串,放入数组
int pos=0;
printf("请输入要修改人的名字:>");
scanf("%s",name);
pos=Findbyname(ps,name);
if(pos==-1)
{
printf("要修改人的消息不存在\n");
}
else
{
printf("请输入名字:>");
scanf("%s",ps->data[ps->size].name);
printf("请输入年龄:>");
scanf("%d",&ps->data[ps->size].age);
printf("请输入性别:>");
scanf("%s",ps->data[ps->size].sex);
printf("请输入电话:>");
scanf("%s",ps->data[ps->size].tele);
printf("请输入地址:>");
scanf("%s",ps->data[ps->size].addr);
ps->size++;
printf("修改成功\n");
}
}
我们既然建立通讯录,肯定不想我们输入的信息不能保存,随着程序关闭就自动销毁,所以,这里我们要用到文件操作的相关知识,把我们输入的联系人信息保存到文件中,这样下次再打开文件联系人信息就还在,不会随程序关闭而销毁了。
保存联系人信息至文件函数SaveContact代码如下:
void SaveContact(Contact *ps)
{
FILE*pf=fopen("contact.dat","wt");
if(pf==NULL)
{
printf("%s\n",strerror(errno));
return;
}
//写通讯录中的数据到文件中
int i=0;
for(i=0;i<ps->size;i++)
{
fwrite(&ps->data[i],sizeof(PeoInfo),1,pf);
}
fclose(pf);
pf=NULL;
}
加载文件中联系人信息至程序函数LoadContact代码如下:
void LoadContact(Contact*ps)
{
PeoInfo tmp={0};
FILE*pf=fopen("contact.dat","tb");
if(pf==NULL)//打印失败pf是NULL,我们需要判断打开是否成功
{
printf("%s\n",strerror(errno));//打印打开文件失败的原因
return;
}
//读取文件,存放到通讯录中
while(fread(&tmp,sizeof(PeoInfo),1,pf))//为0前,不断读取数据
{
CheckCapacity(ps);
ps->data[ps->size]=tmp;
ps->size++;
}
fclose(pf);
pf=NULL;
}
销毁动态通讯录函数DestroyContast代码如下:
void DestroyContast(Contact*ps)
{
free(ps->data);
ps->data=NULL;
}
TongXunLu.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("*******7.save 0.exit*******\n");
printf("************************************\n");
}
int main()
{
int input=0;
//创建通讯录
struct Contact con;//con就是通讯录,里边包含:data指针,size和capacity
//初始化通讯录
InitContact(&con);
LoadContact(&con);
do
{
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:
break;
case EXIT:
//销毁通讯录-释放动态开辟的内存
SaveContact(&con);
DestroyContast(&con);
printf("退出通讯录\n");
break;
case SAVE:
SaveContact(&con);
break;
default:
printf("选择错误\n");
break;
}
}while(input);
return 0;
}
contact.h
#define MAX 1000
#define MAX_NAME 20
#define MAX_SEX 5
#define MAX_TELE 12
#define MAX_ADDR 30
#define DEFAULT_SZ 3
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<errno.h>
enum Option
{
EXIT,
ADD,
DEL,
SEARCH,
MODIFY,
SHOW,
SORT,
SAVE
};
typedef struct PeoInfo
{
char name[MAX_NAME];
int age;
char sex[MAX_SEX];
char tele[MAX_TELE];
char addr[MAX_ADDR];
}PeoInfo;
typedef struct Contact
{
struct PeoInfo *data;//存放1000个信息struct PeoInfo data[1000]
int size;//记录当前已有个数
int capacity;//当前通讯录的最大容量
}Contact;
//声明函数
//初始化通讯录的函数
void InitContact(struct Contact*ps);
//增加一个信息到通讯录
void AddContact(struct Contact*ps);
//打印通讯录中的信息
void ShowContact(const struct Contact*ps);
//删除指定的联系人
void DelContact(struct Contact*ps);
//查找指定的人的信息
void SearchContact(const struct Contact*ps);
//修改指定联系人
void ModifyContact(struct Contact*ps);
//分类指定联系人
void SortContact(Contact* ps);
//销毁通讯录——释放动态开辟的内存
void DestroyContact(Contact* ps);
//存储通讯录中的数据到文件
void SaveContact(Contact *ps);
//加载文件中的信息到通讯录
void LoadContact(Contact *ps);
contact.c
#include"contact.h"
void InitContact(struct Contact* ps)
{
ps->data=(struct PeoInfo*)malloc(DEFAULT_SZ*sizeof(struct PeoInfo));
if(ps->data==NULL)
{
return;//无法成功申请,就退出
}
ps->size=0;//设置通讯录最初有0个元素
ps->capacity=DEFAULT_SZ;//这里相当于初始时能存放的联系人个数
}
void CheckCapacity(struct Contact*ps)
{
if(ps->size == ps->capacity)
{
//增容
struct PeoInfo*ptr=realloc(ps->data,(ps->capacity+2)*sizeof(struct PeoInfo));//这里申请空间时很妙地用ps->capacity+2表示个数
if(ptr!=NULL)
{
ps->data=ptr;
ps->capacity+=2;
printf("增容成功\n");
}
else
{
printf("增容失败\n");
}
}
}
void LoadContact(Contact*ps)
{
PeoInfo tmp={0};
FILE * pf=fopen("contact.dat","tx");
if(pf == NULL)//打印失败pf是NULL,我们需要判断打开是否成功
{
printf("%s\n",strerror(errno));//打印打开文件失败的原因
return;
}
//读取文件,存放到通讯录中
while(fread(ps,sizeof(PeoInfo),1,pf))//为0前,不断读取数据
{
CheckCapacity(ps);
ps->data[ps->size]=tmp;
ps->size++;
}
fclose(pf);
pf=NULL;
}
void AddContact(struct Contact*ps)
{
//检测当前通讯录的容量
//1.如果满了,就增加空间
//2.如果不满,啥事都不干
CheckCapacity(ps);
//增加数据
printf("请输入名字:>");
scanf("%s",ps->data[ps->size].name);
printf("请输入年龄:>");
scanf("%d",&(ps->data[ps->size].age));
printf("请输入性别:>");
scanf("%s",ps->data[ps->size].sex);
printf("请输入电话:>");
scanf("%s",ps->data[ps->size].tele);
printf("请输入地址:>");
scanf("%s",ps->data[ps->size].addr);
ps->size++;
printf("添加成功\n");
}
void ShowContact(const struct Contact*ps)
{
if(ps->size==0)
{
printf("通讯录为空格\n");
}
else
{
int i=0;
printf("%-20s\t%-4s\t%-5s\t%-12s\t%-20s\n","名字","年龄","性别","电话","地址");
for(i=0;i<ps->size;i++)
{
printf("%-20s\t%-4d\t%-5s\t%-12s\t%-20s\n",
ps->data[i].name,
ps->data[i].age,
ps->data[i].sex,
ps->data[i].tele,
ps->data[i].addr);
}
}
}
int Findbyname(struct Contact*ps,char name[MAX_NAME])
{
int i=0;
for(i=0;i<ps->size;i++)
{
if(0==strcmp(ps->data[i].name,name))
{
return i;
}
}
return -1;//找不到的情况
}
void DelContact(struct Contact*ps)
{
char name[MAX_NAME];
int pos=0;
printf("请输入要删除的联系人的名字:>");
scanf("%s",name);
//1.查找要删除的人在什么位置
//找到了返回名字所在的下标
//找不到返回-1
pos=Findbyname(ps,name);
//2.删除
if(pos==-1)
{
printf("要删除的人不存在\n");
}
else
{
int j=0;
for(j=pos;j<ps->size-1;j++)
{
ps->data[j]=ps->data[j+1];
}
ps->size--;
printf("删除成功\n");
}
}
void SearchContact(const struct Contact*ps)
{
char name[MAX_NAME];
int pos=0;
printf("请输入要查找人的名字:>");
scanf("%s",&name);
pos=Findbyname((struct Contact*)ps,name);
if(pos==-1)
{
printf("要查找的人不存在\n");
}
else
{
printf("%-20s\t%-4s\t%-5s\t%-12s\t%-20s\n","名字","年龄","性别","电话","地址");
printf("%-20s\t%-4d\t%-5s\t%-12s\t%-20s\n",
ps->data[pos].name,
ps->data[pos].age,
ps->data[pos].sex,
ps->data[pos].tele,
ps->data[pos].addr);
}
}
void ModifyContact(struct Contact*ps)
{
char name[MAX_NAME];//名字是字符串,放入数组
int pos=0;
printf("请输入要修改人的名字:>");
scanf("%s",name);
pos=Findbyname(ps,name);
if(pos==-1)
{
printf("要修改人的消息不存在\n");
}
else
{
printf("请输入名字:>");
scanf("%s",ps->data[pos].name);
printf("请输入年龄:>");
scanf("%d",&ps->data[pos].age);
printf("请输入性别:>");
scanf("%s",ps->data[pos].sex);
printf("请输入电话:>");
scanf("%s",ps->data[pos].tele);
printf("请输入地址:>");
scanf("%s",ps->data[pos].addr);
ps->size++;
printf("修改成功\n");
}
}
void DestroyContast(Contact*ps)
{
free(ps->data);
ps->data=NULL;
}
void SaveContact(Contact *ps)
{
FILE*pf=fopen("contact.dat","wt");
if(pf==NULL)
{
printf("%s\n",strerror(errno));
return;
}
//写通讯录中的数据到文件中
int i=0;
for(i=0;i<ps->size;i++)
{
fwrite(&ps->data[i],sizeof(PeoInfo),1,pf);
}
fclose(pf);
pf=NULL;
}