通讯录中首先要有人的信息,然后是存放多少个人的信息
再丰富一下通讯录的功能,例如增删查改、显示、排序。
我们分三个文件来实现。
1、实现简易的菜单,通讯录的整体逻辑
#include"contact.h"
void menu()
{
printf("***********通讯录contact**************\n");
printf("*****1、add 2、del 3、search***\n");
printf("*****4、update 5、show 6、sort1 ** \n");
printf("*****7、sort2 0、exit**************\n");
printf("**************************************\n");
}
int main()
{
int input = 0;
do
{
menu();
printf("请选择要使用的功能\n");
scanf("%d", &input);
switch (input)
{
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
break;
case 5:
break;
case 6:
break;
case 7:
break;
case 0:
printf("退出通讯录\n");
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
}
case的不同情况对应菜单中的不同功能。考虑到枚举常量的知识点,可以将上面的功能对应枚举常量的数字,用来代替单一的case+数字,从而更加直观。
enum
{
EXIT,
ADD,
DEL,
SEARCH,
UPDATE,
SHOW,
SORT1,
SORT2,
};
优化后的菜单代码
int main()
{
int input = 0;
do
{
menu();
printf("请选择要使用的功能\n");
scanf("%d", &input);
switch (input)
{
case ADD:
break;
case DEL:
break;
case SEARCH:
break;
case UPDATE:
break;
case SHOW:
break;
case SORT1:
break;
case SORT2:
break;
case EXIT:
printf("退出通讯录\n");
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
}
2、实现每个人信息包含的内容
一个人的信息包含姓名、年龄、电话等不同类型的数据,因此对于每个人的信息,我们可以选择使用结构体类型的数据。
struct peopleinfo
{
char name[20];
int age;
char sex[5];
char tel[12];
char dres[10];
};
这里我们暂时规定 姓名 年龄 性别 电话 地址 五个元素。除年龄外,均为字符数组。
为了我们代码灵活性的考虑,我们在实现某个功能时尽量不要把数字写死(逻辑功能除外),相反,我们可以采用#define定义的标识符常量来替换它。
#define MAX_NAME 20
#define MAX_SEX 5
#define MAX_TEL 12
#define MAX_DRES 10
替换后的人员信息
struct peopleinfo
{
char name[MAX_NAME];
int age;
char sex[MAX_SEX];
char tel[MAX_TEL];
char dres[MAX_DRES];
};
3、通讯录N个人的包含
定义完每个人的个人信息,我们就需要将N个人的信息统一起来。
这里我们可以用创建一个结构体数组,其中包含N个元素。
在这之前,我们可以用typedef使结构体的名字简化一些。
typedef struct peopleinfo
{
char name[MAX_NAME];
int age;
char sex[MAX_SEX];
char tel[MAX_TEL];
char dres[MAX_DRES];
}peopleinfo;
#define N 1000
peopleinfo date[N];
这里我们定义数组有1000个元素,即可以存储1000个人的信息。
为了方便实现后续增删查改时确定我们操作数组的范围,这里我们可以定义一个变量sz,用来标记接下来要操作的元素的下标。
然后我们可以将date数组和sz再封装为一个结构体类型contact。(并用typedef简化)
typedef struct contact
{
peopleinfo date[1000];
int sz;
}contact;
4、通讯录的初始化
得到contact这个结构体后,我们可以对它进行初始化。
sz可初始化为0,date数组可按需初始化,这里我们统一初始化为0,但为了代码灵活性考虑,我们将其封装为一个函数。
contact con;
contact_init(&con);
void contact_init(contact* pc)
{
pc->sz = 0;
//初始化可以才用循环遍历,这里我使用memset直接设置
// 设置的起始地址 要设置成的内容 设置的字节数
memset(pc->date, 0, sizeof(pc->date));
//这里用结构体指针直接指向其中的数组,即数组名表示找到这个数组
// sizeof内部单独放数组名,求的是整个数组的大小
}
这里因为是结构体传参,为了保证性能,我们才用传址调用,用一个结构体指针pc接收,然后再函数内部用pc操作结构体。
5、增加人员信息功能
//增加人员信息
void contact_add(contact* pc)
{
//增加前确保通讯没满
if (pc->sz == N)
{
printf("通讯录已满,无法添加\n");
return; //直接返回,不进行后续功能
}
//通讯录没满,添加信息
printf("请输入姓名\n");
scanf("%s", pc->date[pc->sz].name);
printf("请输入年龄\n");
scanf("%d", &pc->date[pc->sz].age);
//由于其它的都是字符数组,数组名就是地址,不用再加&,int的年龄需要&
printf("请输入性别\n");
scanf("%s", pc->date[pc->sz].sex);
printf("请输入电话\n");
scanf("%s", pc->date[pc->sz].tel);
printf("请输入地址\n");
scanf("%s", pc->date[pc->sz].dres);
//输入后再到下一个元素
pc->sz++;
}
6、显示通讯录
//显示通讯录已有信息
void contact_show(const contact* pc)
{
//先打印一个目录
printf("%-20s\t%-10s\t%-10s\t%-12s\t%-10s\n", "姓名", "年龄", "性别", "电话", "地址");
//各个信息分别打印
int i = 0;
for (i=0;i<pc->sz;i++)
{
printf("%-20s\t%-10d\t%-10s\t%-12s\t%-10s\n", pc->date[i].name,
pc->date[i].age,
pc->date[i].sex,
pc->date[i].tel,
pc->date[i].dres);
}
}
用i遍历数组,挨个结构体,挨个成员打印。
输入两个人的信息后显示。
7、查找某个人的信息是否存在
在删除,查找,更新,等功能中,都需要先找到某个人的信息是否存在,因此我们封装一个查找函数,用来反复使用。
//按名字查找某个人是否存在
int seek_by_name(contact* pc,char name[])
{
int i = 0;
for (i = 0; i < pc->sz; i++)
{
if (strcmp(pc->date[i].name, name) == 0)
{
int pos = i;
return i;//找到后返回数组下标
}
}
//遍历完扔没有返回,即没有找到
return -1;
}
8、删除指定人的信息
删除时有多种方式
1、找到后直接从后往前覆盖。然后sz--,使访问权限-1,完成删除。
2、将最后一个元素直接覆盖到要删除的位置,但是会改变元素的顺序。
这里我们选择往前覆盖的方法。
//删除指定人员信息
void contact_del(contact* pc)
{
assert(pc);
//先判断通讯录内是否还有人员信息
if (pc->sz == 0)
{
printf("通讯录内已无信息,无法删除\n");
return; //直接返回,不进行后续
}
//可以删除时
//先查找要删除的人(例如按照名字)
char name[20];
printf("请输入要删除的人的名字\n");
scanf("%s", name);
//查找要删除的人的名字
int del=seek_by_name(pc,name);
if (del == -1)
{
printf("没有找到要删除人的信息\n");
return;//没找到直接返回,不进行后续操作
}
//找到要删除的元素,进行删除操作
int i = 0;
for (i = del; i < pc->sz - 1; i++)
{
pc->date[i] = pc->date[i + 1];//结构体的两个元素直接覆盖即可
}
//完成删除后sz--,缩小访问权限
pc->sz--;
}
9、查找指定人的信息
//查找指定人员的信息
void contact_search(const contact* pc)
{
printf("请输入要查找的人的名字\n");
char name[20];
scanf("%s", name);
int pos=seek_by_name(pc, name);
if (pos == -1)
{
printf("查无此人\n");
return;//找不到直接返回
}
//找到后打印出来
printf("查找成功,信息为\n");
int i = 0;
printf("%-20s\t%-10s\t%-10s\t%-12s\t%-10s\n", "姓名", "年龄", "性别", "电话", "地址");
for (i = pos; i < pc->sz; i++)
{
printf("%-20s\t%-10d\t%-10s\t%-12s\t%-10s\n",
pc->date[pos].name,
pc->date[pos].age,
pc->date[pos].sex,
pc->date[pos].tel,
pc->date[pos].dres);
}
}
10、更改/更新指定人的信息
//更改指定人员信息
void contact_update(contact* pc)
{
printf("请输入要更改的人的姓名\n");
char name[20];
scanf("%s", name);
int pos=seek_by_name(pc, name);
if (pos == -1)
{
printf("查无此人,无法更改\n");
return;
}
printf("开始更改\n");
printf("请输入姓名\n");
scanf("%s", pc->date[pos].name);
printf("请输入年龄\n");
scanf("%d", &pc->date[pos].age);
printf("请输入性别\n");
scanf("%s", pc->date[pos].sex);
printf("请输入电话\n");
scanf("%s", pc->date[pos].tel);
printf("请输入地址\n");
scanf("%s", pc->date[pos].dres);
printf("信息更新成功\n");
}
11、按照年龄排序(升序)
12、按照名字排序
排序函数可以使用我们之前讲解的bubble_qsort函数来实现
下面附上源码
头文件contact.c
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<assert.h>
#define MAX_NAME 20
#define MAX_SEX 5
#define MAX_TEL 12
#define MAX_DRES 10
#define N 1000
enum
{
EXIT,
ADD,
DEL,
SEARCH,
UPDATE,
SHOW,
SORT1,
SORT2,
};
typedef struct peopleinfo
{
char name[MAX_NAME];
int age;
char sex[MAX_SEX];
char tel[MAX_TEL];
char dres[MAX_DRES];
}peopleinfo;
typedef struct contact
{
peopleinfo date[1000];
int sz;
}contact;
//初始化通讯录
void contact_init(contact* pc);
//增加人员信息
void contact_add(contact* pc);
//显示通讯录已有信息
void contact_show(const contact* pc);
//删除指定人员信息
void contact_del(contact* pc);
//查找指定人员信息
void contact_search(const contact* pc);
//更改指定人员信息
void contact_update(contact* pc);
//按年龄升序
void contact_sort_byage(contact* pc);
//按名字升序
void contact_sort_byname(contact* pc);
test.c
#include"contact.h"
void menu()
{
printf("***********通讯录contact**************\n");
printf("*****1、add 2、del 3、search***\n");
printf("*****4、update 5、show 6、sort1 ** \n");
printf("*****7、sort2 0、exit**************\n");
printf("**************************************\n");
}
int main()
{
contact con;
contact_init(&con);
int input = 0;
do
{
menu();
printf("请选择要使用的功能\n");
scanf("%d", &input);
switch (input)
{
case ADD:
contact_add(&con);
break;
case DEL:
contact_del(&con);
break;
case SEARCH:
contact_search(&con);
break;
case UPDATE:
contact_update(&con);
break;
case SHOW:
contact_show(&con);
break;
case SORT1:
contact_sort_byage(&con);
break;
case SORT2:
contact_sort_byname(&con);
break;
case EXIT:
printf("退出通讯录\n");
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
}
contact.c
#include"contact.h"
//通讯录初始化
void contact_init(contact* pc)
{
assert(pc);
pc->sz = 0;
//初始化可以才用循环便利,这里我使用memset直接设置
// 设置的起始地址 要设置成的内容 设置的字节数
memset(pc->date, 0, sizeof(pc->date));
//这里用结构体指针直接指向其中的数组,即数组名表示找到这个数组
// sizeof内部单独放数组名,求的是整个数组的大小
}
//增加人员信息
void contact_add(contact* pc)
{
assert(pc);
//增加前确保通讯没满
if (pc->sz == N)
{
printf("通讯录已满,无法添加\n");
return; //直接返回,不进行后续功能
}
//通讯录没满,添加信息
printf("请输入姓名\n");
scanf("%s", pc->date[pc->sz].name);
printf("请输入年龄\n");
scanf("%d", &pc->date[pc->sz].age);
//由于其它的都是字符数组,数组名就是地址,不用再加&,int的年龄需要&
printf("请输入性别\n");
scanf("%s", pc->date[pc->sz].sex);
printf("请输入电话\n");
scanf("%s", pc->date[pc->sz].tel);
printf("请输入地址\n");
scanf("%s", pc->date[pc->sz].dres);
//输入后再到下一个元素
pc->sz++;
}
//显示通讯录已有信息
void contact_show(const contact* pc)
{
assert(pc);
//先打印一个目录
printf("%-20s\t%-10s\t%-10s\t%-12s\t%-10s\n", "姓名", "年龄", "性别", "电话", "地址");
//各个信息分别打印
int i = 0;
for (i=0;i<pc->sz;i++)
{
printf("%-20s\t%-10d\t%-10s\t%-12s\t%-10s\n", pc->date[i].name,
pc->date[i].age,
pc->date[i].sex,
pc->date[i].tel,
pc->date[i].dres);
}
}
//按名字查找某个人是否存在
int seek_by_name(const contact* pc,char name[])
{
int i = 0;
for (i = 0; i < pc->sz; i++)
{
if (strcmp(pc->date[i].name, name) == 0)
{
int pos = i;
return i;//找到后返回数组下标
}
}
//遍历完扔没有返回,即没有找到
return -1;
}
//删除指定人员信息
void contact_del(contact* pc)
{
assert(pc);
//先判断通讯录内是否还有人员信息
if (pc->sz == 0)
{
printf("通讯录内已无信息,无法删除\n");
return; //直接返回,不进行后续
}
//可以删除时
//先查找要删除的人(例如按照名字)
char name[20];
printf("请输入要删除的人的名字\n");
scanf("%s", name);
//查找要删除的人的名字
int del=seek_by_name(pc,name);
if (del == -1)
{
printf("没有找到要删除人的信息\n");
return;//没找到直接返回,不进行后续操作
}
//找到要删除的元素,进行删除操作
int i = 0;
for (i = del; i < pc->sz - 1; i++)
{
pc->date[i] = pc->date[i + 1];//结构体的两个元素直接覆盖即可
}
//完成删除后sz--,缩小访问权限
pc->sz--;
}
//查找指定人员的信息
void contact_search(const contact* pc)
{
printf("请输入要查找的人的名字\n");
char name[20];
scanf("%s", name);
int pos=seek_by_name(pc, name);
if (pos == -1)
{
printf("查无此人\n");
return;//找不到直接返回
}
//找到后打印出来
printf("查找成功,信息为\n");
int i = 0;
printf("%-20s\t%-10s\t%-10s\t%-12s\t%-10s\n", "姓名", "年龄", "性别", "电话", "地址");
for (i = pos; i < pc->sz; i++)
{
printf("%-20s\t%-10d\t%-10s\t%-12s\t%-10s\n",
pc->date[pos].name,
pc->date[pos].age,
pc->date[pos].sex,
pc->date[pos].tel,
pc->date[pos].dres);
}
}
//更改指定人员信息
void contact_update(contact* pc)
{
printf("请输入要更改的人的姓名\n");
char name[20];
scanf("%s", name);
int pos=seek_by_name(pc, name);
if (pos == -1)
{
printf("查无此人,无法更改\n");
return;
}
printf("开始更改\n");
printf("请输入姓名\n");
scanf("%s", pc->date[pos].name);
printf("请输入年龄\n");
scanf("%d", &pc->date[pos].age);
printf("请输入性别\n");
scanf("%s", pc->date[pos].sex);
printf("请输入电话\n");
scanf("%s", pc->date[pos].tel);
printf("请输入地址\n");
scanf("%s", pc->date[pos].dres);
printf("信息更新成功\n");
}
//按照 结构体 年龄大小判断是否交换的函数
int exchange_by_age(const void* e1, const void* e2)
{
return ((peopleinfo*)e1)->age - ((peopleinfo*)e2)->age;
}
//按照 结构体 字符串大小比较判断是否交换的函数
int exchange_by_name(const void* e1, const void* e2)
{
return strcmp(((peopleinfo*)e1)->name, ((peopleinfo*)e2)->name);
}
//逐字节交换函数
void swap_by_byte(char* low, char* high, size_t width)
{
int i = 0;
for (i = 0; i < width; i++)
{
char tmp = *low;
*low = *high;
*high = tmp;
low++;
high++;
}
}
//排序函数
void bubble_sort(void* base, size_t num, size_t width, int (*pf)(const void*, const void*))
{
int i = 0;
//冒泡排序的躺数
for (i = 0; i < num - 1; i++)
{
//每一趟冒泡排序
int j = 0;
for (j = 0; j < num - 1 - i; j++)
{
//判断是否需要交换
int ret = pf((char*)base + j * width, (char*)base + (j + 1) * width);
if (ret > 0)
{
//交换相邻的两个元素
swap_by_byte((char*)base + j * width, (char*)base + (j + 1) * width, width);
}
}
}
}
//按年龄升序排列
void contact_sort_byage(contact* pc)
{
bubble_sort(pc->date, pc->sz, sizeof(peopleinfo), exchange_by_age);
printf("按照年龄排序成功\n");
}
//按名字升序排列
void contact_sort_byname(contact* pc)
{
bubble_sort(pc->date, pc->sz, sizeof(peopleinfo), exchange_by_name);
printf("按名字排序成功\n");
}
大家可以动手敲一敲,遇到困难就去解决一下。
目录
1、实现简易的菜单,通讯录的整体逻辑
2、实现每个人信息包含的内容
3、通讯录N个人的包含
4、通讯录的初始化
5、增加人员信息功能
编辑
6、显示通讯录
7、查找某个人的信息是否存在
8、删除指定人的信息
9、查找指定人的信息
10、更改/更新指定人的信息
11、按照年龄排序(升序)
12、按照名字排序