要求:
实现一个通讯录。
(1)人的信息:
包括姓名、年龄、性别、电话地址。
(2)功能:
①存放一百个人的信息。
②增加联系人。
③删除指定联系人。
④查找指定联系人。
⑤修改联系人。
⑥排序。
⑦显示联系人。
(3)文件:
①contact.c:通讯录实现的文件。
②contact.h:通讯录声明的文件。
③test.c :测试通讯录的文件。
目录
[2. 结构体的声明以及初始化](#2. 结构体的声明以及初始化)
[2.1 结构体的声明](#2.1 结构体的声明)
[2.2 结构体的初始化](#2.2 结构体的初始化)
[3. 通讯录的增加功能](#3. 通讯录的增加功能)
[4. 展示通讯录的功能](#4. 展示通讯录的功能)
[7. 修改联系人](#7. 修改联系人)
[9.1 contact.h](#9.1 contact.h)
[9.2 contact.c](#9.2 contact.c)
[9.3 test.c](#9.3 test.c)
[10.2 删除](#10.2 删除)
[10.3 查找](#10.3 查找)
[10.4 修改](#10.4 修改)
[10.5 排序](#10.5 排序)
1.框架的搭建
整体采用dowhile的结构,读取用户的输入根据不同的输入选项,调用不同的方法从而实现不同的功能。
在test.c中代码如下:
cpp
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
void menu()
{
printf("=====================================");
printf("======1.add 2.del ========");
printf("======3.search 4.modify ========");
printf("======5.show 6.sort ========");
printf("======0.exit ========");
printf("=====================================");
}
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 0:
printf("程序已退出!\n");
break;
default:
printf("请正确输入选项!\n");
break;
}
} while (input);
return 0;
}
2. 结构体的声明以及初始化
2.1 结构体的声明
这个部分需要在头文件中contact.h,编写完毕之后若要使用改结构体需要在test.c中引用该头文件。
首先声明一个人信息的结构体,具体成员变量详见文章开头。

我们思考一下,如果我们在主函数中创建了一个100个人信息的结构体数组,我们还需要一个变量来记录这100个变量中实际存放的变量的个数,那么我们可以封装成一个通讯录结构体,通讯录结构体中包含一个人信息结构体数组和实际存放结构体变量的数目。
此时可以在主函数声明一个通讯录结构体变量,这样就会更加的方便。

2.2 结构体的初始化
需要将通讯录结构体变量进行初始化,我们可以在头文件声明一个初始化函数形参传入指针;将成员变量count置为0,将成员变量PeoInfo 类型的数组全部置为0,可以使用memset来实现。

这里需要注意一个细节,既然测试函数和逻辑函数都引用头文件,那么不妨直接把第三方库全部放在头文件中,这样一来只需要引用头文件即可。
3. 通讯录的增加功能
①在头文件中进行方法的声明,形参需要传入通讯录变量的地址;
②具体实现在contact.c中实现。
③首先需要判断指针是否为空。
④如果通讯录的count == 100,说明通讯录已经满了,提示用户并且直接返回。
这里需要注意的是,在代码中出现的数字都可以使用define关键字进行宏定义,后面方便修改。
⑤count可以当做结构体数组下标一用,使用scanf来输入数据,这里需要注意的是数组名本就是地址,除了age需要取地址,剩余四个成员变量不需要取地址。
cpp
// 通讯录的增加方法
void addContact(Contact* p)
{
assert(p); // 断言空指针
if (p->count == N)
{
printf("通讯录已满!存不下了!\n");
return;
}
printf("请输入姓名:\n");
scanf("%s",(p->list)[p->count].name);
printf("请输入年龄:\n");
scanf("%d", &((p->list)[p->count].age));
printf("请输入性别:\n");
scanf("%s", (p->list)[p->count].gender);
printf("请输入电话号码:\n");
scanf("%s", (p->list)[p->count].tele);
printf("请输入地址:\n");
scanf("%s", (p->list)[p->count].addr);
(p->count)++;
printf("通讯录增加成功!");
}
4. 展示通讯录的功能
①首先在头文件定义方法,这个方法的形参是一个const Contact的指针,因为只需要展示无需修改。
②遍历打印p指针指向的结构体即可。
③唯一需要注意的是:输出的格式要对齐,使用左对齐,在%后加上一个-号。
cpp
// 通讯录的显示
void showContact(const Contact* p)
{
assert(p);
int i = 0;
printf("%-20s\t%-5s\t%-5s\t%-12s\t%-30s\n"
,"姓名","年龄","性别","电话","地址"
);
for(i = 0;i < p->count; i++)
{
printf("%-20s\t%-5d\t%-5s\t%-12s\t%-30s\n",
(p->list)[i].name,
(p->list)[i].age,
(p->list)[i].gender,
(p->list)[i].tele,
(p->list)[i].addr);
}
}
左对齐的样式:

5.删除、查找指定联系人
①首先需要判断Contact数组有效元素是否为空,如果为空直接返回;
②删除之前需要读取联系人姓名,根据姓名来查找,找到了就返回下标,没找到就返回-1;
③查找这个动作在修改、查找等功能都会用到,所以可以先封装成一个函数,具有两个参数,第一个参数是结构体的指针,第二个是查找的名字。
(1)查找的逻辑:
a.遍历通讯录中的PeoInfo数组;
b.判断数组中的姓名是否和传入姓名一致;
c.一致就返回下标,没找到就返回-1;
cpp
static int findByname(const Contact* p,char name[])
{
assert(p);
int i = 0;
for ( i = 0; i < p->count; i++)
{
if (strcmp(p->list[i].name,name ) == 0)
{
return i;
}
}
return -1;
}
查找函数不在外界暴露,所以不需要声明,最好加上关键字static修饰,只有本文件能够访问。
④查找到了之后需要根据下标进行删除,其实就是将数组的此下标的后面元素向前覆盖即可;需要将有效数据count再-1;
cpp
// 通讯录的删除
void deleContact(Contact* p)
{
char name[NAME_LONG] = { 0 };
assert(p);
// 判断通讯录联系人数量为0
if (p->count == 0)
{
printf("通讯录为空,无法删除\n");
}
printf("请输入要删除的联系人姓名\n");
scanf("%s", name);
int ret = findByname(p, name);
if (ret == -1)
{
printf("删除的联系人不存在!\n");
return;
}
else
{
// 删除
for (int i = ret; i < p->count - 1; i++)
{
p->list[i] = p->list[i + 1];
}
p->count--; // 有效的数据-1
printf("删除成功!!\n");
}
}
6.查找联系人
我们之前已经写过函数了,所以这里只需要封装一层。
cpp
// 通讯录的查找
void SearchContact(Contact* p)
{
char name[NAME_LONG];
printf("请输入查找的姓名:\n");
scanf("%s",name);
int ret = findByname(p, name);
if (ret == -1)
{
printf("该姓名不存在!\n");
return;
}
else
{
printf("查找成功!以下是该联系人的信息:\n");
printf("%-20s\t%-5s\t%-5s\t%-12s\t%-30s\n"
, "姓名", "年龄", "性别", "电话", "地址");
printf("%-20s\t%-5d\t%-5s\t%-12s\t%-30s\n",
(p->list)[ret].name,
(p->list)[ret].age,
(p->list)[ret].gender,
(p->list)[ret].tele,
(p->list)[ret].addr);
}
}
7. 修改联系人
①先根据姓名查找。
②找到了就根据下标来进行修改(和添加联系人雷同);
③找不到就提示并返回。
cpp
// 通讯录的修改
void ModifyContact(Contact* p)
{
char name[NAME_LONG];
printf("请输入要修改的联系人姓名:\n");
scanf("%s", name);
int ret = findByname(p, name);
if (ret == -1)
{
printf("该姓名不存在!\n");
return;
}
else
{
printf("请输入修改姓名:\n");
scanf("%s", (p->list)[ret].name);
printf("请输入修改年龄:\n");
scanf("%d", &((p->list)[ret].age));
printf("请输入修改性别:\n");
scanf("%s", (p->list)[ret].gender);
printf("请输入修改电话号码:\n");
scanf("%s", (p->list)[ret].tele);
printf("请输入修改地址:\n");
scanf("%s", (p->list)[ret].addr);
printf("通讯录修改成功!\n");
}
}
8.排序联系人
①直接使用qsort函数即可,唯一需要注意的是明确按照什么来排序,这里按照姓名来排序
②首先保证传入的指针有效。
③qsort的第一个参数是排序的起始地址,第二个参数是排序元素的个数,第三个参数是每一个元素的字节数,第四个是一个比较函数,这个比较函数的形参有两个void*指针,需要强转成排序的两个元素的指针类型。
cpp
size_t contact_cmp(const void* e1, const void* e2)
{
return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
}
// 通讯录的排序
void SortContact(Contact* p)
{
assert(p);
qsort(p->list, p->count, sizeof(p->list[0]), contact_cmp);
printf("排序成功!\n");
}
9.完整代码
9.1 contact.h
cpp
#define _CRT_SECURE_NO_WARNINGS
#define N 100
#define NAME_LONG 20
#define GENDER_LONG 10
#define TELE_LONG 12
#define ADDRESS_LONG 30
#include<string.h>
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
// 人信息的结构体声明
typedef struct PeoInfo
{
char name[NAME_LONG]; // 姓名
int age; // 年龄
char gender[GENDER_LONG]; // 性别
char tele[TELE_LONG]; // 电话号码
char addr[ADDRESS_LONG]; // 地址
}PeoInfo;
// 通讯录结构体的声明
typedef struct Contact
{
// 通讯录假如可以存放一百条信息
PeoInfo list[N];
// 真实存放的信息的条数
int count;
}Contact;
// 初始化通讯录结构体变量
void InitContact(Contact* p);
// 通讯录的增加方法
void addContact(Contact* p);
// 通讯录的显示
void showContact(const Contact* p);
// 通讯录的删除
void deleContact(Contact* p);
// 通讯录的查找
void SearchContact(Contact* p);
// 通讯录的修改
void ModifyContact(Contact* p);
// 通讯录的排序
void SortContact(Contact* p);
9.2 contact.c
cpp
#define _CRT_SECURE_NO_WARNINGS
#include"contact.h"
// 初始化通讯录结构体变量
void InitContact(Contact* p)
{
assert(p);
p->count = 0;
memset(p, 0, sizeof(p->list)); // list是PeoInfo的结构体数组
}
// 通讯录的增加方法
void addContact(Contact* p)
{
assert(p);
if (p->count == N)
{
printf("通讯录已满!存不下了!\n");
return;
}
printf("请输入姓名:\n");
scanf("%s", (p->list)[p->count].name);
printf("请输入年龄:\n");
scanf("%d", &((p->list)[p->count].age));
printf("请输入性别:\n");
scanf("%s", (p->list)[p->count].gender);
printf("请输入电话号码:\n");
scanf("%s", (p->list)[p->count].tele);
printf("请输入地址:\n");
scanf("%s", (p->list)[p->count].addr);
(p->count)++;
printf("通讯录增加成功!\n");
}
// 通讯录的显示
void showContact(const Contact* p)
{
assert(p);
int i = 0;
printf("%-20s\t%-5s\t%-5s\t%-12s\t%-30s\n"
, "姓名", "年龄", "性别", "电话", "地址"
);
for (i = 0; i < p->count; i++)
{
printf("%-20s\t%-5d\t%-5s\t%-12s\t%-30s\n",
(p->list)[i].name,
(p->list)[i].age,
(p->list)[i].gender,
(p->list)[i].tele,
(p->list)[i].addr);
}
}
static int findByname(const Contact* p, char name[])
{
assert(p);
int i = 0;
for (i = 0; i < p->count; i++)
{
if (strcmp(p->list[i].name, name) == 0)
{
return i;
}
}
return -1;
}
// 通讯录的删除
void deleContact(Contact* p)
{
char name[NAME_LONG] = { 0 };
assert(p);
// 判断通讯录联系人数量为0
if (p->count == 0)
{
printf("通讯录为空,无法删除\n");
}
printf("请输入要删除的联系人姓名\n");
scanf("%s", name);
int ret = findByname(p, name);
if (ret == -1)
{
printf("删除的联系人不存在!\n");
return;
}
else
{
// 删除
for (int i = ret; i < p->count - 1; i++)
{
p->list[i] = p->list[i + 1];
}
p->count--; // 有效的数据-1
printf("删除成功!!\n");
}
}
// 通讯录的查找
void SearchContact(Contact* p)
{
char name[NAME_LONG];
printf("请输入查找的姓名:\n");
scanf("%s", name);
int ret = findByname(p, name);
if (ret == -1)
{
printf("该姓名不存在!\n");
return;
}
else
{
printf("查找成功!以下是该联系人的信息:\n");
printf("%-20s\t%-5s\t%-5s\t%-12s\t%-30s\n"
, "姓名", "年龄", "性别", "电话", "地址"
);
printf("%-20s\t%-5d\t%-5s\t%-12s\t%-30s\n",
(p->list)[ret].name,
(p->list)[ret].age,
(p->list)[ret].gender,
(p->list)[ret].tele,
(p->list)[ret].addr);
}
}
// 通讯录的修改
void ModifyContact(Contact* p)
{
char name[NAME_LONG];
printf("请输入要修改的联系人姓名:\n");
scanf("%s", name);
int ret = findByname(p, name);
if (ret == -1)
{
printf("该姓名不存在!\n");
return;
}
else
{
printf("请输入修改姓名:\n");
scanf("%s", (p->list)[ret].name);
printf("请输入修改年龄:\n");
scanf("%d", &((p->list)[ret].age));
printf("请输入修改性别:\n");
scanf("%s", (p->list)[ret].gender);
printf("请输入修改电话号码:\n");
scanf("%s", (p->list)[ret].tele);
printf("请输入修改地址:\n");
scanf("%s", (p->list)[ret].addr);
printf("通讯录修改成功!\n");
}
}
size_t contact_cmp(const void* e1, const void* e2)
{
return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
}
// 通讯录的排序
void SortContact(Contact* p)
{
assert(p);
qsort(p->list, p->count, sizeof(p->list[0]), contact_cmp);
printf("排序成功!\n");
}
9.3 test.c
cpp
#define _CRT_SECURE_NO_WARNINGS
#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("======0.exit ========\n");
printf("========================================\n");
}
int main()
{
// 声明通讯录结构体变量
Contact con;
// 初始化通讯录结构体变量
InitContact(&con);
int input = 0;
do
{
menu();
printf("请输入一个选项:\n");
scanf("%d", &input);
switch (input)
{
case 1:
addContact(&con);
break;
case 2:
deleContact(&con);
break;
case 3:
SearchContact(&con);
break;
case 4:
ModifyContact(&con);
break;
case 5:
showContact(&con);
break;
case 6:
SortContact(&con);
break;
case 0:
printf("程序已退出!\n");
break;
default:
printf("请正确输入选项!\n");
break;
}
} while (input);
return 0;
}
10.功能演示
10.1添加、展示

10.2 删除

10.3 查找

10.4 修改

10.5 排序
