目录
检验学习成果最好的方式是实践
在学习完C语言的基础知识以后,就可以简易的实现一些小项目 本文章为手把手讲解实现C语言静态通讯录(好好看,相信不会太难的说) 当你自己完成后一定成就感幸福感满满的!!
注:手把手教学,实操性非常强!!!
用C语言实现简易通讯录
vs2019
通讯录可以用来存储100个人的信息,每个人的信息包括:姓名、性别、年龄、电话、住址
依靠数组和结构体来实现(较为基础)
注:参考代码部分有些地方可以根据自己的需要进行优化
需要包含包括姓名、性别、年龄、电话、住址
除了联系人,还需要记录使用的人数(方便后面功能的实现)
由此我们需要创建结构体变量
//个人信息结构
struct Peoinfo
{
char name[NAME_MAX];
int age;
char sex[SEX_MAX];
int number[NUMBER_MAX];
char addr[ADDR_MAX];
};
//通讯录结构
struct Contact
{
//总联系人容量
struct Peoinfo data[PEOINFO_MAX];
//使用个数记录
int sz;
};
为了方便以后维护修改,我们可以把一些数据用预处理指令来修饰
//信息接收最大数目(预处理指令便于维护)
#define NAME_MAX 20
#define NUMBER_MAX 11
#define SEX_MAX 10
#define ADDR_MAX 30
#define PEOINFO_MAX 100
首先进入选择功能或者退出,选择功能后还可以继续选择,由此采用do while循环来实现整体逻辑
对于各种功能的实现在这里一般只需要传入结构体地址来进行操作通讯录,所以参数形式都一致,返回类型也一致,所以采用回调函数便于调用函数,并且简易便捷
一个数组,元素为函数指针(便于进行传入函数地址)
//回调函数,需要传入函数地址(调用函数)和通讯录地址(操作通讯录)
void Calo(void(*pc)(struct Peoinfo*i), struct Contact* p)
{
pc(p);//pc()为函数调用,参数传入p即通讯录地址
}
//主体逻辑
int main()
{
int intput;
struct Contact con;//创建一个通讯录
InitContact(&con);//对通讯录初始化
//写个罗盘数组,元素为函数指针(便于进行调用函数,进行传参)
void(*table[8])(struct Contract* pc) = { 0,AddContact,DelContact,SearchContact,ModifyContact,ShowContact,SortContact,InitContact };
//基本逻辑循环
do
{
menu();
printf("请输入接下来想要进行的操作:->\n");
scanf("%d", &intput);
if (intput > 0 && intput <=7 )
{
printf("成功进入所选择项操作!\n");
Calo(table[intput],&con);//回调操作
}
else if (intput == 0)
{
printf("退出操作成功!!\n");
break;
}
else
{
printf("输入项错误!请重新选择:->\n");
}
} while (intput);//intput为0则退出
}
这个很简单,直接上代码
void menu()
{
printf("**************************\n");
printf("****1.add 2.del****\n");
printf("****3.search 4.modify****\n");
printf("****5.show 6.sort****\n");
printf("****7.clear 0.exit****\n");
printf("**************************\n");
}
//初始化通讯录
void InitContact(struct Contact* p)
{
p->sz = 0;
//使用内存函数进行初始化数组
memset(p->data, 0, sizeof(p->data));
}
//添加通讯录联系人
void AddContact(struct Contact*p)
{
if (p->sz == PEOINFO_MAX)
{
printf("通讯录联系人已满,无法添加!\n");
}
else
{
printf("请输入新联系人的名字:");
scanf("%s", p->data[p->sz].name);
printf("请输入新联系人的年龄:");
scanf("%d", &p->data[p->sz].age);
printf("请输入新联系人的性别:");
scanf("%s", p->data[p->sz].sex);
printf("请输入新联系人的电话号码:");
scanf("%s", p->data[p->sz].number);
printf("请输入新联系人的地址:");
scanf("%s", p->data[p->sz].addr);
p->sz++;
printf("添加通讯录联系人%s成功!\n", p->data[p->sz].name);
}
return;
}
//删除通讯录联系人
void DelContact(struct Contact* p)
{
char name[NAME_MAX];
printf("请输入要删除的联系人名字:\n");
scanf("%s", &name);
for (int i = 0; i < p->sz; i++)
{
//字符串比较采用strcmp
if (strcmp(name, p->data[i].name) == 0)
{
for (int j = i; j < p->sz - 1; j++)
{
//字符串赋值采用strcpy
strcpy(p->data[j].name , p->data[j + 1].name);
p->data[j].age = p->data[j + 1].age;
strcpy(p->data[j].sex , p->data[j + 1].sex);
strcpy(p->data[j].number , p->data[j + 1].number);
strcpy(p->data[j].addr , p->data[j + 1].addr);
}
p->sz--;
printf("删除通讯录联系人%s成功!\n", name);
return;
}
}
printf("在通讯录中无法查找到联系人%s!\n",name);
return;
}
//查找通讯录联系人
void SearchContact(struct Contact* p)
{
char name[NAME_MAX];
printf("请输入要进行操作的联系人名字:\n");
scanf("%s", &name);
for (int i = 0; i < p->sz; i++)
{
if (strcmp(name, p->data[i].name) == 0)
{
printf("联系人的名字:%s\n", p->data[i].name);
printf("联系人的年龄:%d\n", p->data[i].age);
printf("联系人的性别:%s\n",p->data[i].sex);
printf("联系人的电话号码:%s\n", p->data[i].number);
printf("联系人的地址:%s\n", p->data[i].addr);
return;//展示后就可以退出了
}
}
//前面条件不符合即没有该联系人
printf("在通讯录中无法查找到该联系人!\n");
return;
}
//操作选项
void option()
{
printf("**************************\n");
printf("****1.name 2.age****\n");
printf("****3.sex 4.number****\n");
printf("****5.addr 0.exit****\n");
printf("**************************\n");
}
//修改通讯录联系人信息
void ModifyContact(struct Contact* p)
{
int pos=-1;
int intput = -1;
ShowContact(p);
char name[NAME_MAX];
printf("请输入要进行操作的联系人名字:\n");
scanf("%s", &name);
for (int i = 0; i < p->sz; i++)
{
if (strcmp(name, p->data[i].name) == 0)
{
//找到则记录位置并进行展示
printf("联系人的名字:%s\n", p->data[i].name);
printf("联系人的年龄:%d\n", p->data[i].age);
printf("联系人的性别:%s\n", p->data[i].sex);
printf("联系人的电话号码:%s\n", p->data[i].number);
printf("联系人的地址:%s\n", p->data[i].addr);
pos = i;
break;
}
}
//找不到则退出
if(pos==-1)
{
printf("在通讯录中无法查找到该联系人!\n");
return;
}
do
{
option();
printf("请输入要修改的选项:->\n");
scanf("%d", &intput);
switch (intput)
{
case 0:
printf("退出修改成功!\n");
break;
case 1:
printf("请输入新的名字:\n");
scanf("%s", &p->data[pos].name);
break;
case 2:
printf("请输入新的年龄:\n");
scanf("%d", &p->data[pos].age);
break;
case 3:
printf("请输入新的性别:\n");
scanf("%d", &p->data[pos].sex);
break;
case 4:
printf("请输入新的号码:\n");
scanf("%d", &p->data[pos].number);
break;
case 5:
printf("请输入新的地址:\n");
scanf("%d", &p->data[pos].addr);
break;
default:
printf("输入错误,请重新输入:\n");
}
} while (intput);
return;
}
//展示通讯录联系人
void ShowContact(struct Contact* p)
{
//制表
printf("————————————————————————————————————————————————\n");
printf("||%20s|\t%5s|\t%11s|\t%10s|\t%30s||\n",
"name", "age", "sex", "tele", "addr");
printf("————————————————————————————————————————————————\n");
for (int i = 0; i < p->sz; i++)
{
printf("||%20s|\t%5d|\t%11s|\t%10s|\t%30s||\n",
p->data[i].name,
p->data[i].age,
p->data[i].sex,
p->data[i].number,
p->data[i].addr);
printf("————————————————————————————————————————————————\n");
}
return;
}
//qsort函数参数比较函数的实现
int cmp_con_name(const void* e1,const void* e2)
{
return strcmp(((struct Peoinfo*)e1)->name, ((struct Peoinfo*)e2)->name);
}
int cmp_con_age(const void* e1, const void* e2)
{
return ((struct Peoinfo*)e1)->age - ((struct Peoinfo*)e2)->age;
}
int cmp_con_sex(const void* e1, const void* e2)
{
return strcmp(((struct Peoinfo*)e1)->sex, ((struct Peoinfo*)e2)->sex);
}
int cmp_con_number(const void* e1, const void* e2)
{
return strcmp(((struct Peoinfo*)e1)->number, ((struct Peoinfo*)e2)->number);
}
int cmp_con_addr(const void* e1, const void* e2)
{
return strcmp(((struct Peoinfo*)e1)->addr, ((struct Peoinfo*)e2)->addr);
}
//通讯录排序
void SortContact(struct Contact* p)
{
int intput=-1;
do
{
option();
printf("请输入排序方式的选项:\n");
scanf("%d", &intput);
switch (intput)
{
case 0:
printf("退出修改成功!\n");
break;
case 1:
qsort(p->data, p->sz, sizeof(p->data[0]), cmp_con_name);
ShowContact(p);
break;
case 2:
qsort(p->data, p->sz, sizeof(p->data[0]), cmp_con_age);
ShowContact(p);
break;
case 3:
qsort(p->data, p->sz, sizeof(p->data[0]), cmp_con_sex);
ShowContact(p);
break;
case 4:
qsort(p->data, p->sz, sizeof(p->data[0]), cmp_con_number);
ShowContact(p);
break;
case 5:
qsort(p->data, p->sz, sizeof(p->data[0]), cmp_con_addr);
ShowContact(p);
break;
default:
printf("输入错误,请重新输入:\n");
}
} while (intput);
return;
}
注:如果对qsort函数有什么问题,可参考博主这篇文章: ⭐️ 高阶指针 ⭐️回调函数(回调型计算器/冒泡排序/qsort函数)
#define _CRT_SECURE_NO_WARNINGS
#pragma once
//特定数据宏定义方式,包含各种头文件,以及结构体与函数的声明
#include<stdio.h>
#include<string.h>
//信息接收最大数目(预处理指令便于维护)
#define NAME_MAX 20
#define NUMBER_MAX 11
#define SEX_MAX 10
#define ADDR_MAX 30
#define PEOINFO_MAX 100
//个人信息结构
struct Peoinfo
{
char name[NAME_MAX];
int age;
char sex[SEX_MAX];
int number[NUMBER_MAX];
char addr[ADDR_MAX];
};
//通讯录结构
struct Contact
{
//总联系人容量
struct Peoinfo data[PEOINFO_MAX];
//使用个数记录
int sz;
};
//初始化通讯录(清空通讯录)
void InitContact(struct Contact* p);
//添加通讯录联系人
void AddContact(struct Contact* p);
//删除通讯录联系人
void DelContact(struct Contact* p);
//查找通讯录联系人
void SearchContact(struct Contact* p);
//修改通讯录联系人信息
void ModifyContact(struct Contact* p);
//展示通讯录联系人
void ShowContact(struct Contact* p);
//通讯录排序
void SortContact(struct Contact* p);
//操作选项
void option();
int cmp_con_name(void* e1, void* e2);
int cmp_con_age(void* e1, const void* e2);
int cmp_con_sex(const void* e1, const void* e2);
int cmp_con_number(const void* e1, const void* e2);
int cmp_con_addr(const void* e1, const void* e2);
//写通讯录的整体流程和逻辑
#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.clear 0.exit****\n");
printf("**************************\n");
}
//回调函数,需要传入函数地址(调用函数)和通讯录地址(操作通讯录)
void Calo(void(*pc)(struct Peoinfo*i), struct Contact* p)
{
pc(p);//pc()为函数调用,参数传入p即通讯录地址
}
//主体逻辑
int main()
{
int intput;
struct Contact con;
InitContact(&con);
//写个罗盘数组,元素为函数指针(便于进行调用函数,进行传参)
void(*table[8])(struct Contract* pc) = { 0,AddContact,DelContact,SearchContact,ModifyContact,ShowContact,SortContact,InitContact };
//基本逻辑循环
do
{
menu();
printf("请输入接下来想要进行的操作:->\n");
scanf("%d", &intput);
if (intput > 0 && intput <=7 )
{
printf("成功进入所选择项操作!\n");
Calo(table[intput],&con);//回调操作
}
else if (intput == 0)
{
printf("退出操作成功!!\n");
break;
}
else
{
printf("输入项错误!请重新选择:->\n");
}
} while (intput);//intput为0则退出
}
//实现通讯录功能函数
#include "contact.h"
//初始化通讯录
void InitContact(struct Contact* p)
{
p->sz = 0;
//使用内存函数进行初始化数组
memset(p->data, 0, sizeof(p->data));
}
//添加通讯录联系人
void AddContact(struct Contact*p)
{
if (p->sz == PEOINFO_MAX)
{
printf("通讯录联系人已满,无法添加!\n");
}
else
{
printf("请输入新联系人的名字:");
scanf("%s", p->data[p->sz].name);
printf("请输入新联系人的年龄:");
scanf("%d", &p->data[p->sz].age);
printf("请输入新联系人的性别:");
scanf("%s", p->data[p->sz].sex);
printf("请输入新联系人的电话号码:");
scanf("%s", p->data[p->sz].number);
printf("请输入新联系人的地址:");
scanf("%s", p->data[p->sz].addr);
p->sz++;
printf("添加通讯录联系人%s成功!\n", p->data[p->sz].name);
}
return;
}
//删除通讯录联系人
void DelContact(struct Contact* p)
{
char name[NAME_MAX];
printf("请输入要删除的联系人名字:\n");
scanf("%s", &name);
for (int i = 0; i < p->sz; i++)
{
//字符串比较采用strcmp
if (strcmp(name, p->data[i].name) == 0)
{
for (int j = i; j < p->sz - 1; j++)
{
//字符串赋值采用strcpy
strcpy(p->data[j].name , p->data[j + 1].name);
p->data[j].age = p->data[j + 1].age;
strcpy(p->data[j].sex , p->data[j + 1].sex);
strcpy(p->data[j].number , p->data[j + 1].number);
strcpy(p->data[j].addr , p->data[j + 1].addr);
}
p->sz--;
printf("删除通讯录联系人%s成功!\n", name);
return;
}
}
printf("在通讯录中无法查找到联系人%s!\n",name);
return;
}
//查找通讯录联系人
void SearchContact(struct Contact* p)
{
char name[NAME_MAX];
printf("请输入要进行操作的联系人名字:\n");
scanf("%s", &name);
for (int i = 0; i < p->sz; i++)
{
if (strcmp(name, p->data[i].name) == 0)
{
printf("联系人的名字:%s\n", p->data[i].name);
printf("联系人的年龄:%d\n", p->data[i].age);
printf("联系人的性别:%s\n",p->data[i].sex);
printf("联系人的电话号码:%s\n", p->data[i].number);
printf("联系人的地址:%s\n", p->data[i].addr);
return;
}
}
printf("在通讯录中无法查找到该联系人!\n");
return;
}
//操作选项
void option()
{
printf("**************************\n");
printf("****1.name 2.age****\n");
printf("****3.sex 4.number****\n");
printf("****5.addr 0.exit****\n");
printf("**************************\n");
}
//修改通讯录联系人信息
void ModifyContact(struct Contact* p)
{
int pos=-1;
int intput = -1;
ShowContact(p);
char name[NAME_MAX];
printf("请输入要进行操作的联系人名字:\n");
scanf("%s", &name);
for (int i = 0; i < p->sz; i++)
{
if (strcmp(name, p->data[i].name) == 0)
{
//找到则记录位置并进行展示
printf("联系人的名字:%s\n", p->data[i].name);
printf("联系人的年龄:%d\n", p->data[i].age);
printf("联系人的性别:%s\n", p->data[i].sex);
printf("联系人的电话号码:%s\n", p->data[i].number);
printf("联系人的地址:%s\n", p->data[i].addr);
pos = i;
break;
}
}
//找不到则退出
if(pos==-1)
{
printf("在通讯录中无法查找到该联系人!\n");
return;
}
do
{
option();
printf("请输入要修改的选项:->\n");
scanf("%d", &intput);
switch (intput)
{
case 0:
printf("退出修改成功!\n");
break;
case 1:
printf("请输入新的名字:\n");
scanf("%s", &p->data[pos].name);
break;
case 2:
printf("请输入新的年龄:\n");
scanf("%d", &p->data[pos].age);
break;
case 3:
printf("请输入新的性别:\n");
scanf("%d", &p->data[pos].sex);
break;
case 4:
printf("请输入新的号码:\n");
scanf("%d", &p->data[pos].number);
break;
case 5:
printf("请输入新的地址:\n");
scanf("%d", &p->data[pos].addr);
break;
default:
printf("输入错误,请重新输入:\n");
}
} while (intput);
return;
}
//展示通讯录联系人
void ShowContact(struct Contact* p)
{
//制表
printf("————————————————————————————————————————————————\n");
printf("||%20s|\t%5s|\t%11s|\t%10s|\t%30s||\n",
"name", "age", "sex", "tele", "addr");
printf("————————————————————————————————————————————————\n");
for (int i = 0; i < p->sz; i++)
{
printf("||%20s|\t%5d|\t%11s|\t%10s|\t%30s||\n",
p->data[i].name,
p->data[i].age,
p->data[i].sex,
p->data[i].number,
p->data[i].addr);
printf("————————————————————————————————————————————————\n");
}
return;
}
//qsort函数参数比较函数的实现
int cmp_con_name(const void* e1,const void* e2)
{
return strcmp(((struct Peoinfo*)e1)->name, ((struct Peoinfo*)e2)->name);
}
int cmp_con_age(const void* e1, const void* e2)
{
return ((struct Peoinfo*)e1)->age - ((struct Peoinfo*)e2)->age;
}
int cmp_con_sex(const void* e1, const void* e2)
{
return strcmp(((struct Peoinfo*)e1)->sex, ((struct Peoinfo*)e2)->sex);
}
int cmp_con_number(const void* e1, const void* e2)
{
return strcmp(((struct Peoinfo*)e1)->number, ((struct Peoinfo*)e2)->number);
}
int cmp_con_addr(const void* e1, const void* e2)
{
return strcmp(((struct Peoinfo*)e1)->addr, ((struct Peoinfo*)e2)->addr);
}
//通讯录排序
void SortContact(struct Contact* p)
{
int intput=-1;
do
{
option();
printf("请输入排序方式的选项:\n");
scanf("%d", &intput);
switch (intput)
{
case 0:
printf("退出修改成功!\n");
break;
case 1:
qsort(p->data, p->sz, sizeof(p->data[0]), cmp_con_name);
ShowContact(p);
break;
case 2:
qsort(p->data, p->sz, sizeof(p->data[0]), cmp_con_age);
ShowContact(p);
break;
case 3:
qsort(p->data, p->sz, sizeof(p->data[0]), cmp_con_sex);
ShowContact(p);
break;
case 4:
qsort(p->data, p->sz, sizeof(p->data[0]), cmp_con_number);
ShowContact(p);
break;
case 5:
qsort(p->data, p->sz, sizeof(p->data[0]), cmp_con_addr);
ShowContact(p);
break;
default:
printf("输入错误,请重新输入:\n");
}
} while (intput);
return;
}
对于静态通讯录的实现我们用的是数组来存储数据 但是这样的通讯录存储的联系人容量是有限的(可能会少了,也可能大了) 而实现一个动态的通讯录(使用动态内存管理实现)就能避免这样的问题
对于动态通讯录的实现其实只要在静态通讯录上修改几个地方就可以了
注:动态内存管理知识后面我们会进行详细讲解
//通讯录结构
struct Contact
{
//联系人总容量
int capacity;
//标记动态通讯录的首地址
struct Peoinfo *data;
//使用个数记录
int sz;
};
使用malloc来开辟一个默认大小的动态空间
//初始化通讯录
void InitContact(struct Contact* p)
{
p->capacity = C_SZ;
p->sz = 0;
p->data=(struct Peoinfo*)malloc(sizeof(struct Peoinfo) * C_SZ);
LoadContact(p);
}
//默认容量大小
#define C_SZ 2
//默认添加大小
#define PLUS 3
//检查通讯录是否满
void CheakContact(struct Contact* p)
{
if (p->sz == p->capacity)
{
//如果使用人数已经满了,则申请开辟更大的空间
struct Peoinfo* ptr = (struct Peoinfo*)realloc(p->data, sizeof(struct Peoinfo) * (C_SZ + PLUS));
//ptr不为NULL则开辟成功
if (ptr != NULL)
{
p->data = ptr;
p->capacity += PLUS;
}
else
{
perror("CheakContact");//打印出错误原因
exit(1);//结束程序
}
}
}
退出通讯录时我们需要将动态开辟的空间给释放(使用free函数)(否则可能造成内存泄漏)
//动态通讯录申请空间后不用还得释放
void DestoryContact(struct Contact* p)
{
free(p->data);
p -> data = NULL;
p->capacity = 0;
p->sz = 0;
}
在我们的通讯录写好后,我们如果还想让通讯录能够保存我们所输入联系人数据(在退出后再打开通讯录)
注:这就需要一点文件操作的知识了(在后面的专题中会仔细讲解)
在通讯录运行结束前,我们让通讯录联系人的数据以二进制的形式输出到指定的文件中 (即将数据保存在文件中)
//保存通讯录数据
void SaveContact(struct Contact* p)
{
//fopen函数:找到指定文件并返回文件的地址
//wb:以只写的方式打开(为了输出数据,打开一个二进制文件)
FILE* pf = fopen("contact.txt", "wb");
if (pf == NULL)
{
//打开失败则打印错误信息
perror("SaveContact");
return;
}
for (int i = 0; i < p->sz; i++)
{
//fwrite:二进制输出
fwrite(p->data+i, sizeof(struct Peoinfo), 1, pf);
}
//用完pf后进行释放
fclose(pf);
pf = NULL;
return;
}
当下一次进入通讯录将原来保存的数据输入到内存中
//加载通讯录数据
void LoadContact(struct Contact*p)
{
//rb:以只读的方式打开一个二进制文件
FILE* pf = fopen("contact.txt","rb");
if (pf == NULL)
{
perror("Loadcontact");
return;
}
//暂时保存数据
struct Peoinfo tmp = { 0 };
//fread:二进制输入(输入的数据完整则返回1,否则返回0)
while (fread(&tmp, sizeof(struct Peoinfo), 1, pf))
{
//输入时判断是否需要增容
CheakContact(p);
p->data[p->sz] = tmp;
p->sz++;
}
//释放
fclose(pf);
pf = NULL;
return;
}
#define _CRT_SECURE_NO_WARNINGS
#pragma once
//特定数据宏定义方式,包含各种头文件,以及结构体与函数的声明
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
//信息接收最大数目(预处理指令便于维护)
#define NAME_MAX 20
#define NUMBER_MAX 11
#define SEX_MAX 10
#define ADDR_MAX 30
//默认容量大小
#define C_SZ 2
//默认添加大小
#define PLUS 3
//个人信息结构
struct Peoinfo
{
char name[NAME_MAX];
int age;
char sex[SEX_MAX];
int number[NUMBER_MAX];
char addr[ADDR_MAX];
};
//通讯录结构
struct Contact
{
//联系人总容量
int capacity;
//标记动态通讯录的首地址
struct Peoinfo *data;
//使用个数记录
int sz;
};
//初始化通讯录(清空通讯录)
void InitContact(struct Contact* p);
//添加通讯录联系人
void AddContact(struct Contact* p);
//删除通讯录联系人
void DelContact(struct Contact* p);
//查找通讯录联系人
void SearchContact(struct Contact* p);
//修改通讯录联系人信息
void ModifyContact(struct Contact* p);
//展示通讯录联系人
void ShowContact(struct Contact* p);
//通讯录排序
void SortContact(struct Contact* p);
//清除通讯录(释放)
void DestoryContact(struct Contact* p);
void SaveContact(struct Contact* p);
void LoadContact(struct Contact* p);
//操作选项
void option();
int cmp_con_name(void* e1, void* e2);
int cmp_con_age(void* e1, const void* e2);
int cmp_con_sex(const void* e1, const void* e2);
int cmp_con_number(const void* e1, const void* e2);
int cmp_con_addr(const void* e1, const void* e2);
//实现通讯录功能函数
#include "contact.h"
//检查通讯录是否满
void CheakContact(struct Contact* p)
{
if (p->sz == p->capacity)
{
//如果使用人数已经满了,则申请开辟更大的空间
struct Peoinfo* ptr = (struct Peoinfo*)realloc(p->data, sizeof(struct Peoinfo) * (C_SZ + PLUS));
//ptr不为NULL则开辟成功
if (ptr != NULL)
{
p->data = ptr;
p->capacity += PLUS;
}
else
{
perror("LoadContact");
exit(1);
}
}
}
//加载通讯录数据
void LoadContact(struct Contact*p)
{
//rb:以只读的方式打开一个二进制文件
FILE* pf = fopen("contact.txt","rb");
if (pf == NULL)
{
perror("Loadcontact");
return;
}
//暂时保存数据
struct Peoinfo tmp = { 0 };
//fread:二进制输入(输入的数据完整则返回1,否则返回0)
while (fread(&tmp, sizeof(struct Peoinfo), 1, pf))
{
//输入时判断是否需要增容
CheakContact(p);
p->data[p->sz] = tmp;
p->sz++;
}
//释放
fclose(pf);
pf = NULL;
return;
}
//初始化通讯录
void InitContact(struct Contact* p)
{
p->capacity = C_SZ;
p->sz = 0;
p->data=(struct Peoinfo*)malloc(sizeof(struct Peoinfo) * C_SZ);
LoadContact(p);
}
//添加通讯录联系人
void AddContact(struct Contact* p)
{
CheakContact(p);
printf("请输入新联系人的名字:");
scanf("%s", p->data[p->sz].name);
printf("请输入新联系人的年龄:");
scanf("%d", &p->data[p->sz].age);
printf("请输入新联系人的性别:");
scanf("%s", p->data[p->sz].sex);
printf("请输入新联系人的电话号码:");
scanf("%s", p->data[p->sz].number);
printf("请输入新联系人的地址:");
scanf("%s", p->data[p->sz].addr);
printf("添加通讯录联系人%s成功!\n", p->data[p->sz].name);
p->sz++;
return;
}
//删除通讯录联系人
void DelContact(struct Contact* p)
{
if (p->sz == 0)
{
printf("没有联系人,无法删除!\n");
return;
}
char name[NAME_MAX];
printf("请输入要删除的联系人名字:\n");
scanf("%s", &name);
for (int i = 0; i < p->sz; i++)
{
//字符串比较采用strcmp
if (strcmp(name, p->data[i].name) == 0)
{
for (int j = i; j < p->sz - 1; j++)
{
//字符串赋值采用strcpy
strcpy(p->data[j].name, p->data[j + 1].name);
p->data[j].age = p->data[j + 1].age;
strcpy(p->data[j].sex, p->data[j + 1].sex);
strcpy(p->data[j].number, p->data[j + 1].number);
strcpy(p->data[j].addr, p->data[j + 1].addr);
}
p->sz--;
printf("删除通讯录联系人%s成功!\n", name);
return;
}
}
printf("在通讯录中无法查找到联系人%s!\n", name);
return;
}
//查找通讯录联系人
void SearchContact(struct Contact* p)
{
char name[NAME_MAX];
printf("请输入要进行操作的联系人名字:\n");
scanf("%s", &name);
for (int i = 0; i < p->sz; i++)
{
if (strcmp(name, p->data[i].name) == 0)
{
printf("联系人的名字:%s\n", p->data[i].name);
printf("联系人的年龄:%d\n", p->data[i].age);
printf("联系人的性别:%s\n", p->data[i].sex);
printf("联系人的电话号码:%s\n", p->data[i].number);
printf("联系人的地址:%s\n", p->data[i].addr);
return;
}
}
printf("在通讯录中无法查找到该联系人!\n");
return;
}
//操作选项
void option()
{
printf("**************************\n");
printf("****1.name 2.age****\n");
printf("****3.sex 4.number****\n");
printf("****5.addr 0.exit****\n");
printf("**************************\n");
}
//修改通讯录联系人信息
void ModifyContact(struct Contact* p)
{
int pos = -1;
int intput = -1;
ShowContact(p);
char name[NAME_MAX];
printf("请输入要进行操作的联系人名字:\n");
scanf("%s", &name);
for (int i = 0; i < p->sz; i++)
{
if (strcmp(name, p->data[i].name) == 0)
{
//找到则记录位置并进行展示
printf("联系人的名字:%s\n", p->data[i].name);
printf("联系人的年龄:%d\n", p->data[i].age);
printf("联系人的性别:%s\n", p->data[i].sex);
printf("联系人的电话号码:%s\n", p->data[i].number);
printf("联系人的地址:%s\n", p->data[i].addr);
pos = i;
break;
}
}
//找不到则退出
if (pos == -1)
{
printf("在通讯录中无法查找到该联系人!\n");
return;
}
do
{
option();
printf("请输入要修改的选项:->\n");
scanf("%d", &intput);
switch (intput)
{
case 0:
printf("退出修改成功!\n");
break;
case 1:
printf("请输入新的名字:\n");
scanf("%s", &p->data[pos].name);
break;
case 2:
printf("请输入新的年龄:\n");
scanf("%d", &p->data[pos].age);
break;
case 3:
printf("请输入新的性别:\n");
scanf("%d", &p->data[pos].sex);
break;
case 4:
printf("请输入新的号码:\n");
scanf("%d", &p->data[pos].number);
break;
case 5:
printf("请输入新的地址:\n");
scanf("%d", &p->data[pos].addr);
break;
default:
printf("输入错误,请重新输入:\n");
}
} while (intput);
return;
}
//展示通讯录联系人
void ShowContact(struct Contact* p)
{
//制表
printf("————————————————————————————————————————————————\n");
printf("||%20s|\t%5s|\t%11s|\t%10s|\t%30s||\n",
"name", "age", "sex", "tele", "addr");
printf("————————————————————————————————————————————————\n");
for (int i = 0; i < p->sz; i++)
{
printf("||%20s|\t%5d|\t%11s|\t%10s|\t%30s||\n",
p->data[i].name,
p->data[i].age,
p->data[i].sex,
p->data[i].number,
p->data[i].addr);
printf("————————————————————————————————————————————————\n");
}
return;
}
//qsort函数参数比较函数的实现
int cmp_con_name(const void* e1, const void* e2)
{
return strcmp(((struct Peoinfo*)e1)->name, ((struct Peoinfo*)e2)->name);
}
int cmp_con_age(const void* e1, const void* e2)
{
return ((struct Peoinfo*)e1)->age - ((struct Peoinfo*)e2)->age;
}
int cmp_con_sex(const void* e1, const void* e2)
{
return strcmp(((struct Peoinfo*)e1)->sex, ((struct Peoinfo*)e2)->sex);
}
int cmp_con_number(const void* e1, const void* e2)
{
return strcmp(((struct Peoinfo*)e1)->number, ((struct Peoinfo*)e2)->number);
}
int cmp_con_addr(const void* e1, const void* e2)
{
return strcmp(((struct Peoinfo*)e1)->addr, ((struct Peoinfo*)e2)->addr);
}
//通讯录排序
void SortContact(struct Contact* p)
{
int intput = -1;
do
{
option();
printf("请输入排序方式的选项:\n");
scanf("%d", &intput);
switch (intput)
{
case 0:
printf("退出修改成功!\n");
break;
case 1:
qsort(p->data, p->sz, sizeof(p->data[0]), cmp_con_name);
ShowContact(p);
break;
case 2:
qsort(p->data, p->sz, sizeof(p->data[0]), cmp_con_age);
ShowContact(p);
break;
case 3:
qsort(p->data, p->sz, sizeof(p->data[0]), cmp_con_sex);
ShowContact(p);
break;
case 4:
qsort(p->data, p->sz, sizeof(p->data[0]), cmp_con_number);
ShowContact(p);
break;
case 5:
qsort(p->data, p->sz, sizeof(p->data[0]), cmp_con_addr);
ShowContact(p);
break;
default:
printf("输入错误,请重新输入:\n");
}
} while (intput);
return;
}
//动态通讯录申请空间后不用还得释放
void DestoryContact(struct Contact* p)
{
free(p->data);
p -> data = NULL;
p->capacity = 0;
p->sz = 0;
}
//保存通讯录数据
void SaveContact(struct Contact* p)
{
//fopen函数:找到指定文件并返回文件的地址
//wb:以只写的方式打开(为了输出数据,打开一个二进制文件)
FILE* pf = fopen("contact.txt", "wb");
if (pf == NULL)
{
//打开失败则打印错误信息
perror("SaveContact");
return;
}
for (int i = 0; i < p->sz; i++)
{
//fwrite:二进制输出
fwrite(p->data+i, sizeof(struct Peoinfo), 1, pf);
}
//用完pf后进行释放
fclose(pf);
pf = NULL;
return;
}
//写通讯录的整体流程和逻辑
#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.clear 0.exit****\n");
printf("**************************\n");
}
//回调函数,需要传入函数地址(调用函数)和通讯录地址(操作通讯录)
void Calo(void(*pc)(struct Peoinfo* i), struct Contact* p)
{
pc(p);//pc()为函数调用,参数传入p即通讯录地址
}
//主体逻辑
int main()
{
int intput;
struct Contact con;
InitContact(&con);
//写个罗盘数组,元素为函数指针(便于进行调用函数,进行传参)
void(*table[8])(struct Contract* pc) = { 0,AddContact,DelContact,SearchContact,ModifyContact,ShowContact,SortContact,DestoryContact };
//基本逻辑循环
do
{
menu();
printf("请输入接下来想要进行的操作:->\n");
scanf("%d", &intput);
if (intput > 0 && intput <= 7)
{
printf("成功进入所选择项操作!\n");
Calo(table[intput], &con);//回调操作
}
else if (intput == 0)
{
SaveContact(&con);
Calo(table[7], &con);
printf("退出操作成功!!\n");
break;
}
else
{
printf("输入项错误!请重新选择:->\n");
}
} while (intput);//intput为0则退出
}