文章目录
通讯录的实现
1.整体布局
1.1 打印菜单
通讯录中的菜单就类似于去餐馆吃饭时,餐桌上的菜单清晰明了展示各种饭菜。通讯录中的菜单也是此番效果,可以快速选择通讯录的功能。
void menu()
{
printf("**********************************************\n");
printf("**** 1.add 2.rearch ******\n");
printf("**** 3.del 4.modify ******\n");
printf("**** 5.show 6.sort ******\n");
printf("**** 0.exit ******\n");
printf("**********************************************\n");
}
1.2 创建有关联系人的结构体
结构体peoinfo用于记录联系人的各种信息
结构体contact就是通讯录,其中包含有关联系人的结构体数组和当前联系人的个数
//联系人的个人信息
typedef struct peoinfo
{
char name[name_MAX];
int age;
char sex[sex_MAX];
char tele[tele_MAX];
char addr[addr_MAX];
}peoinfo;
//通讯录
typedef struct contact
{
peoinfo data[MAX];
int count;//记录当前通讯录中联系人的个数
}contact;
1.3 定义全局变量
#define MAX 100//联系人的数量
#define name_MAX 20//姓名的长度
#define sex_MAX 10//性别
#define tele_MAX 12//电话的长度
#define addr_MAX 15//地址的长度
1.4 菜单功能的实现
利用switch函数,通过不同的数字选择实现不同的菜单功能
int main()
{
int input = 0;
contact con;//创建通讯录
Initcontact(&con);//初始化通讯录
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
addcontact(&con);//添加联系人
break;
case 2:
delcontact(&con);//删除联系人
break;
case 3:
searchcontact(&con);//查找联系人
break;
case 4:
modifycontact(&con);//修改联系人信息
break;
case 5:
showcontact(&con);//显示联系人信息
break;
case 6:
sortcontact(&con);//对联系人进行排序
break;
case 0:
printf("退出通讯录\n");
break;
default:
printf("输入错误\n");
break;
}
} while (input);
return 0;
}
2.通讯录的实现
2.1 创建通讯录变量
通过结构体contact创建通讯录变量con
其中包括数组peoinfo data[MAX] 和 count
contact con;
2.2 初始化通讯录
这里需要介绍一个内存操作函数
memset-内存设置函数
可以将指定的内存内存设置为自己指定的内容
将ptr指向的内存块的前几个num字节设置为指定值(解释为无符号char)
void Initcontact(contact* p)
{
assert(p);
p->count = 0;
memset(p->data, 0, sizeof(p->data));
}
初始化运行结果
2.3 添加联系人
通过菜单选择添加联系人功能。
首先需要判断通讯录中联系人数量是否已满,
若已满则不能继续添加联系人;
若未满可通过指针p逐个访问结构体contact中的变量进行添加信息
void addcontact(contact* p)
{
assert(p);
//判断通讯录中联系人数量是否已满
if (p->count == MAX)
{
printf("数量已满,不能添加联系人\n");
return;
}
//未满
printf("请输入待添加联系人姓名:>");
scanf("%s", p->data[p->count].name);
printf("请输入待添加联系人年龄:>");
scanf("%d", &(p->data[p->count].age));//int类型需要取地址
printf("请输入待添加联系人性别:>");
scanf("%s", p->data[p->count].sex);
printf("请输入待添加联系人电话:>");
scanf("%s", p->data[p->count].tele);
printf("请输入待添加联系人地址:>");
scanf("%s", p->data[p->count].addr);
printf("添加成功\n");
p->count++;
}
2.4 查找联系人
这里需要创建一个查找函数Find_name通过自己输入的姓名在通讯录中查找联系人,若没找到则返回-1;若找到则返回该联系人在通讯录中所对应位置的下标
//查找
int Find_name(contact* p, char name[])
{
assert(p);
int i = 0;
for (i = 0; i < p->count; i++)
{
if (strcmp(p->data[i].name, name) == 0)
{
return i;
}
}
return -1;
}
void searchcontact(contact* p)
{
assert(p);
char name[name_MAX] = { 0 };
printf("请输入待查找联系人姓名;>");
scanf("%s", name);
int ret = Find_name(p, name);
if (ret == -1)
{
printf("待查找的联系人未找到\n");
}
printf("%20s\t%5s\t%10s\t%12s\t%15s\n", "姓名", "年龄", "性别", "电话", "地址");
printf("%20s\t%5d\t%10s\t%12s\t%15s\n",
p->data[ret].name,
p->data[ret].age,
p->data[ret].sex,
p->data[ret].tele,
p->data[ret].addr);
}
2.5 删除联系人
第一步先通过姓名在通讯录中查找是否存在此联系人
若不存在,则不需要进行删除
若存在,则进行删除
这里的删除并非真正意义上的删除,本质上是利用待删除联系人后面的联系人的信息将待删除联系人的信息进行覆盖,从而达到删除的作用。
//查找
int Find_name(contact* p, char name[])
{
assert(p);
int i = 0;
for (i = 0; i < p->count; i++)
{
if (strcmp(p->data[i].name, name) == 0)
{
return i;
}
}
return -1;
}
void delcontact(contact* p)
{
assert(p);
//1.查找
char name[name_MAX] = { 0 };
printf("请输入待查找联系人姓名:>");
scanf("%s", name);
int ret = Find_name(p, name);
if (ret == -1)
{
printf("待删除联系人未找到\n");
}
//2.删除
int i = 0;
for (i = ret; i < p->count - 1; i++)
{
p->data[i] = p->data[i + 1];
}
printf("删除成功\n");
p->count--;
}
p->count-1 是考虑到如果待删除的联系人是最后一位联系人,将会出现数组越界的情况。
2.6 修改联系人信息
与上面步骤类似
//查找
int Find_name(contact* p, char name[])
{
assert(p);
int i = 0;
for (i = 0; i < p->count; i++)
{
if (strcmp(p->data[i].name, name) == 0)
{
return i;
}
}
return -1;
}
void modifycontact(contact* p)
{
assert(p);
//1.查找
char name[name_MAX] = { 0 };
printf("请输入待修改联系人的姓名;>");
scanf("%s", name);
int ret = Find_name(p, name);
if (ret == -1)
{
printf("待修改联系人未找到\n");
return;
}
printf("待修改联系人已找到,接下来进行修改\n");
printf("请输入修改之后姓名:>");
scanf("%s", p->data[ret].name);
printf("请输入修改之后年龄:>");
scanf("%d", &(p->data[ret].age));
printf("请输入修改之后性别:>");
scanf("%s", p->data[ret].sex);
printf("请输入修改之后电话:>");
scanf("%s", p->data[ret].tele);
printf("请输入修改之后地址:>");
scanf("%s", p->data[ret].addr);
printf("修改成功\n");
}
2.7 打印通讯录
第一步先判断通讯录中是否有联系人,第二步若存在则进行逐个联系人打印,或者不存在不进行打印。
void showcontact(contact* p)
{
assert(p);
//判断通讯录中是否有联系人
if (p->count == 0)
{
printf("通讯录中没有联系人\n");
return;
}
int i = 0;
for (i = 0; i < p->count; i++)
{
printf("%20s\t%5s\t%10s\t%12s\t%15s\n",
"姓名", "年龄", "性别", "电话", "地址");
printf("%20s\t%5d\t%10s\t%12s\t%15s\n",
p->data[i].name,
p->data[i].age,
p->data[i].sex,
p->data[i].tele,
p->data[i].addr);
}
}
2.8 排序通讯录
介绍一下用于各种类型数据排序的库函数qsort
void qsort(void*base,//待排序的数据的起始位置
size_t num,//待排序的数据的元素个数
size_t width,//待排序的数据的元素大小(单位字节)
int(*cmp)(const void*e1,const void*e2)//函数指针-比较函数
)
关于库函数的具体使用可以去看我的另一篇博客指针的进阶
link
int cmp_name(const void* e1, const void* e2)
{
//因为数据类型是peoinfo所以这里需要进行强制类型转换
return strcmp(((peoinfo*)e1)->name, ((peoinfo*)e2)->name);
}
void sortcontact(contact* p)
{
assert(p);
qsort(p->data, p->count, sizeof(peoinfo),cmp_name);
printf("排序完成\n");
}
因为需要排序通讯录,所以可以根据联系人的姓名,年龄,地址进行排序,在这里所选择的是按照姓名进行排序。
至于其余的排序方式,读者可以试着自己编写代码去实现。
3.改进a-动态版本
动态版本
3.1菜单选择增加
void menu()
{
printf("**********************************************\n");
printf("**** 1.add 2.rearch ******\n");
printf("**** 3.del 4.modify ******\n");
printf("**** 5.show 6.sort ******\n");
printf("**** 7.destory 0.exit ******\n");
printf("**********************************************\n");
}
3.2修改初始联系人数目
#define Max 3//联系人数量
3.3初始化功能修改为动态开辟内存
void Initcontact(contact* p)
{
assert(p);
p->count = 0;
p->data = (peoinfo*)calloc(Max , sizeof(peoinfo));
if (p->data == NULL)
{
printf("%s\n", strerror(errno));
//若开辟内存失败,打印错误原因
return;
}
p->capacity = Max;
return 0;
}
3.4 判断联系人数量是否已满,或进行扩容
//判断通讯录中联系人数量是否已满
if (p->count == p->capacity)
{
printf("数量已满,需要增容\n");
peoinfo* str = (peoinfo*)realloc(p->data, (p->capacity+Max) * sizeof(peoinfo));
if (str == NULL)//扩容失败
{
printf("%s\n", strerror(errno));
return;
}
else//扩容成功
{
p->data = str;
p->capacity += Max;
}
}
3.5增加销毁通讯录的功能
void destorycontact(contact* p)
{
assert(p);
free(p->data);
p->data = NULL;
printf("通讯录已销毁\n");
}
4.改进b-文件版本
4.1 在销毁通讯录之前将通讯录保存到文件中
void savecontact(const contact* p)
{
assert(p);
//采用二进制形式将通讯录写入文件中
FILE* pfwrite = fopen("text.txt", "wb");
if (pfwrite == NULL)
{
printf("%s\n", strerror(errno));
return;
}
//写文件
int i = 0;
for (i = 0; i < p->count; i++)
{
//一次写入一个联系人的信息
fwrite(p->data + i, sizeof(peoinfo), 1, pfwrite);
}
//关闭文件
fclose(pfwrite);
pfwrite = NULL;
}
4.2在初始化通讯录过程中,将文件信息加载到通讯录
void loadcontact(contact* p)
{
assert(p);
FILE* pfread = fopen("test.txt", "rb");
if (pfread == NULL)
{
printf("%s\n", strerror(errno));
return;
}
peoinfo peo = { 0 };
//每次读一位联系人到通讯录中
while (fread(&peo, sizeof(peoinfo), 1, pfread) == 1)
{
//检查通讯录是否需要扩容
checkcontact(p);
p->data[p->count] = peo;
p->count++;
}
//关闭文件
fclose(pfread);
pfread = NULL;
}