个人主页:@Weraphael
✍作者简介:目前是C语言学习者
✈️专栏:项目
希望大家多多支持,咱一起进步!
如果文章对你有帮助的话
欢迎 评论 点赞 收藏 加关注
结构体知识回顾:【C语言初阶】结构体+【C语言进阶】结构体
这篇博客将带你用结构体相关知识来写一个简单的通讯录
- 包含人的相关信息:姓名、年龄、性别、地址和电话
- 假设通讯录要存放100个人(具体情况自己定)
- 功能:
①增加联系人
②删除指定联系人
③查找指定联系人的信息
④修改指定联系人的信息
⑤显示所有人的信息
⑥可以按照联系人的年龄或者按照名字排序(具体情况自己定)
为了方便管理,我们可以创建多个文件来实现
按照我们的要求,其功能有6种,为了后期要选择相对应的功能,因此我们可以创建一个简易的菜单。
为了能使程序变得更简短而清晰,我们可以封装一个函数menu
【代码实现test.c】
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include "contact.h"
void menu()
{
printf("**********************************\n"); //Add - 添加联系人
printf("**** 1. Add 2. del ****\n"); //del - 删除联系人
printf("**** 3. search 4. modify ****\n"); //search - 查找指定联系人
printf("**** 5. show 6. sort ****\n"); //modify - 修改指定联系人
printf("**** 0. exit ****\n"); //show - 展示联系人
printf("**********************************\n"); //sort - 对通讯录排序
//exit - 退出程序
}
int main()
{
do
{
//创建菜单
menu();
} while ();
return 0;
}
创建好菜单后,还要能选择菜单上的功能
【代码实现test.c】
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include "contact.h"
void menu()
{
printf("**********************************\n"); //Add - 添加联系人
printf("**** 1. Add 2. del ****\n"); //del - 删除联系人
printf("**** 3. search 4. modify ****\n"); //search - 查找联系人
printf("**** 5. show 6. sort ****\n"); //modify - 修改联系人
printf("**** 0. exit ****\n"); //show - 展示联系人
printf("**********************************\n"); //sort - 排序
//exit - 退出程序
}
int main()
{
int input = 0;
do
{
//创建菜单
menu();
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
break;
case 5:
break;
case 6:
break;
case 0:
printf("退出通讯录\n");
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
return 0;
}
这里的
input
可以输入任何值,输入1~6,用户则可以选择相对应的功能,但万一用户不小心输入其他值时,我们可以提醒“选择错误,重新选择”,这时switch
语句就派上用场了。最后do while
循环条件是input
,大家想想,假如input
为0,0为假,则结束循环,这不就和退出程序相互匹配上了。(功能的实现还未完善)
根据要求,人的信息必须包含姓名、年龄、性别、地址和电话。因为在信息中,有
char
、int
不同的类型,因此我们可以创建一个结构体(概念:结构是一些值的集合,这些值可以称为成员变量,结构的每个成员可以是不同类型的变量。)
然后我们把类型的定义以及声明放到contact.h
中
【代码实现contact.h】
//用户信息
typedef struct PeoInfo
{
char name[20];//名字
int age; //年龄
char sex[5]//性别
char addr[30] //地址
char tele[12]; //电话号码
}PeoInfo;
上面的代码还能优化
#define MAX 100 //通讯录人数的最大值
#define NAME_MAX 20 //人名字符串的最大值
#define SEX_MAX 5 //性别字符的最大值
#define ADDR_MAX 30 //地址字符的最大值
#define TELE_MAX 12 //电话字符的最大值
//用户的信息
typedef struct PeoInfo
{
char name[NAME_MAX]; //名字
int age; //年龄
char sex[SEX_MAX]; //性别
char addr[ADDR_MAX]; //地址
char tele[TELE_MAX]; //电话
}PeoInfo;
优化的好处是:万一下次要改变数值的大小就方便多了
定义完用户信息后,接下来就要存放100个人的信息,然后还要记录下当前已经存放的人的信息的个数。那该如何创建通讯录呢?一个是结构体类型、一个是
int
类型,所以我们再封装一个结构体。老样子,类型的声明和定义我们都放在contact.h
中
【contact.h】
#define MAX 100 //人的个数最大值
#define NAME_MAX 20//人名字符串的最大值
#define SEX_MAX 5 //性别字符的最大值
#define ADDR_MAX 30 //地址字符的最大值
#define TELE_MAX 12 //电话字符的最大值
//人的信息
typedef struct PeoInfo
{
char name[NAME_MAX]; //名字
int age; //年龄
char sex[SEX_MAX]; //性别
char addr[ADDR_MAX]; //地址
char tele[TELE_MAX]; //电话
}PeoInfo;
//创建通讯录
typedef struct Contact
{
PeoInfo data[MAX]; //存放人的信息
int sz; //表示当前通讯录有多少个人的信息
}Contact;
接下来就是创建通讯录以及它的初始化
【创建通讯录test.c】
【初始化通讯录test.c】
可以封装一个函数来帮助我们初始化(&结构体变量的效率更高)
初始化通讯录函数的声明放到
contact.h
中
然后前期又创建一个
contact.c
文件,它的作用就是用来实现通讯录的
2个可能有疑惑的问题
- 头文件:
由于结构体类型的定义在contact.h
中,而我们又想在另外一个文件中使用contact.h
的内容,就要包含头文件,而又因为这个头文件不是C语言函数库提供的,所以要用双引号
2.初始化通讯录
创建完变量就初始化是一个非常好的编程习惯!但有很多人疑问为什么不这样初始化Contact con = {0}
,其实这样是可以的,但是这种方法太暴力了。为了提高逼格,我们可以封装一个函数来对其初始化。而函数的声明我们还是要放到contact.h
中
首先在test.c封装一个函数
【test.c】
对应的函数的声明,我们定义在
contact.h
【contact.h】
Add函数的实现
【contact.c】
显示联系人的信息无非就是打印嘛,那就非常简单了,只需要把通讯录内容遍历打印就行。
首先在
test.c
封装一个show
函数
然后再对其进行函数的声明,声明就在contact.h中。
最后实现就在contact.c即可
接下来我们现在来调试,像代码量大的工程一定要学会调试,不然写到后面发现一堆bug,那就非常尴尬了。
因为我们上一步已经把添加联系人(Add函数)写好了,现在利用show来看看代码效果
首先现在test.c中封装
del
函数
然后在contact.h对其声明
最后,在contact.c里实现
void del(Contact* pc)
{
char name[NAME_MAX];
//记录要删除用户的位置,因为我们的删除方法是从后往前覆盖要删除的对象
int position = 0;
//通讯录可能一位联系人都没有
if (pc->sz == 0)
{
printf("您的通讯录没有联系人,无法删除\n");
return;
}
//通讯录有人
//1.找到要删除的人
printf("请输入您要删除的对象:");
scanf("%s", name);
//2.查找通讯录是否有这个人(遍历)
for (int i = 0; i < pc->sz; i++)
{
if (strcmp(pc->data[i].name, name) == 0) //=0说明找到了
{
position = i;
break;
}
}
//3.删除(将后面的人覆盖掉要删除的对象)
for (int i = position; i < pc->sz - 1; i++)
{
pc->data[i] = pc->data[i + 1];
}
pc->sz--;
printf("删除成功\n");
}
上面的代码其实还可以再优化一下,大家想:后面还要完善查找联系人信息和修改联系人信息的功能,它们有一个共同的步骤都是要先查找有没有对应的联系人,因此我们可以将查找这一步封装成一个函数。
int fine_by_name(Contact* pc,char name[])
{
for (int i = 0; i < pc->sz; i++)
{
if (strcmp(pc->data[i].name, name) == 0) //=0说明找到了
{
return i;//返回下标
}
}
return -1;
}
void del(Contact* pc)
{
char name[NAME_MAX];
if (pc->sz == 0)
{
printf("您的通讯录没有用户,无法删除\n");
return;
}
//1.找到要删除的人
printf("请输入您要删除的对象:");
scanf("%s", name);
//2.查找通讯录是否有这个人(查找函数)
int res = fine_by_name(pc, name);
if (res == -1)
{
printf("您的通讯录没有这个人\n");
return;
}
//3.删除(将后面的人覆盖掉要删除的对象)
for (int i = res; i < pc->sz - 1; i++)
{
pc->data[i] = pc->data[i + 1];
}
pc->sz--;
printf("删除成功\n");
}
在test.c封装一个search函数
然后在contact.h进行函数的声明
最后在contact.c实现
首先在
test.c
封装一个modify函数
然后在
contact.h
进行声明
最后再
contact.c
中实现即可
void modify(Contact* pc)
{
char name[NAME_MAX];
printf("请输入要修改人的名字:");
scanf("%s", name);
//1.查找
int res = fine_by_name(pc, name);
if (-1 == res)
{
printf("您要修改的对象不存在\n");
return;
}
//2.修改(重新输入)
printf("请输入名字:");
scanf("%s", pc->data[res].name);
printf("请输入年龄:");
scanf("%d", &(pc->data[res].age));
printf("请输入性别:");
scanf("%s", pc->data[res].sex);
printf("请输入地址:");
scanf("%s", pc->data[res].addr);
printf("请输入电话号码:");
scanf("%s", pc->data[res].tele);
printf("修改成功!\n");
}
对于排序大家可以用很多种方式实现,我这里使用的是qsort函数,毕竟是我前几天刚刚学的知识。如果大家也想了解qsort,可以看看我往期的博客 -> 传送门
test.c部分
contact.h部分
contact.c部分
test.c部分
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include "contact.h"
void menu()
{
printf("**********************************\n"); //Add - 添加联系人
printf("**** 1. Add 2. del ****\n"); //del - 删除联系人
printf("**** 3. search 4. modify ****\n"); //search - 查找联系人
printf("**** 5. show 6. sort ****\n"); //modify - 修改联系人
printf("**** 0. exit ****\n"); //show - 展示联系人
printf("**********************************\n"); //sort - 排序
//exit - 退出程序
}
int main()
{
int input = 0;
//创建通讯录
Contact con;
//初始化通讯录
InitContact(&con);//结构体传参
do
{
//创建菜单
menu();
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
Add(&con);
break;
case 2:
del(&con);
break;
case 3:
search(&con);
break;
case 4:
modify(&con);
break;
case 5:
show(&con);
break;
case 6:
sort(&con);
break;
case 0:
printf("退出通讯录\n");
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
return 0;
}
contact.h部分
#define _CRT_SECURE_NO_WARNINGS 1
#define MAX 100 //人的个数最大值
#define NAME_MAX 20//人名字符串的最大值
#define SEX_MAX 5 //性别字符的最大值
#define ADDR_MAX 30 //地址字符的最大值
#define TELE_MAX 12 //电话字符的最大值
//人的信息
typedef struct PeoInfo
{
char name[NAME_MAX]; //名字
int age; //年龄
char sex[SEX_MAX]; //性别
char addr[ADDR_MAX]; //地址
char tele[TELE_MAX]; //电话
}PeoInfo;
//创建通讯录
typedef struct Contact
{
PeoInfo data[MAX]; //存放人的信息
int sz;//表示当前通讯录有多少个人的信息
}Contact;
//初始化通讯录函数
void InitContact(Contact* pc);
//增加联系人
void Add(Contact* pc);
//显示联系人
void show(Contact* pc);
//删除联系人
void del(Contact* pc);
//查找联系人
void search(Contact* pc);
//修改联系人
void modify(Contact* pc);
//排序
void sort(Contact* pc);
contact.c部分
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
#include "contact.h"
//通讯录的初始化
void InitContact(Contact* pc)
{
pc->sz = 0;
memset(pc->data, 0, sizeof(pc->data)); //初始化为0
}
void Add(Contact* pc)
{
if (pc ->sz == MAX) //MAX在头文件定义了,表示通讯录最大人数
{
printf("添加失败,通讯录空间已满\n");
return;
}
//否则就能增加一个人的信息
printf("请输入名字:");
scanf("%s", pc->data[pc->sz].name);//name是数组名
printf("请输入年龄:");
scanf("%d", &(pc->data[pc->sz].age));
printf("请输入性别:");
scanf("%s", pc->data[pc->sz].sex);//sex是数组名
printf("请输入地址:");
scanf("%s", pc->data[pc->sz].addr);//addr是数组名
printf("请输入电话号码:");
scanf("%s", pc->data[pc->sz].tele);//tele是数组名
pc->sz++; //增加一个人对于的sz也要增加
}
void show(Contact* pc)
{
printf("%s\t%s\t%s\t%s\t%s\n", "名字", "年龄", "性别", "地址", "电话");
for (int i = 0; i < pc->sz; i++)
{
printf("%s\t%d\t%s\t%s\t%s\n", pc->data[i].name,
pc->data[i].age,
pc->data[i].sex,
pc->data[i].addr,
pc->data[i].tele);
}
}
int fine_by_name(Contact* pc,char name[])
{
for (int i = 0; i < pc->sz; i++)
{
if (strcmp(pc->data[i].name, name) == 0) //=0说明找到了
{
return i;
}
}
return -1;
}
void del(Contact* pc)
{
char name[NAME_MAX];
if (pc->sz == 0)
{
printf("您的通讯录没有用户,无法删除\n");
return;
}
//通讯录有人删除
//1.找到要删除的人
printf("请输入您要删除的对象:");
scanf("%s", name);
//2.查找通讯录是否有这个人
//查找函数
int res = fine_by_name(pc, name);
if (res == -1)
{
printf("您的通讯录没有这个人\n");
return;
}
//3.删除(将后面的人覆盖掉要删除的对象)
for (int i = res; i < pc->sz - 1; i++)
{
pc->data[i] = pc->data[i + 1];
}
pc->sz--;
printf("删除成功\n");
}
void search(Contact* pc)
{
char name[NAME_MAX];
printf("请输入要查找人的名字:");
scanf("%s", name);
//利用上一步的查找函数
int res = fine_by_name(pc, name);
if (-1 == res)
{
printf("您查找的该用户不存在\n");
return;
}
//找到了就打印
printf("%s\t%s\t%s\t%s\t%s\n", "名字", "年龄", "性别", "地址", "电话");
printf("%s\t%d\t%s\t%s\t%s\n", pc->data[res].name,
pc->data[res].age,
pc->data[res].sex,
pc->data[res].addr,
pc->data[res].tele);
}
void modify(Contact* pc)
{
char name[NAME_MAX];
printf("请输入要修改人的名字:");
scanf("%s", name);
//1.查找
int res = fine_by_name(pc, name);
if (-1 == res)
{
printf("您要修改的对象不存在\n");
return;
}
//2.找到(重新输入)
printf("请输入名字:");
scanf("%s", pc->data[res].name);
printf("请输入年龄:");
scanf("%d", &(pc->data[res].age));
printf("请输入性别:");
scanf("%s", pc->data[res].sex);
printf("请输入地址:");
scanf("%s", pc->data[res].addr);
printf("请输入电话号码:");
scanf("%s", pc->data[res].tele);
printf("修改成功!\n");
}
int cmp_name(void* p1, void* p2)
{
return strcmp(((PeoInfo*)p1)->name, ((PeoInfo*)p2)->name);
}
int cmp_age(void* p1, void* p2)
{
return ((PeoInfo*)p1)->age - ((PeoInfo*)p2)->age;
}
void sort(Contact* pc)
{
int input = 0;
printf("请选择排序方式(默认从小到大):1(年龄)/2(姓名):");
scanf("%d", &input);
if (input == 2)
qsort(pc, pc->sz, sizeof(PeoInfo), cmp_name);
else
qsort(pc, pc->sz, sizeof(PeoInfo), cmp_age);
printf("恭喜你排序成功,请在选择show中观察效果\n");
}