目录
1、测试结果
2、整体思路
3、结构体的定义
4、核心函数搭建
5、模块代码解读
(1)、显示菜单
(2)、初始化通讯录
(3)、给通讯录增加容量
(4)、增加联系人
(5)、查找联系人
(6)、删除联系人
(7)、修改联系人
(8)、对通讯录里面的联系人按名字进行排序
(9)、显示通讯录
(10)、保存文件
(11)、加载文件信息
(12)、释放空间
6、代码整体实现
contact.h
contact.c
test.c
今天来给大家分享一个通讯录的实现,包含了指针,结构体,动态内存分配以及文件的读写,文章有点长,完整通讯录代码在文章末尾处,希望大家能耐心看完并且有所收获哦
这是最初使用此通讯录的结果,文件里面还没有联系人名单。
将此程序退出后,我们会发现在此工程路径下多了一个文本文件,里面记录着之前的联系人信息,当再次使用本程序时,文件里面保存的联系人信息会再加载进来。请看下图:
关于里面联系人的删除,查找,排序,修改就不一一在这里演示啦,不然截图可就太长了 ,有兴趣的友友们可以拿去在自己的编译器上测试一下看看效果哦。那现在就直接进入主题吧~~
代码的整体风格呢还是和之前写的三子棋与扫雷一样,还是建了3个文件,还是先在contact.c文件里放各个函数的实现,然后在头文件contact.h中放这些函数的声明,这样在test.c文件中就可以直接引用contact.h,用contact.c中的函数啦,这样能保证主函数逻辑清晰,简洁利落,简单易懂。
因为是通讯录,要记录每个人的姓名、性别、年龄、住址、电话号码等,因此我定义了一个联系人结构体,包含一个人的各种信息。
typedef struct PeoInfo
{
char name[NAME_MAX];
int age;
char number[NUMBER_MAX];
char adress[ADDR_MAX];
char sex[SEX_MAX];
}PeoInfo;
然后为了方便管理通讯录里面的信息,我又建立了一个关于通讯录的结构体。
typedef struct Contact
{
PeoInfo* data;//存放数据
int capacity;//当前通讯录容量
int sz;//实际有效信息数量
}Contact;
enum Option
{
EXIT,
ADD,
DEL,
SEARCH,
MODIFY,
SORT,
SHOW
};
void menu()
{
printf("***************菜单****************\n");
printf("****** 1.add 2.del ******\n");
printf("****** 3.search 4.modify ******\n");
printf("****** 5.sort 6.show ******\n");
printf("****** 0.exit ******\n");
printf("***********************************\n");
}
void test()
{
Contact con;
InitContact(&con);//初始化通讯录
int input = 0;
do
{
menu();
printf("请选择->");
scanf("%d", &input);
switch (input)
{
case ADD:
//增加联系人
AddContact(&con);
break;
case DEL:
//删除联系人
DeleteContact(&con);
break;
case SEARCH:
//查找联系人
FindContact(&con);
break;
case MODIFY:
//修改联系人
ModifyContact(&con);
break;
case SORT:
//对通讯录进行排序(按名字)
SortContact(&con);
break;
case SHOW:
//显示联系人
ShowContact(&con);
break;
case EXIT:
SaveContact(&con);
DestroyContact(&con);
printf("退出通讯录\n");
break;
default:
printf("选择错误,请重新选择!\n");
break;
}
}while (input);
}
这里主要用的是do-while语句和switch语句来将各个函数串联起来,形成一个较为完整的工程。因为操作的对象是通讯录里面的信息,所以定义了一个通讯录类型的变量con。看这段代码应该不难发现基本上每一个函数的参数都是con。里面的涉及到的每一个函数我都会在下面一一讲解的。
这个太简单了,直接上代码~
void menu()
{
printf("***************菜单****************\n");
printf("****** 1.add 2.del ******\n");
printf("****** 3.search 4.modify ******\n");
printf("****** 5.sort 6.show ******\n");
printf("****** 0.exit ******\n");
printf("***********************************\n");
}
别急,菜单里面的各种功能都会在后面一一讲解的 。
因为通讯录最初是没有联系人的任何信息的,所以我们需要将它的实际有效信息数量置为0,然后申请一个PeoInfo大小的空间来存放信息,最初也是将data也就是联系人的各种信息置为空。再接着就是为设置此通讯录的容量。如果空间申请失败,则打印错误信息,方便后期的检查与维护。
但是如果不是第一次使用这个程序,则之前记录下来的联系人信息就会在启动该程序时自动加载进来,方便查看和修改。因此我利用文件知识写了一个加载信息的函数LoadContact()(别急
后面也会讲它的具体实现),在初始化时进行加载。
void InitContact(Contact* pc)
{
assert(pc);
pc->sz = 0;
PeoInfo *tmp = (PeoInfo*)malloc(DEFAULT_SZ *sizeof(PeoInfo));
if (tmp != NULL)
{
pc->data = tmp;
}
else
{
printf("InitContact()::%s\n", strerror(errno));
return;
}
pc->capacity = DEFAULT_SZ;
LoadContact(pc);
}
这个函数就是利用动态内存管理的知识来实现。每次当通讯录pc的容量capacity与实际存放的有效信息数(存放在通讯录中的人数)相等时,我就需要对通讯录进行增容,以便后续联系人的添加。也就是利用realloc()函数更改通讯录的容量(内存)。
int addcapacity(Contact* pc)
{
assert(pc);
if (pc->sz == pc->capacity)
{
PeoInfo* tmp = (PeoInfo*)realloc(pc->data, sizeof(PeoInfo)*(pc->capacity+ 2));
if (tmp != NULL)
{
pc->data = tmp;
pc->capacity += 2;
//printf("增容成功\n");
return 1;
}
else
{
printf("addcapacity()::%s\n", strerror(errno));
return 0;
}
}
return 1;
}
AddContact()这个函数是用做增添联系人的,调用这个函数就代表要在通讯录里面增加联系人信息,但是每当增添联系人信息时,都需要对通讯录的容量进行一个判断,如果通讯录满了,我就要给它进行增容,因此在AddContact()函数里面的最开始我调用了自己写的一个函数,其功能是判断通讯录是否曾容,是的话就增容,不是的话就暂时不增容,增容成功和判断为不增容都返回1,增容失败则返回0。判断通讯录的容量充足或增容成功之后才可对通讯录进行联系人的添加。
其实添加联系人很简单,就是对通讯录结构体里面的联系人类型data进行赋值,每次增添一个联系人之后通讯录pc的实际有效信息人数sz就要加一。
void AddContact(Contact* pc)
{
assert(pc);
int i=addcapacity(pc);
if (i)
{
printf("请输入姓名->");
scanf("%s", pc->data[pc->sz].name);
printf("请输入性别->");
scanf("%s", pc->data[pc->sz].sex);
printf("请输入年龄->");
scanf("%d", &(pc->data[pc->sz].age));
printf("请输入住址->");
scanf("%s", pc->data[pc->sz].adress);
printf("请输入电话->");
scanf("%s", pc->data[pc->sz].number);
pc->sz++;
printf("添加联系人成功!\n");
}
else
{
printf("AddContact::%s\n", strerror(errno));
}
}
联系人的信息都存放在联系人类型的数组里,查找联系人其实就相当于对数组的一个遍历,当输入要查找的联系人姓名时,就会对此数组进行遍历。找到就返回数组下标,找不到就返回-1。
int FindPeoInfo(char* name, Contact* pc)//查找联系人,返回其下标
{
int i = 0;
for (i = 0; i < pc->sz; i++)
{
if (*name == *(pc->data[i].name))
{
return i;
}
}
return -1;
}
删除联系人其实就是对被删除的联系人进行覆盖,将要删除的联系人后面的联系人信息依次往前移,而删除联系人之前我们需要先找到该联系人对应的数组下标,就需要调用FindPeoInfo()函数,然后再将此元素的下一个元素及后面元素依次向前移进行覆盖。覆盖完成之后,再将通讯录的实际有效信息人数sz减一。
void DeleteContact(Contact* pc)//删除联系人
{
char name[NAME_MAX] = "0";
int i = 0;
printf("请输入要删除的联系人姓名->");
scanf("%s", name);
//查找联系人
int flag=FindPeoInfo(name,pc);
if (flag != -1)
{
for (i = flag; i < pc->sz-1; i++)
{
pc->data[i] = pc->data[i + 1];
}
pc->sz--;
printf("删除成功!\n");
}
else
{
printf("没有找到您需要删除的人!\n");
}
}
修改联系人就是将原来赋值的联系人类型data数组中的元素再次赋值,达到更改的一个效果。同样的,在进行修改之前仍然要先调用FindPeoInfo()来查找要修改的联系人。
void ModifyContact(Contact* pc)//修改联系人
{
char name[NAME_MAX] = "0";
printf("请输入要修改的联系人姓名->");
scanf("%s", name);
int flag = FindPeoInfo(name, pc);
if (flag == -1)
{
printf("查无此人\n");
return;
}
else
{
int input = 0;
do
{
/*char rename[NAME_MAX] = "0";
int reage = 0;
char resex[] = "0";
char readdress[] = "0";
char*/
printf("1.姓名 2.年龄 3.性别 4.住址 5.电话 0.退出\n");
printf("请输入要修改的选项(按0退出修改)->");
scanf("%d", &input);
switch (input)
{
case 1:
printf("请输入改正后的姓名->");
scanf("%s", pc->data[flag].name);
break;
case 2:
printf("请输入改正后的年龄->");
scanf("%d", &(pc->data[flag].age));
break;
case 3:
printf("请输入改正后的性别->");
scanf("%s", pc->data[flag].sex);
break;
case 4:
printf("请输入改正后的住址->");
scanf("%s", pc->data[flag].adress);
break;
case 5:
printf("请输入改正后的电话->");
scanf("%s", pc->data[flag].number);
break;
case 0:
break;
default:
printf("输入错误,请重新输入!\n");
break;
}
} while(input);
printf("修改成功!\n");
}
}
这个也很简单,就是调用qsort()函数对结构体类型中的那字符串name进行排序。就不过多赘述啦,直接看代码~
//对联系人进行排序(按名字)
int compare_Peo(const void* e1, const void* e2)
{
return strcmp(((PeoInfo*)e1)->name,((PeoInfo*)e2)->name);
}
void SortContact(Contact* pc)
{
qsort(pc->data, pc->sz, sizeof(pc->data[0]), compare_Peo);
printf("排序成功\n");
ShowContact(pc);
}
这个函数的实现就是先判断通讯录的实际有效信息人数是否为0,如果为0的话,说明通讯录里面没有人,如果不为0,则对联系人类型的data数组进行遍历,一一打印。
void ShowContact(Contact* pc)//显示通讯录
{
assert(pc);
if (pc->sz == 0)
{
printf("通讯录里无联系人!\n");
}
else
{
int i = 0;
printf("%-7s\t%-7s\t%-6s\t%-20s\t%-15s\n", "姓名", "性别", "年龄", "住址", "电话");
for (i = 0; i < pc->sz; i++)
{
printf("%-7s\t%-7s\t%-6d\t%-20s\t%-15s\n",
pc->data[i].name, pc->data[i].sex, pc->data[i].age,
pc->data[i].adress, pc->data[i].number);
}
}
}
这个函数的功能就是将此次对通讯录里面信息的修改以二进制的形式写入文件,保存到此工程的路径下,方便下次启动此程序时查看。
//保存文件
void SaveContact(Contact* pc)
{
FILE* fp = fopen("contact1.txt", "wb");
if (fp == NULL)
{
printf("SaveContact::open for writting:%s", strerror(errno));
return;
}
//写入文件
int i = 0;
for (i = 0; i < pc->sz; i++)
{
fwrite(pc->data + i, sizeof(PeoInfo), 1, fp);
}
//关闭文件
fclose(fp);
fp = NULL;
}
当启动此程序时,我们要先对通讯录进行初始化(这一步在上面有讲过),然后再将之前保存的联系人信息加载进来,这样方便此次用此程序对之前的联系人信息进行修改查看。读文件信息时也相当于是添加联系人了,因此在读入之前也需要判断通讯录容量,看是否需要对其增容。
void LoadContact(Contact* pc)
{
//打开文件
FILE* pf = fopen("contact1.txt", "rb");
if (pf == NULL)
{
printf("LoadContact::open for reading:%s\n", strerror(errno));
return;
}
//读文件
PeoInfo tmp = { 0 };
while (fread(&tmp, sizeof(PeoInfo), 1, pf))
{
addcapacity(pc);
pc->data[pc->sz] = tmp;
pc->sz++;
}
//关闭文件
fclose(pf);
pf = NULL;
}
因为通讯录中的联系人类型的数组的空间是动态申请的,所以结束此程序时我们先用SaveContact()函数将其联系人信息保存下来,然后再将此空间释放,将指向此空间的指针置为NULL,以免被非法访问。
void DestroyContact(Contact* pc)//销毁
{
free(pc->data);
pc->data = NULL;
pc->capacity = 0;
pc->sz = 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
//#define MAX_SIZE 10
#define NAME_MAX 20
#define SEX_MAX 5
#define ADDR_MAX 30
#define NUMBER_MAX 12
#define DEFAULT_SZ 3
typedef struct PeoInfo
{
char name[NAME_MAX];
int age;
char number[NUMBER_MAX];
char adress[ADDR_MAX];
char sex[SEX_MAX];
}PeoInfo;
typedef struct Contact
{
PeoInfo* data;//存放数据
int capacity;//当前通讯录容量
int sz;//实际有效信息数量
}Contact;
void InitContact(Contact* pc);//初始化通讯录
void AddContact(Contact* pc);//增加联系人
void ShowContact(Contact* pc);//显示通讯录
void DeleteContact(Contact* pc);//删除联系人
int FindPeoInfo(char* name, Contact* pc);//查找联系人,返回其下标
void FindContact(Contact* pc);//查找联系人
void ModifyContact(Contact* pc);//修改联系人
void SortContact(Contact* pc);//对联系人进行排序(按名字)
int addcapacity(Contact* pc);//增容
void DestroyContact(Contact* pc);//销毁通讯录
void LoadContact(Contact* pc);//加载通讯录
void SaveContact(Contact* pc);//保存通讯录
#include"contact.h"
//初始化通讯录
void InitContact(Contact* pc)
{
assert(pc);
pc->sz = 0;
PeoInfo *tmp = (PeoInfo*)malloc(DEFAULT_SZ *sizeof(PeoInfo));
if (tmp != NULL)
{
pc->data = tmp;
}
else
{
printf("InitContact()::%s\n", strerror(errno));
return;
}
pc->capacity = DEFAULT_SZ;
LoadContact(pc);
}
//判断是否增容
int addcapacity(Contact* pc)
{
assert(pc);
if (pc->sz == pc->capacity)
{
PeoInfo* tmp = (PeoInfo*)realloc(pc->data, sizeof(PeoInfo)*(pc->capacity+ 2));
if (tmp != NULL)
{
pc->data = tmp;
pc->capacity += 2;
//printf("增容成功\n");
return 1;
}
else
{
printf("addcapacity()::%s\n", strerror(errno));
return 0;
}
}
return 1;
}
//加载文件
void LoadContact(Contact* pc)
{
//打开文件
FILE* pf = fopen("contact1.txt", "rb");
if (pf == NULL)
{
printf("LoadContact::open for reading:%s\n", strerror(errno));
return;
}
//读文件
PeoInfo tmp = { 0 };
while (fread(&tmp, sizeof(PeoInfo), 1, pf))
{
addcapacity(pc);
pc->data[pc->sz] = tmp;
pc->sz++;
}
//关闭文件
fclose(pf);
pf = NULL;
}
//增加联系人
void AddContact(Contact* pc)
{
assert(pc);
int i=addcapacity(pc);
if (i)
{
printf("请输入姓名->");
scanf("%s", pc->data[pc->sz].name);
printf("请输入性别->");
scanf("%s", pc->data[pc->sz].sex);
printf("请输入年龄->");
scanf("%d", &(pc->data[pc->sz].age));
printf("请输入住址->");
scanf("%s", pc->data[pc->sz].adress);
printf("请输入电话->");
scanf("%s", pc->data[pc->sz].number);
pc->sz++;
printf("添加联系人成功!\n");
}
else
{
printf("AddContact::%s\n", strerror(errno));
}
}
void ShowContact(Contact* pc)//显示通讯录
{
assert(pc);
if (pc->sz == 0)
{
printf("通讯录里无联系人!\n");
}
else
{
int i = 0;
printf("%-7s\t%-7s\t%-6s\t%-20s\t%-15s\n", "姓名", "性别", "年龄", "住址", "电话");
for (i = 0; i < pc->sz; i++)
{
printf("%-7s\t%-7s\t%-6d\t%-20s\t%-15s\n",
pc->data[i].name, pc->data[i].sex, pc->data[i].age,
pc->data[i].adress, pc->data[i].number);
}
}
}
int FindPeoInfo(char* name, Contact* pc)//查找联系人,返回其下标
{
int i = 0;
for (i = 0; i < pc->sz; i++)
{
if (*name == *(pc->data[i].name))
{
return i;
}
}
return -1;
}
void DeleteContact(Contact* pc)//删除联系人
{
char name[NAME_MAX] = "0";
int i = 0;
printf("请输入要删除的联系人姓名->");
scanf("%s", name);
//查找联系人
int flag=FindPeoInfo(name,pc);
if (flag != -1)
{
for (i = flag; i < pc->sz-1; i++)
{
pc->data[i] = pc->data[i + 1];
}
pc->sz--;
printf("删除成功!\n");
}
else
{
printf("没有找到您需要删除的人!\n");
}
}
void FindContact(Contact* pc)//查找联系人
{
char name[NAME_MAX] = "0";
printf("请输入要查找的联系人姓名->");
scanf("%s", name);
int flag = FindPeoInfo(name, pc);
if (flag != -1)
{
printf("%-7s\t%-7s\t%-6s\t%-20s\t%-15s\n", "姓名", "性别", "年龄", "住址", "电话");
printf("%-7s\t%-7s\t%-6d\t%-20s\t%-15s\n",
pc->data[flag].name, pc->data[flag].sex, pc->data[flag].age,
pc->data[flag].adress, pc->data[flag].number);
}
else
{
printf("查无此人!\n");
}
}
void ModifyContact(Contact* pc)//修改联系人
{
char name[NAME_MAX] = "0";
printf("请输入要修改的联系人姓名->");
scanf("%s", name);
int flag = FindPeoInfo(name, pc);
if (flag == -1)
{
printf("查无此人\n");
return;
}
else
{
int input = 0;
do
{
/*char rename[NAME_MAX] = "0";
int reage = 0;
char resex[] = "0";
char readdress[] = "0";
char*/
printf("1.姓名 2.年龄 3.性别 4.住址 5.电话 0.退出\n");
printf("请输入要修改的选项(按0退出修改)->");
scanf("%d", &input);
switch (input)
{
case 1:
printf("请输入改正后的姓名->");
scanf("%s", pc->data[flag].name);
break;
case 2:
printf("请输入改正后的年龄->");
scanf("%d", &(pc->data[flag].age));
break;
case 3:
printf("请输入改正后的性别->");
scanf("%s", pc->data[flag].sex);
break;
case 4:
printf("请输入改正后的住址->");
scanf("%s", pc->data[flag].adress);
break;
case 5:
printf("请输入改正后的电话->");
scanf("%s", pc->data[flag].number);
break;
case 0:
break;
default:
printf("输入错误,请重新输入!\n");
break;
}
} while(input);
printf("修改成功!\n");
}
}
//对联系人进行排序(按名字)
int compare_Peo(const void* e1, const void* e2)
{
return strcmp(((PeoInfo*)e1)->name,((PeoInfo*)e2)->name);
}
void SortContact(Contact* pc)
{
qsort(pc->data, pc->sz, sizeof(pc->data[0]), compare_Peo);
printf("排序成功\n");
ShowContact(pc);
}
void DestroyContact(Contact* pc)//空间销毁
{
free(pc->data);
pc->data = NULL;
pc->capacity = 0;
pc->sz = 0;
}
//保存文件
void SaveContact(Contact* pc)
{
FILE* fp = fopen("contact1.txt", "wb");
if (fp == NULL)
{
printf("SaveContact::open for writting:%s", strerror(errno));
return;
}
//写入文件
int i = 0;
for (i = 0; i < pc->sz; i++)
{
fwrite(pc->data + i, sizeof(PeoInfo), 1, fp);
}
//关闭文件
fclose(fp);
fp = NULL;
}
#include"contact.h"
enum Option
{
EXIT,
ADD,
DEL,
SEARCH,
MODIFY,
SORT,
SHOW
};
void menu()
{
printf("***************菜单****************\n");
printf("****** 1.add 2.del ******\n");
printf("****** 3.search 4.modify ******\n");
printf("****** 5.sort 6.show ******\n");
printf("****** 0.exit ******\n");
printf("***********************************\n");
}
void test()
{
Contact con;
InitContact(&con);//初始化通讯录
int input = 0;
do
{
menu();
printf("请选择->");
scanf("%d", &input);
switch (input)
{
case ADD:
//增加联系人
AddContact(&con);
break;
case DEL:
//删除联系人
DeleteContact(&con);
break;
case SEARCH:
//查找联系人
FindContact(&con);
break;
case MODIFY:
//修改联系人
ModifyContact(&con);
break;
case SORT:
//对通讯录进行排序(按名字)
SortContact(&con);
break;
case SHOW:
//显示联系人
ShowContact(&con);
break;
case EXIT:
SaveContact(&con);
DestroyContact(&con);
printf("退出通讯录\n");
break;
default:
printf("选择错误,请重新选择!\n");
break;
}
}while (input);
}
int main()
{
test();
return 0;
}
分享就到这里啦,怎么样,是不是很简单呢,有疑问或者看不懂的友友们可以问我哦,如果发现哪
里写的有问题或者可以改进的也可以提出来,评论区或私聊都可以,我是很希望收到大家的建议的哦