通讯录
1 目录结构
-
test.c
测试
-
contact.c
实现函数功能
功能:
- 存放1000个好友的信息
- 姓名
- 电话
- 性别
- 住址
- 年龄
- 增加好友信息
- 删除指定名字的信息
- 查找好友信息
- 修改好友信息
- 打印好友信息
- 排序…
2 静态数组方式
2.1. 测试程序
int main()
{
int input = 0;
//int size = 0; // 记录通讯录有多少个记录
// 创建通讯录
// 好友信息结构体存放在 contact.h 中
Contact con; // 存放1000个人的信息,和size
// 初始换通讯录
initContact(&con);
//
do
{
menu();
printf("please choose command number\n");
scanf("%d", &input);
switch (input)
{
case ADD:
addContactor(&con);
showContact(&con);
break;
case DEL:
deleteContact(&con);
showContact(&con);
break;
case SEARCH:
searchContact(&con);
break;
case MODIFY:
modifyContact(&con);
break;
case SHOW:
showContact(&con);
break;
case EXIT:
printf("exit the contact\n");
break;
default:
printf("you have the wrong choose number, please choose again\n");
break;
}
} while (input);
}
2.2 创建的结构体和枚举类
//contact.h
#define MAX 1000
#define MAX_NAME 20
#define MAX_SEX 5
#define MAX_TELE 12
#define MAX_ADDR 50
// 在菜单栏进行选择的时候,如果使用switch case
// case这里跟上1,2,3,4...这些数字,对程序员不友好
// 想要知道数字是哪种操作,还要看一眼菜单对应的操作是什么,不如使用枚举类
enum Option
{
EXIT,
ADD,
DEL,
SEARCH,
MODIFY,
SHOW
};
// 联系人信息
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
{
int size; // 记录当前元素个数
PeoInfo data[MAX]; // 存放1000个信息
}Contact;
2.3. 通讯录初始化
// contact.h
// 初始化通讯录
void initContact(Contact* pcon);
// contact.c
void initContact(Contact* pcon)
{
memset(pcon->data, 0, sizeof(pcon->data));
pcon->size = 0; // 设置通讯录最初是 0 个元素
}
2.4. 菜单功能
//test.c
void menu()
{
printf("********************************************\n");
printf("*******************通讯录********************\n");
printf("***********1、add 2、del *********\n");
printf("***********3、search 4、modify *********\n");
printf("***********5、show 0、exit *********\n");
printf("********************************************\n");
printf("********************************************\n");
}
2.5. 添加联系人
// contact.h
// 添加联系人
// 添加失败返回0,添加成功返回1
int addContactor(Contact* pcon);
// contact.c
int addContactor(Contact* pcon)
{
int size = pcon->size;
if (size == MAX )
{
printf("通讯录已满,添加失败...\n");
return 0;
}
printf("please input the name of your friend\n");
scanf("%s", pcon->data[size].name);
printf("please input the tele of your friend\n");
scanf("%s", pcon->data[size].tele);
printf("please input the age of your friend\n");
scanf("%d", &(pcon->data[size].age));
printf("please input the sex of your friend\n");
scanf("%s", pcon->data[size].sex);
printf("please input the addr of your friend\n");
scanf("%s", pcon->data[size].addr);
pcon->size = size + 1;
printf("添加成功...\n");
return 1;
}
2.6. 查找联系人
// contact.h
// 查找联系人
int searchContact(Contact* con);
// 查找指定姓名的联系人
// 返回下标,未找到返回-1
// 给函数或者全局变量加上 static 时,只能自己所在原文件有效。
static int searchContactByName(Contact* con, char name[]);
//contact.c
static int searchContactByName(Contact* con, char name[]) // 给函数或者全局变量加上 static 时,只能自己所在原文件有效。
{
for (int i = 0; i < con->size; i++)
{
if (strcmp(con->data[i].name, name) == 0)
{
return i;
}
}
return -1;
}
int searchContact(Contact* con)
{
int size = con->size;
char searchName[MAX_NAME] = {0};
int index = -1;
if (size == 0)
{
printf("the contact is empty...\n");
return 0;
}
printf("请输入要查找的姓名>\n");
scanf("%s", searchName);
index = searchContactByName(con, searchName);
if (index == -1)
{
printf("未查找到姓名为[%s]的联系人\n", searchName);
return 0;
}
printf("该联系人的信息为>\n");
showContactByIndex(con, index);
return 1;
}
2.7. 显示全部联系人和显示指定索引处的联系人
// contact.h
// 显示通讯录
void showContact(const Contact* pcon);
// 显示指定索引处联系人的信息
void showContactByIndex(const Contact* pcon, int index);
// contact.c
void showContact(const Contact* pcon)
{
int size = pcon->size;
if (size == 0)
{
printf("the info of contact is null\n");
return;
}
printf("the info of contact is...\n");
printf("%-4s\t%-20s\t%-12s\t%-4s\t%-5s\t%-50s \n", "序号", "姓名", "电话", "年龄", "性别", "地址");
for (int i = 0; i < size; i++)
{
printf("%-4d\t%-20s\t%-12s\t%-4d\t%-5s\t%-50s \n",
i,
pcon->data[i].name,
pcon->data[i].tele,
pcon->data[i].age,
pcon->data[i].sex,
pcon->data[i].addr);
}
printf("end...\n");
}
void showContactByIndex(const Contact* pcon, int index)
{
if (index >= pcon->size)
{
printf("索引超出范围...\n");
return;
}
printf("%-4s\t%-20s\t%-12s\t%-4s\t%-5s\t%-50s \n", "序号", "姓名", "电话", "年龄", "性别", "地址");
printf("%-4d\t%-20s\t%-12s\t%-4d\t%-5s\t%-50s \n",
index,
pcon->data[index].name,
pcon->data[index].tele,
pcon->data[index].age,
pcon->data[index].sex,
pcon->data[index].addr);
}
2.8. 删除联系人
//contact.h
int deleteContact(Contact* con);
//contact,c
int deleteContact(Contact* con)
{
int size = con->size;
int index = -1; // 定义要删除姓名的下标
char deleteName[MAX_NAME] = {0};
char delConfirm = 0; // 确认是否删除
if (size == 0)
{
printf("the contact is empty, you can't delete...\n");
return 0;
}
printf("please input the name you need to delete>\n");
scanf("%s", deleteName);
index = searchContactByName(con, deleteName);
if (index == -1)
{
printf("未找到姓名为\"%s\"的联系人\n", deleteName);
return 0;
}
while (1)
{
printf("你确定删除姓名为\"%s\"的联系人吗?y/n\n", deleteName);
scanf("%c", &delConfirm);
if (delConfirm == 'y' || delConfirm == 'Y')
{
// 确认删除
for (int i = index + 1; i < size; i++)
{
con->data[i - 1] = con->data[i];
}
con->size--;
printf("删除成功!\n");
return 1;
}
if (delConfirm == 'n' || delConfirm == 'N')
{
// 取消删除
return 0;
}
printf("请输入正确的指令\n");
}
return 0;
}
2.9. 修改联系人
// contact.h
int modifyContact(Contact* con);
//contact.c
int modifyContact(Contact* con)
{
char modifyName[MAX_NAME];
int index = -1;
if (con->size == 0)
{
printf("contact is empty...\n");
return 0;
}
printf("需要修改的联系人姓名:\n");
scanf("%s", modifyName);
index = searchContactByName(con, modifyName);
if (index == -1)
{
printf("通讯录没有姓名为【%s】的人\n", modifyName);
return 0;
}
printf("该联系人的信息为>\n");
showContactByIndex(con, index);
printf("请输入要修改的信息>\n");
printf("please input the name of your friend\n");
scanf("%s", con->data[index].name);
printf("please input the tele of your friend\n");
scanf("%s", con->data[index].tele);
printf("please input the age of your friend\n");
scanf("%d", &(con->data[index].age));
printf("please input the sex of your friend\n");
scanf("%s", con->data[index].sex);
printf("please input the addr of your friend\n");
scanf("%s", con->data[index].addr);
printf("***********修改成功***********\n");
printf("修改后的联系人信息为>\n");
showContactByIndex(con, index);
return 1;
}
3 动态申请内存方式
上面写的通讯录,上来就能存放1000个人的信息,如果多了少了对内存都不友好。
那我们就可以设计,比如默认可以存放10个人的信息,当发现通讯录满的时候,我们进行动态的扩容。每次增加5个人的空间等等:
10 15 20 25...
3.1. 修改通讯录结构体
typedef struct Contact
{
//Info data[MAX];
Info* data;
int size;
int capacity; // 通讯录的最大容量,当size和这个一样大时,我们进行扩容
}Contact;
我们给通讯录初始化时,是创建一个有10个人信息的空间,当容量达到10个时,我们进行扩容。
所以,除了需要记录通讯录当前包含联系人的个数之外,还要记录当前通讯录的容量,这一点在静态数组中没有记录的原因,是静态数组已经规定了最大容量,这个值我们是已知的
3.2. 初始化通讯录
初始化时,我们可以默认是10,也可以默认是1,2,3,4,5等等任意的数字,所以我们可以在
contact.h
中定义
#define DEFALUT_CAPACITY 10
void initContact(Contact* pcon)
{
//memset(pcon->data, 0, sizeof(pcon->data));
pcon->data = (PeoInfo*)malloc(DEFAULT_CAPACITY * sizeof(PeoInfo));
if (pcon->data == NULL)
{
return;
}
pcon->size = 0; // 设置通讯录最初是 0 个元素
pcon->capacity = DEFAULT_CAPACITY;
}
3.3. 增加联系人
通讯录满的时候,原来是不能增加,现在是扩容
仍然在contact.h中定义默认的扩容个数
#define EXTEND_CAPACITY 5
int addContactor(Contact* pcon)
{
int size = pcon->size;
// 判断通讯录容量是否满了
// 如果满了,就扩容,扩容完继续执行添加
// 如果不满,就继续添加
if (contactIsFull(pcon))
{
// 扩容
PeoInfo* ptemp = (PeoInfo*)realloc(pcon->data, (pcon->capacity + EXTEND_CAPACITY) * sizeof(PeoInfo));
if (ptemp == NULL)
{
printf("添加失败\n");
return 0;
}
pcon->data = ptemp;
pcon->capacity += 2;
printf("扩容成功\n");
return 1;
}
printf("please input the name of your friend\n");
scanf("%s", pcon->data[size].name);
printf("please input the tele of your friend\n");
scanf("%s", pcon->data[size].tele);
printf("please input the age of your friend\n");
scanf("%d", &(pcon->data[size].age));
printf("please input the sex of your friend\n");
scanf("%s", pcon->data[size].sex);
printf("please input the addr of your friend\n");
scanf("%s", pcon->data[size].addr);
pcon->size = size + 1;
printf("添加成功...\n");
return 1;
}
// 这里面涉及到一个contactIsFull函数,是用来判断当前通讯录是否为满的
// 声明在 contact.h中
static int contactIsFull(const Contact* pcon);
// 定义在contact.c
static int contactIsFull(const Contact* pcon)
{
if (pcon->size == pcon->capacity)
{
return 1;
}
return 0;
}
3.4. 删除/修改/查找联系人
不需要修改,因为扩容完了之后,再删除,只是移动 size 这个标记,并把原来的内容往前移动,并不涉及内存的回收。
3.5. 退出(销毁内存空间)
销毁通讯录,释放动态申请的内存空间
//contact.h
// 释放内存空间
void destroyContact(Contact* pcon);
//contact.c
// 释放内存空间
void destroyContact(Contact* pcon)
{
free(pcon->data);
pcon->data = NULL;
}
4 文件版本的通讯录
之前的版本,是都放在内存中的,当程序结束运行时,通讯录就会销毁了。
我们现在把通讯录持久化到文件中去。
我们可以新增一个保存
save
函数。
4.1. 菜单更改,添加一个save
void menu()
{
printf("********************************************\n");
printf("*******************通讯录********************\n");
printf("***********1、add 2、del *********\n");
printf("***********3、search 4、modify *********\n");
printf("***********5、show 6、save *********\n");
printf("*********** 0、xeit *********\n");
printf("********************************************\n");
}
所以在枚举类和测试函数的switch case中,也要加上
enum Option
{
EXIT,
ADD,
DEL,
SEARCH,
MODIFY,
SHOW,
SAVE
};
4.2. 添加保存到文件的操作
case SAVE:
saveContact(&con);
break;
case EXIT:
// 销毁通讯录,释放动态申请的内存空间
// 退出销毁之前,最好是也要保存一下通讯录
saveContact(&con);
destroyContact(&con);
printf("exit the contact\n");
break;
// contact.h
// 保存文件
void saveContact(Contact* pcon);
// contact.c
// 保存文件
void saveContact(Contact* pcon)
{
FILE* pfWrite = fopen("contact.txt", "wb");
if (pfWrite == NULL)
{
printf("saveContact::%s\n", strerror(errno));
}
if (pcon->size == 0)
{
printf("通讯录为空...\n");
return;
}
//fprintf(pfWrite,"%-4s\t%-20s\t%-12s\t%-4s\t%-5s\t%-50s \n", "序号", "姓名", "电话", "年龄", "性别", "地址");
for (int i = 0; i < pcon->size; i++)
{
fwrite(&(pcon->data[i]), sizeof(PeoInfo), 1, pfWrite);
}
//fprintf(pfWrite ,"end...\n");
fclose(pfWrite);
pfWrite = NULL;
}
4.3. 初始化通讯录
初始化通讯录时,我们最好是把保存在文件中的数据,读入到内存中,这样才更合理
void initContact(Contact* pcon)
{
//memset(pcon->data, 0, sizeof(pcon->data));
pcon->data = (PeoInfo*)malloc(DEFAULT_CAPACITY * sizeof(PeoInfo));
if (pcon->data == NULL)
{
return;
}
pcon->size = 0; // 设置通讯录最初是 0 个元素
pcon->capacity = DEFAULT_CAPACITY;
// 把文件加载到通讯录中
loadContact(pcon);
}
// 其中 loadContact 在 contact.h中声明
// 在 contact.c 中定义
// 加载文件中的信息到通讯录中
void loadContact(Contact* pcon)
{
FILE* pfRead = fopen("contact.txt", "rb");
PeoInfo tmp = {0};
if (pfRead == NULL)
{
printf("loadContact::%s\n", strerror(errno));
return;
}
while (fread(&tmp,sizeof(PeoInfo),1,pfRead)) // 读取的返回值,是实际读取到的元素个数,如果没有读到,就说明是返回0
{
pcon->data[pcon->size] = tmp;
pcon->size++;
if (contactIsFull(pcon))
{
// 扩容
PeoInfo* ptemp = (PeoInfo*)realloc(pcon->data, (pcon->capacity + EXTEND_CAPACITY) * sizeof(PeoInfo));
if (ptemp == NULL)
{
printf("添加失败\n");
return 0;
}
pcon->data = ptemp;
pcon->capacity += 2;
//printf("扩容成功\n");
}
}
// 关闭文件
fclose(pfRead);
pfRead = NULL;
}