目录
一、创建联系人信息(结构体)
二、创建通讯录(结构体)
三、define定义常量
四、打印通讯录菜单
五、枚举菜单选项
六、初始化通讯录
七、实现通讯的的功能
7.1 增加加联系人
7.2 显示所有联系人的信息
7.3 单独查找人的函数
7.4 删除指定联系人
7.5 查找指定联系人
7.6 修改指定联系人
7.7 按照名字顺序进行排序
7.8 清空所有联系人
八、通讯录优化——静态->动态
8.1 通讯录结构体优化
8.2 初始化函数的优化
8.3增加联系人的优化
8.4 释放通讯录的空间
九、完整代码
test.c
contact.h
contact.c
实现一个通讯录,这里我们先来简单构思一下,通讯录中保存人的信息,分别有姓名,年龄,性别,电话,住址,假设我们通讯录中可以存放1000个人的信息。同时我们通讯录还要具备一些功能。包括;
① 增加联系人
② 删除指定联系人
③ 修改指定联系人
④ 查找指定联系人
⑤ 显示所有联系人的信息
⑥ 按照名字顺序进行排序
⑦ 清空所有联系人
在写完之后,我们紧接着可以对通讯录进行优化,将静态通讯录改为动态通讯录。
一起跟着博主的思路来完成通讯录的实现吧!
首先我们需要三个文件,分别是test.c——测试通讯录,contact.h——函数和类型的声明,contact.c——函数的实现,其次我们需要创建保存人信息的结构体。接下来我们开始慢代码实现吧!
为了保存联系人的各个信息,我们创建了一个结构体,放在头文件中,同时为了方便使用,对结构体重命名。
typedef struct PeoInfo
{
char name[20];
int age;
char sex[5];
char tele[12];
char addr[30];
}PeoInfo;
为了方便记录通讯录中现在的人数,创建一个结构体将联系人结构体和人数封装在一起。
//通讯录
typedef struct Contact
{
PeoInfo data[100];
int sz;
}Contact;
写到这里,我们发现如果以后想要更改通讯录的人数,或者名字的宽度,以及地址的宽度这些变量,要到代码中修改,很麻烦,我们可以通过define定义常量,这样使用或者更改的时候就会很方便,达到我们想要的效果。
#define MAX 100 //最大人数
#define MAX_NAME 20 //名字最大宽度
#define MAX_SEX 5 //性别种类
#define MAX_TELE 12 //电话最大长度
#define MAX_ADDR 30 //地址最大宽度
因为每次使用至少使用一次,所以我们采用do—while循环来设计测试通讯录。
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***********\n");
printf("********* 0.exit ***********\n");
printf("*******************************\n");
}
void test()
{
int input = 0;
do
{
menu();
printf("请选择:> ");
scanf("%d", &input);
} while(input);
}
为了代码美观,以及更方便的去识别代码的意思,我们可以采用自定义枚举的方式来实现功能选择。具体代码如下
enum Option
{
Exit,//0 ——退出
Add,//1 ——增加联系人
Del,//2 ——删除指定联系人
Search,//3 ——查找指定联系人信息
Modify,//4 ——修改指定联系人信息
Show,//5 ——显示通讯录中所有联系人
Sort,//6 ——排序联系人
Clear//7 ——清空所有联系人
};
这样就容易理解许多,效果如下所示:
void test()
{
int input = 0;
//定义通讯录
Contact con;
InitContact(&con);
do
{
menu();//打印菜单
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case Add:
AddContact(&con);
break;
case Del:
DelContact(&con);
break;
case Search:
SearchContact(&con);
break;
case Modify:
ModifyContact(&con);
break;
case Show:
ShowContact(&con);
break;
case Sort:
SortContact(&con);
break;
case Clear:
ClearContact(&con);
break;
case Exit:
printf("退出通讯录\n");
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
}
当我们创建完通讯录之后,为了更好的实现后面的功能,最好初始化一下通讯录。
注意:在contact.h中声明,在contact.c中实现。
这里博主使用memset来讲通讯录中的信息全部初始化为0,如果不想使用memset,也可以采用循环的形式来初始化。具体代码如下:
//初始化通讯录
void InitContact(Contact* pc)
{
memset(pc->data, 0, sizeof(pc->data));
pc->sz = 0;
}
注意:①以下函数的实行都是在contact.h中声明类型,然后在contact.v中实现
②在进入函数时,需要assert进行断言,防止传入空指针
③如果要进行的操作不需要更改通讯录的内容(例如:显示所有联系人)时,要用const修饰以下,保护通讯录内容。
增加联系人我们首先需要判断通讯录里面的人数是否满了。没满才能继续进行操作
//增加联系人
void AddContact(Contact* pc)
{
assert(pc);//断言,防止pc是空指针
//判断通讯录是否满了
if (pc->sz == MAX)
{
printf("通讯录已满,无法添加\n");
return;
}
printf("请输入名字:");
scanf("%s", pc->data[pc->sz].name);
printf("请输入年龄:");
scanf("%d", &(pc->data[pc->sz].age));
printf("请输入性别:");
scanf("%s", pc->data[pc->sz].sex);
printf("请输入电话:");
scanf("%s", pc->data[pc->sz].tele);
printf("请输入地址:");
scanf("%s", pc->data[pc->sz].addr);
pc->sz++;
printf("成功添加联系人");
}
当我们增加完联系人之后,我们想要看看是否成功添加,来看看通讯录中存在的联系人。
注意:我们打印的时候可以借助\t制表符来进行打印,这样打印出来会很整齐,很好看!
//显示所有联系人
void ShowContact(const Contact* pc)
{
//断言
assert(pc);
//打印列标题
printf("%-10s\t%-4s\t%-5s\t%-12s\t%-30s\n","姓名","年龄","性别","电话","地址");
int i = 0;
//打印数据
for (i = 0; i < pc->sz; i++)
{
printf("%-10s\t%-4d\t%-5s\t%-12s\t%-30s\n", pc->data[i].name,
pc->data[i].age,
pc->data[i].sex,
pc->data[i].tele,
pc->data[i].addr);
}
}
具体效果如下图所示:
写到这里我们发现不管是删除联系人还是查找联系人以及修改联系人,都会经过查找这一步,所以单独写个查找的函数代码如下:
//只在本文件使用
static int FindName(const Contact* pc, char name[])
{
int i = 0;
for (i = 0; i < pc->sz; i++)
{
if (strcmp(pc->data[i].name, name)==0)
{
return i;
}
}
return -1;
}
当我们写完单独查找人的函数后,这个删除指定联系人就会简单许多,在查找前仍然要检查一下通讯录是否为空,如果为空就没必要再继续下去了,先查找到指定联系人,看看是否存在,然后再删除,具体代码如下:
//删除指定联系人
void DelContact(Contact* pc)
{
if (pc->sz == 0)
{
printf("通讯录为空,无法删除\n");
return;
}
char name[MAX_NAME]={0};
assert(pc);
//删除
printf("请输入要删除人的名字:");
scanf("%s", name);
// 不管是删除联系人还是查找联系人以及修改联系人,都会经过查找这一步,所以单独写个查找的函数
int del=FindName(pc, name);
if (del == -1)
{
printf("要删除的人不存在\n");
return;
}
//删除所要删除的联系人
int i = 0;
for (i = del; i < pc->sz-1; i++)
{
pc->data[i] = pc->data[i + 1];
}
//人数减一
pc->sz--;
printf("成功删除联系人\n");
}
直接调用单独查找人的函数即可,是不是很方便!如果查找的人不存在,那就直接跳出即可,如果找到,就答应查找的联系人,代码如下:
//查找指定联系人
void SearchContact(const Contact* pc)
{
char name[MAX_NAME] = { 0 };
assert(pc);
printf("请输入要查找人的名字:");
scanf("%s", name);
int pos = FindName(pc, name);
if (pos == -1)
{
printf("要查找的人不存在\n");
}
else
{
printf("%-10s\t%-4s\t%-5s\t%-12s\t%-30s\n", "姓名", "年龄", "性别", "电话", "地址");
printf("%-10s\t%-4d\t%-5s\t%-12s\t%-30s\n", pc->data[pos].name,
pc->data[pos].age,
pc->data[pos].sex,
pc->data[pos].tele,
pc->data[pos].addr);
}
}
修改联系人很简单,这里就不多做解释啦,直接上代码!
//修改联系人
void ModifyContact(Contact* pc)
{
char name[MAX_NAME] = { 0 };
assert(pc);
printf("请输入要修改联系人的姓名:");
scanf("%s", name);
int pos = FindName(pc, name);
if (pos == -1)
{
printf("要修改的联系人不存在\n");
}
else
{
printf("请输入名字:");
scanf("%s", pc->data[pos].name);
printf("请输入年龄:");
scanf("%d", &(pc->data[pos].age));
printf("请输入性别:");
scanf("%s", pc->data[pos].sex);
printf("请输入电话:");
scanf("%s", pc->data[pos].tele);
printf("请输入地址:");
scanf("%s", pc->data[pos].addr);
}
}
这里博主就拿按照名字顺序进行排序,利用库函数qsort函数进行排序,具体qsort函数是什么,博主这里建议大家去查一查,了解一下,当然小编也准备单独写一篇博客来介绍qsort函数以及qsort的模拟实现,如果感兴趣,可以关注,期待小编更新哦。
int cmp_name(const void* p1, const void* p2)
{
return strcmp(((PeoInfo*)p1)->name, ((PeoInfo*)p2)->name);
}
//排序联系人
void SortContact(Contact* pc)
{
assert(pc);
//就拿按照名字排序吧
//利用qsort函数
qsort(pc->data, pc->sz, sizeof((pc->data)[0]), cmp_name);
//排序结束,输出通讯录
//打印列标题
printf("%-10s\t%-4s\t%-5s\t%-12s\t%-30s\n", "姓名", "年龄", "性别", "电话", "地址");
int i = 0;
//打印数据
for (i = 0; i < pc->sz; i++)
{
printf("%-10s\t%-4d\t%-5s\t%-12s\t%-30s\n", pc->data[i].name,
pc->data[i].age,
pc->data[i].sex,
pc->data[i].tele,
pc->data[i].addr);
}
}
清空联系人也会很简单,就直接讲通讯录里面的sz人数置为0即可,具体代码如下
void ClearContact(Contact* pc)
{
assert(pc);
//其实清空联系人只需要把里面的人数清零即可
int i = 0;
printf("确定要清空所有联系人吗?确定请按1,按其余键返回\n");
scanf("%d", &i);
if (i == 1)
{
pc->sz = 0;
printf("清空联系人成功");
}
}
写到这里,其实简单的通讯录已经介绍完毕,但是我们这个通讯录其实可以进行优化的。
优化目标:
①通讯录空间不是固定的,大小可以调整的。
②默认能放三个人的信息,如果不够,就每次增加两个人的容量
因为这里的通讯录人数不是固定的了,所以我们首先想到的是优化通讯录这个结构。通讯录的空间是动态开辟的,由malloc开辟即可。
注意:①sz是记录当前放的有效元素的个数
②capacity是记录通讯录当前最大容量
#define DEFAULT_SZ 3 //起始容量
#define ADD_SZ 2 //每次需要增加的容量
//动态版本
//通讯录容量可以更改,不够时进行增加
typedef struct contact
{
PeoInfo* data;//指向存放数据的空间
int sz;//当前放的有效元素的个数
int capacity;//通讯录当前最大容量
}Contact;
初始化函数也不能是固定的100人全部初始化。而是应该最开始开辟三个人的空间。人数为0,容量为3。
//动态版本
void InitContact(Contact* pc)
{
assert(pc);
pc->data = (PeoInfo*)malloc(DEFAULT_SZ * sizeof(PeoInfo));
if (pc->data == NULL)
{
perror("InitContact");
return;
}
pc->sz = 0;
pc->capacity = DEFAULT_SZ;
}
这时动态版本的增加联系人就不存在满的问题了,而是先检查人数是否等于这里的最大容量的,如果等于,那就需要进行扩容了。(利用realloc函数)这里的好处就体现出来了,我们用多少空间,就开辟多少空间,每次只多开辟两个人的空间即可。
我们这里用int来接收增容的结果,如果增容成功,就返回1,增容失败就返回0,然后在进行判断,如果失败,就没必要继续往下走了。
//动态版本
void AddContact(Contact* pc)
{
assert(pc);//断言,防止pc是空指针
CheckCapacity(pc);
if (0 == CheckCapacity(pc))
{
return;
}
printf("请输入名字:");
scanf("%s", pc->data[pc->sz].name);
printf("请输入年龄:");
scanf("%d", &(pc->data[pc->sz].age));
printf("请输入性别:");
scanf("%s", pc->data[pc->sz].sex);
printf("请输入电话:");
scanf("%s", pc->data[pc->sz].tele);
printf("请输入地址:");
scanf("%s", pc->data[pc->sz].addr);
pc->sz++;
printf("成功添加联系人\n");
}
int CheckCapacity(Contact* pc)
{
assert(pc);
//判断通讯录是否满了
if (pc->sz == pc->capacity)
{
PeoInfo* ptr=(PeoInfo*)realloc(pc->data, (pc->capacity+ADD_SZ)*sizeof(PeoInfo));
if (ptr == NULL)
{
perror("CheckCapacity");
return 0;
}
else
{
pc->data = ptr;
pc->capacity += ADD_SZ;
printf("增容成功\n");
return 1;
}
}
return 1;
}
因为这里的通讯录是动态开辟,所以当我们使用完之后要将开辟的内存释放掉,防止内存泄漏。
当然释放完空间,我们里面的信息也就没有了,这时要把人数置为0,容量变为3.
//因为通讯录是动态开辟的,所以使用完需要释放
void DestroyContact(Contact* pc)
{
free(pc->data);
pc->data = NULL;
pc->capacity = 0;
pc->sz = 0;
}
到这里,我们不管是简单的静态通讯录,还是动态的通讯录都实现完毕,代码博主这里放在一起来展示。
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#define _CRT_SECURE_NO_WARNINGS 1
#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***********\n");
printf("********* 0.exit************\n");
printf("*******************************\n");
}
void test()
{
int input = 0;
//定义通讯录
Contact con;
InitContact(&con);
do
{
menu();//打印菜单
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case Add:
AddContact(&con);
break;
case Del:
DelContact(&con);
break;
case Search:
SearchContact(&con);
break;
case Modify:
ModifyContact(&con);
break;
case Show:
ShowContact(&con);
break;
case Sort:
SortContact(&con);
break;
case Clear:
ClearContact(&con);
break;
case Exit:
DestroyContact(&con);
printf("退出通讯录\n");
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
}
int main()
{
test();
return 0;
}
contact.h
#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#define MAX 100
#define MAX_NAME 20
#define MAX_SEX 5
#define MAX_TELE 12
#define MAX_ADDR 30
#define DEFAULT_SZ 3
#define ADD_SZ 2
#include
#include
#include
#include
//类型的声明
enum Option
{
Exit,//0
Add,//1
Del,//2
Search,//3
Modify,//4
Show,//5
Sort,//6
Clear//7
};
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
//{
// PeoInfo data[MAX];
// int sz;
//}Contact;
//动态版本
//通讯录容量可以更改,不够时进行增加
typedef struct contact
{
PeoInfo* data;//指向存放数据的空间
int sz;//当前放的有效元素的个数
int capacity;//通讯录当前最大容量
}Contact;
//函数声明
//初始化通讯录函数
void InitContact(Contact* pc);
//增加联系人信息
void AddContact(Contact* pc);
//显示所有联系人的信息
void ShowContact(const Contact* pc);
//删除指定联系人
void DelContact(Contact* pc);
//查找指定联系人
void SearchContact(const Contact* pc);
//修改联系人
void ModifyContact(Contact* pc);
//排序联系人
void SortContact(Contact* pc);
//清空联系人
void ClearContact(Contact* pc);
//释放通讯录的空间
void DestroyContact(Contact* pc);
contact.c
#define _CRT_SECURE_NO_WARNINGS 1
#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.h"
//函数的实现
//静态版本
初始化通讯录
//void InitContact(Contact* pc)
//{
// memset(pc->data, 0, sizeof(pc->data));
// pc->sz = 0;
//}
//动态版本
void InitContact(Contact* pc)
{
assert(pc);
pc->data = (PeoInfo*)malloc(DEFAULT_SZ * sizeof(PeoInfo));
if (pc->data == NULL)
{
perror("InitContact");
return;
}
pc->sz = 0;
pc->capacity = DEFAULT_SZ;
}
//静态版本
//增加联系人
//void AddContact(Contact* pc)
//{
// assert(pc);//断言,防止pc是空指针
// //判断通讯录是否满了
// if (pc->sz == MAX)
// {
// printf("通讯录已满,无法添加\n");
// return;
// }
// printf("请输入名字:");
// scanf("%s", pc->data[pc->sz].name);
// printf("请输入年龄:");
// scanf("%d", &(pc->data[pc->sz].age));
// printf("请输入性别:");
// scanf("%s", pc->data[pc->sz].sex);
// printf("请输入电话:");
// scanf("%s", pc->data[pc->sz].tele);
// printf("请输入地址:");
// scanf("%s", pc->data[pc->sz].addr);
//
// pc->sz++;
// printf("成功添加联系人");
//
//}
//动态版本
int CheckCapacity(Contact* pc)
{
assert(pc);
//判断通讯录是否满了
if (pc->sz == pc->capacity)
{
PeoInfo* ptr=(PeoInfo*)realloc(pc->data, (pc->capacity+ADD_SZ)*sizeof(PeoInfo));
if (ptr == NULL)
{
perror("CheckCapacity");
return 0;
}
else
{
pc->data = ptr;
pc->capacity += ADD_SZ;
printf("增容成功\n");
return 1;
}
}
return 1;
}
void AddContact(Contact* pc)
{
assert(pc);//断言,防止pc是空指针
CheckCapacity(pc);
if (0 == CheckCapacity(pc))
{
return;
}
printf("请输入名字:");
scanf("%s", pc->data[pc->sz].name);
printf("请输入年龄:");
scanf("%d", &(pc->data[pc->sz].age));
printf("请输入性别:");
scanf("%s", pc->data[pc->sz].sex);
printf("请输入电话:");
scanf("%s", pc->data[pc->sz].tele);
printf("请输入地址:");
scanf("%s", pc->data[pc->sz].addr);
pc->sz++;
printf("成功添加联系人\n");
}
//显示所有联系人
void ShowContact(const Contact* pc)
{
//断言
assert(pc);
//打印列标题
printf("%-10s\t%-4s\t%-5s\t%-12s\t%-30s\n", "姓名", "年龄", "性别", "电话", "地址");
int i = 0;
//打印数据
for (i = 0; i < pc->sz; i++)
{
printf("%-10s\t%-4d\t%-5s\t%-12s\t%-30s\n", pc->data[i].name,
pc->data[i].age,
pc->data[i].sex,
pc->data[i].tele,
pc->data[i].addr);
}
}
//只在本文件使用
static int FindName(const Contact* pc, char name[])
{
int i = 0;
for (i = 0; i < pc->sz; i++)
{
if (strcmp(pc->data[i].name, name) == 0)
{
return i;
}
}
return -1;
}
//删除指定联系人
void DelContact(Contact* pc)
{
if (pc->sz == 0)
{
printf("通讯录为空,无法删除\n");
return;
}
char name[MAX_NAME] = { 0 };
assert(pc);
//删除
printf("请输入要删除人的名字:");
scanf("%s", name);
找到要删除的人
//int i = 0;
//int del = 0;
//int flag = 0;
//for (i = 0; i < pc->sz; i++)
//{
// if (strcmp(pc->data[i].name, name)==0)
// {
// del = i;
// flag = 1;//找到了
// break;
// }
//}
//if (flag == 0)
//{
// printf("要删除的人不存在\n");
// return;
//}
// 不管是删除联系人还是查找联系人以及修改联系人,都会经过查找这一步,所以单独写个查找的函数
int del = FindName(pc, name);
if (del == -1)
{
printf("要删除的人不存在\n");
return;
}
//删除所要删除的联系人
int i = 0;
for (i = del; i < pc->sz - 1; i++)
{
pc->data[i] = pc->data[i + 1];
}
//人数减一
pc->sz--;
printf("成功删除联系人\n");
}
//查找指定联系人
void SearchContact(const Contact* pc)
{
char name[MAX_NAME] = { 0 };
assert(pc);
printf("请输入要查找人的名字:");
scanf("%s", name);
int pos = FindName(pc, name);
if (pos == -1)
{
printf("要查找的人不存在\n");
}
else
{
printf("%-10s\t%-4s\t%-5s\t%-12s\t%-30s\n", "姓名", "年龄", "性别", "电话", "地址");
printf("%-10s\t%-4d\t%-5s\t%-12s\t%-30s\n", pc->data[pos].name,
pc->data[pos].age,
pc->data[pos].sex,
pc->data[pos].tele,
pc->data[pos].addr);
}
}
//修改联系人
void ModifyContact(Contact* pc)
{
char name[MAX_NAME] = { 0 };
assert(pc);
printf("请输入要修改联系人的姓名:");
scanf("%s", name);
int pos = FindName(pc, name);
if (pos == -1)
{
printf("要修改的联系人不存在\n");
}
else
{
printf("请输入名字:");
scanf("%s", pc->data[pos].name);
printf("请输入年龄:");
scanf("%d", &(pc->data[pos].age));
printf("请输入性别:");
scanf("%s", pc->data[pos].sex);
printf("请输入电话:");
scanf("%s", pc->data[pos].tele);
printf("请输入地址:");
scanf("%s", pc->data[pos].addr);
}
}
int cmp_name(const void* p1, const void* p2)
{
return strcmp(((PeoInfo*)p1)->name, ((PeoInfo*)p2)->name);
}
//排序联系人
void SortContact(Contact* pc)
{
assert(pc);
//就拿按照名字排序吧
//利用qsort函数
qsort(pc->data, pc->sz, sizeof((pc->data)[0]), cmp_name);
//排序结束,输出通讯录
//打印列标题
printf("%-10s\t%-4s\t%-5s\t%-12s\t%-30s\n", "姓名", "年龄", "性别", "电话", "地址");
int i = 0;
//打印数据
for (i = 0; i < pc->sz; i++)
{
printf("%-10s\t%-4d\t%-5s\t%-12s\t%-30s\n", pc->data[i].name,
pc->data[i].age,
pc->data[i].sex,
pc->data[i].tele,
pc->data[i].addr);
}
}
//清空联系人
void ClearContact(Contact* pc)
{
assert(pc);
//其实清空联系人只需要把里面的人数清零即可
int i = 0;
printf("确定要清空所有联系人吗?确定请按1,按其余键返回\n");
scanf("%d", &i);
if (i == 1)
{
pc->sz = 0;
printf("清空联系人成功");
}
}
//因为通讯录是动态开辟的,所以使用完需要释放
void DestroyContact(Contact* pc)
{
free(pc->data);
pc->data = NULL;
pc->capacity = 0;
pc->sz = 0;
}
好啦,到这里,今天的博主给大家带来的通讯录到这里就结束啦,如果哪里有问题,欢迎在评论区留言。如果觉得小编写的还不错的,那么可以一键三连哦,您的关注点赞和收藏是对小编最大的鼓励。谢谢大家!!!