实现一个通讯录;
通讯录可以用来存储1000个人的信息,每个人的信息包括:姓名、性别、年龄、电话、住址
通讯录功能:
- 添加联系人信息
- 删除指定联系人信息
- 查找指定联系人信息
- 修改指定联系人信息
- 以年龄排序所有联系人
- 显示所有联系人信息
首先声明一下,我们这里写的通讯录是开辟动态内存空间实现的。在实现通讯录之前我们需要做一些"铺垫"。
1.使用结构体来声明联系人的基本信息typedef struct PepoInFo
{
char name[NAME_MAX];
char sex[SEX_MAX];
int age;
char tele[TELE_MAX];
char addr[ADDR_MAX];
}PepoInFo;
结构体中数组的大小可以用define来定义,便于修改,以下是我自己定义的数组大小
#define NAME_MAX 20
#define SEX_MAX 5
#define TELE_MAX 12
#define ADDR_MAX 30
2.使用结构体来声明通讯录
typedef struct contact
{
PepoInFo* data;//接收存放信息空间的指针
int sz;//已经存放的个数
int capacity;//申请空间个数
}contact;
1.(PepoInFo*类型)data指向的是动态开辟出的通讯录空间(最大存储1000个联系人信息)
2. sz表示当前通讯录中存储联系人的个数
3. capacity表示当前通讯录的存储大小(若sz=capacity,则增容,每次增加2个联系人容量)
3.用枚举来给通讯录菜单中的各个功能赋值(便于提高代码的可读性)enum menu
{
add = 1,
del,
search,
modify,
sort,
display
};
一.主函数的实现
int main()
{
test();
return 0;
}
将通讯录包装在test()函数中
void test()
{
int input = 0;
contact con;//创建通讯录
InitContact(&con);//初始化通讯录
do
{
menu();
printf("请选择>:");
scanf("%d", &input);
switch (input)
{
case add:
AddContact(&con);// 添加联系人
Sleep(1500);
system("cls");//清空屏幕
break;
case del:
DelContact(&con);//删减联系人
Sleep(1500);
system("cls");
break;
case search:
SearchContact(&con);//查找联系人
break;
case modify:
ModifyContact(&con);//修改联系人
Sleep(1500);
system("cls");
break;
case sort:
SortContact(&con);//排序所有联系人---(按年龄排序)
Sleep(1500);
system("cls");
break;
case display:
DisplayContact(&con);//显示所有联系人
break;
case 0:
RestoreContact(&con);//将通讯录数据存储文本中
FreeContact(&con);//释放内存空间
printf("退出通讯录\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
} while (input);
}
通讯录菜单
void menu()
{
printf("************************************\n");
printf("**** 1.add 2.del ****\n");
printf("**** 3.search 4.modify ****\n");
printf("**** 5.sort 6.display ****\n");
printf("**** 0.exit ****\n");
printf("************************************\n");
}
test()函数中首先创建了一个通讯录变量(contact类型) con,然后使用InitContact()函数来初始化通讯录,接下来就使用do while()循环和switch来实现通讯录的各个功能(增加、删减、查找、修改、排序、打印)。接下来我们来一一编写通讯录的各项功能
1.通讯录的初始化
typedef struct contact
{
PepoInFo* data;//接收存放信息空间的指针
int sz;//已经存放的个数
int capacity;//申请空间个数
}contact;
通讯录的初始化是:
1.将通讯录结构中的sz=0(通讯录中暂无任何联系人)
2.将capacity=3(当前通讯录中最多可以存储3个联系人信息,后期会不断增容)
3.data指向由calloc函数动态开辟出的一个空间,其空间内容全部初始化为0
void InitContact(contact* va)
{
assert(va != NULL);
va->sz = 0;
va->capacity = DEFAULT_SZ;
PepoInFo* pf = (PepoInFo*)calloc(va->capacity, sizeof(PepoInFo));
if (pf == NULL)
{
perror("InitContact::calloc");
return;
}
va->data = pf;
//将原来存储在文本的信息加载到通讯录中
LoadContact(va);
}
LoadContact()函数是将文本中的信息加载到通讯录中(后面会详细介绍)
2.通讯录各个功能的实现通讯录有6个功能(增加、删减、查找、修改、排序、打印)
在介绍这6个功能之前,我们先要实现两个函数FindName()和 IncreaseCapacity()
FindName()可以判定联系人是否存在及快速定位联系人在通讯录中的位置
IncreaseCapacity()是增容函数---当通讯录中联系人的个数等于当前通讯录的最大容量时就可以增容(每次增加2个联系人容量)
FindName()FindName()中的char arr[]是name数组,我们可以通过输入姓名来判定该联系人是否存在,以及他在通讯录中的位置,便于我们后期各个功能的实现。
int FindName(contact* ve, char arr[])
{
int i = 0;
for (i = 0; i < ve->sz; i++)
{
if (0 == strcmp(arr, ve->data[i].name))
return i;
}
return -1;
}
IncreaseCapacity()
data重新指向由realloc开辟的增容空间,capacity每次增容后增加2个存储单位。
void IncreaseCapacity(contact* va)
{
//当目前通讯录容量(va->sz)等于要存储的最大容量时(va->capacity)
//最大容量就需要增容(每次增容2个存储单位)
if (va->sz == va->capacity)
{
PepoInFo* pa = (PepoInFo*)realloc(va->data, (va->capacity + 2) * sizeof(PepoInFo));
if (pa != NULL)
{
va->data = pa;
}
else
{
perror("IncreaseCapacity::realloc");
return;
}
va->capacity += 2;
printf("增容成功!\n");
}
}
通讯录中的6大功能函数
//添加联系人函数
void AddContact(contact* vb);
//显示所有联系人函数
void DisplayContact(contact* vc);
//查找联系人函数
void SearchContact(contact* vd);
//删减联系人函数
void DelContact(contact* vf);
//修改联系人信息函数
void ModifyContact(contact* ve);
//排序所有联系人函数
void SortContact(contact* vg);
1.添加联系人函数
AddContact()
在添加新的联系人之前需要判断是否达到通讯录的最大存储容量(最大存储1000个联系人),达到最大容量就不能再增加新的联系人。
再判断是否达到通讯录当前的最大容量(capacity),达到当前的最大容量就使用 IncreaseCapacity()函数进行增容。
接下来就依次输入联系人的基本信息。
最后(当前通讯录中联系人的个数)sz+1
void AddContact(contact* vb)
{
assert(vb != NULL);
if (vb->sz != MAX)//超过通讯录最大存储容量(1000)时就不再增容
{
IncreaseCapacity(vb);
}
else
{
printf("通讯录已满,无法添加!\n");
return;
}
printf("请输入姓名>:");
scanf("%s", vb->data[vb->sz].name);
printf("请输入性别>:");
scanf("%s", vb->data[vb->sz].sex);
printf("请输入年龄>:");
scanf("%d", &vb->data[vb->sz].age);
printf("请输入电话号码>:");
scanf("%s", vb->data[vb->sz].tele);
printf("请输入地址>:");
scanf("%s", vb->data[vb->sz].addr);
vb->sz++;
printf("添加成功\n");
}
2.删减联系人函数
DelContact()
在删减联系人之前需要先判断当前通讯录中联系人的个数,若联系人个数为0就不能删减。
然后通过之前FindName()函数来判断输入的联系人是否存在通讯录中,若不存在也无法删减。
最后若联系人存在,我们可以通过不断的将后一位联系人的信息覆盖在它的前一位来达到删减的效果。最后还需要sz-1(保持通讯录中联系人的个数正确)
void DelContact(contact* vf)
{
assert(vf != NULL);
if (0 == vf->sz)
{
printf("通讯录已空,无法删除\n");
return;
}
char name1[20] = { 0 };
printf("请输入删除人的姓名>:");
scanf("%s", name1);
int row = FindName(vf, name1);
if (-1 == row)
{
printf("联系人不存在,无法删除\n");
return;
}
else
{
for (int i = row; i < vf->sz - 1; i++)
{
vf->data[i] = vf->data[i + 1];
}
}
vf->sz--;
printf("联系人已删除\n");
}
3.查找联系人函数
SearchContact()
通过之前的FindName()函数可判断输入的联系人是否存在,若存在则输入他的准确位置
通过FindName()函数的输出值打印该联系人的基本信息(注意对齐)
void SearchContact(contact* vd)
{
assert(vd != NULL);
char name1[20] = { 0 };
printf("请输入联系人姓名>:");
scanf("%s", name1);
int row = FindName(vd, name1);
if (-1 == row)
{
printf("联系人不存在\n");
return;
}
else
{
printf("%-15s %-5s %-5s %-12s %-30s\n", "姓名", "性别", "年龄", "电话号码", "地址");
printf("%-15s %-5s %-5d %-12s %-30s\n", vd->data[row].name, vd->data[row].sex, vd->data[row].age, vd->data[row].tele, vd->data[row].addr);
}
}
4.修改联系人信息函数ModifyContact()
在创建该函数之前我们需要先创建一个修改选项的菜单和一个声明菜单的枚举。
接下来先判断通讯录中是否存在联系人,若不存在则无法修改。
然后我们通过FindName()函数来获取输入的联系人的准确位置,借用do while()和switch函数来修改自己想修改的信息。
最后修改成功并退出。
//创建一个修改选项的菜单
void Menu()
{
printf("****************************\n");
printf("**** 1.NAME 2.SEX ****\n");
printf("**** 3.AGE 4.TELE ****\n");
printf("**** 5.ADDR 0.exit ****\n");
printf("****************************\n");
}
//用枚举来声明该菜单
enum menu1
{
NAME=1,
SEX,
AGE,
TELE,
ADDR
};
void ModifyContact(contact* ve)
{
assert(ve != NULL);
if (0 == ve->sz)
{
printf("通讯录目前为空,无法修改\n");
return;
}
char name1[NAME_MAX];
char sex1[SEX_MAX];
int age1;
char tele1[TELE_MAX];
char addr1[ADDR_MAX];
printf("请输入修改人的姓名>:");
scanf("%s", name1);
int row = FindName(ve, name1);
if (row == -1)
printf("联系人不存在\n");
int input = 0;
do
{
Menu();
printf("请选择需要修改的选项>:");
scanf("%d", &input);
switch (input)
{
case NAME:
printf("请输入修改后的姓名>:");
scanf("%s", name1);
strcpy(ve->data[row].name, name1);
break;
case SEX:
printf("请输入修改后的性别>:");
scanf("%s", sex1);
strcpy(ve->data[row].sex, sex1);
break;
case AGE:
printf("请输入修改后的年龄>:");
scanf("%d", &age1);
ve->data[row].age = age1;
break;
case TELE:
printf("请输入修改后的电话号码>:");
scanf("%s", tele1);
strcpy(ve->data[row].tele, tele1);
break;
case ADDR:
printf("请输入修改后的地址>:");
scanf("%s", addr1);
strcpy(ve->data[row].addr, addr1);
break;
case 0:
printf("修改成功,退出程序\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
} while (input);
}
5.排序所有联系人函数SortContact()
利用快速排序函数qsort来排序(qsort函数的使用可以在网上查找,本章暂不介绍如何使用)
//自定义比较函数----(对年龄排序)
int cmp_age(const void* e1, const void* e2)
{
return ((PepoInFo*)e1)->age-((PepoInFo*)e2)->age;
}
void SortContact(contact* vg)
{
assert(vg != NULL);
//使用快速排序函数来排序
qsort(vg->data, vg->sz, sizeof(PepoInFo), cmp_age);
printf("排序成功\n");
}
6.显示所有联系人函数DisplayContact()
注意对齐
void DisplayContact(contact* vc)
{
int i = 0;
printf("%-15s %-5s %-5s %-12s %-30s\n", "姓名", "性别", "年龄", "电话号码", "地址");
for (i = 0; i < vc->sz; i++)
{
printf("%-15s %-5s %-5d %-12s %-30s\n", vc->data[i].name, vc->data[i].sex, vc->data[i].age, vc->data[i].tele, vc->data[i].addr);
}
}
以上就是通讯录6大功能的函数
接下来我们将介绍 1.将通讯录的数据存放在文本中 2.从文本中读取数据存放在通讯录中 3.通讯录的空间销毁在test()函数中当我们要退出通讯录时会经过两个函数RestoreContact()和FreeContact()
RestoreContact()----------将通讯录数据存储文本中。
FreeContact()----------------------------释放内存空间。
通过介绍RestoreContact()和FreeContact()函数,你就会明白通讯录的空间是如何销毁的和通讯录的数据如何存放在文本中。
FreeContact()因为通讯录的空间是动态内存开辟的,因此在使用完通讯录后需要销毁通讯录的空间(防止内存泄漏)。
使用free()函数对data所指向的空间进行释放,再将data置为NULL
再将通讯录中的sz和capacity也全部变为0
void FreeContact(contact* va)
{
free(va->data);
va->data = NULL;
va->sz = 0;
va->capacity = 0;
printf("销毁成功\n");
}
RestoreContact()
将通讯录数据存储文本中,需要使用fopen、fwrite、fclose等函数(不清楚的小伙伴,可以去看下一章我写的文本 *** 作,里面详细介绍了这些函数的说明)
我们创建一个(FILE*类型)变量pb指向由fopen函数创建的一个文本空间"contact.con"中,输入指令"wb"是将数据以二进制的方式存储在文本中
通过fwrite()函数将通讯录中的信息写入pb指向的空间中
最后通过fclose()函数关闭pb指向的空间,并将pb置为NULL
//将通讯录数据存储文本中的函数
void RestoreContact(contact* vh)
{
//打开文件
FILE* pb = (FILE*)fopen("contact.con", "wb");
if (pb == NULL)
{
perror("RestoreContact::fopen");
return;
}
//写文件
int i = 0;
for (i = 0; i < vh->sz; i++)
{
fwrite(vh->data + i, sizeof(PepoInFo), 1, pb);
}
//关闭文件
fclose(pb);
pb = NULL;
printf("保存成功!\n");
}
LoadContact()
与RestoreContact()函数相同,LoadContact()函数也需要通过创建一个(FILE*类型)变量pc指向由fopen函数创建的一个文本空间"contact.con"中,输入指令"rb"是将数据以二进制的方式从文本中读取出来
再创建一个(PepoInFo类型)变量tmp,通过fread()函数将pc指向文本空间中的数据存放在tmp变量中。
需要注意的是因为fread()函数的返回值是读取数据的个数,我们通过该函数的返回值来作为循环停止的条件。每次读取一个(PepoInFo类型)的数据,当读完最后一个数据时,下一次读取就会返回0,循环结束。
最后通过fclose()函数关闭pc指向的空间,并将pc置为NULL
//加载文本信息函数
void LoadContact(contact* va)
{
//打开文件
FILE* pc = (FILE*)fopen("contact.con", "rb");
if (pc == NULL)
{
perror("LoadContact::fopen");
return;
}
//读文件
PepoInFo tmp = { 0 };
while (fread(&tmp, sizeof(PepoInFo), 1, pc))
{
//先扩容
IncreaseCapacity(va);
va->data[va->sz] = tmp;
va->sz++;
}
//关闭文件
fclose(pc);
pc = NULL;
printf("保存成功!\n");
}
以上就是整个通讯录的实现模板,然后通讯录的源码可以通过下面的链接进行访问,如果对各位小伙伴有帮助的话,希望点赞收藏。c语言: C语言 - Gitee.comhttps://gitee.com/haomana/c-language/tree/master/C%E8%AF%AD%E8%A8%80%E4%BB%93%E5%BA%93/c-language/test%204_15
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)