文章目录
前言
学习了结构体、枚举、联合这三种自定义类型,再融合之前学过的知识,尝试写一个通讯录程序。
要求:
- 该通讯录能存放1000个人的信息。每个人的信息包括:姓名+年龄+性别+电话+地址;
- 增加通讯录信息;
- 删去通讯录信息;
- 查找通讯录信息;
- 修改通讯录信息;
- 排序通讯录信息;
一、实现思路
1.1 整体架构搭建
当拿到这个题目后,我们发现要求我们实现增删查改这些基本上算是互相独立的功能,既然如此,我们就考虑到在主函数中使用switch结构,通过case的值来选择启动相应的功能函数。
因为程序的运行并非使用一次就退出,因此考虑do while循环。为了能选择各功能,需要一个input变量来存放用户所键入的数字,switch也是根据这个键入的数字来判断执行哪个功能。
既然确定了主函数使用的架构,那么我们现在需要一个菜单选择功能函数。
为了让代码脉络清晰,考虑将所有功能函数写在addressBook.c文件中,结构体等类型的声明与定义以及函数的声明都写在addressBook.h文件中。而主函数则写在main.c文件中。这样操作以使代码清晰明确、可读性高。
再考虑到switch语句中case的值都是1、2、3、4这种自然数,其他人看到一时不知道该对应哪一个功能,那么这里可以使用一个枚举类型,将功能对应的数字存储到一个意义鲜明的枚举常量中。
//main.c中的架构:
#include "addressBook.h"
int main()
{
int input = 0;
do {
menu();//打印菜单,以供选择
printf("请选择功能 >\n");
scanf("%d", &input);
switch (input)
{
//增加通讯录信息:
case ADD:
break;
//删除通讯录信息:
case DEL:
break;
//查找通讯录信息:
case SEARCH:
break;
//修改通讯录信息:
case MODIFY:
break;
//退出程序:
case EXIT:
break;
default:
break;
}
} while (input);
}
//.h文件:
#include <stdio.h>
#include <string.h>
enum fun
{
EXIT,//从0开始,EXIT = 0
ADD,//1
DEL,//2
SEARCH,//3
MODIFY//4
};
1.2 通讯录结构体定义与声明
因为通讯录的个人信息中包含多种信息,这里考虑使用结构体来实现,当然,这个结构体应该定义在.h文件中。
为了方便全局使用,我们把结构体重命名为PeoInfo,这样以后定义结构体时可以省去struct关键字。同时结构体内数组的大小也直接用#define宏定义,方便所有的文件使用。
考虑到我们在通讯录的使用中需要对到底是第几个人的信息进行增删查改,因此需要一个计数变量来方便我们增删查改,因此再封装一个通讯录结构体,这个结构体内嵌套一个个人信息结构体,再加一个计数器size。
#define MAX_NAME 20
#define MAX_SEX 6
#define MAX_TELE 12
#define MAX_ADDR 20
//个人信息结构体:
typedef struct PeoInfo
{
char name[MAX_NAME];
int age;
char sex[MAX_SEX];
char tele[MAX_TELE];
char addr[MAX_ADDR];
}PeoInfo;
//通讯录结构体:
typedef struct Book
{
PeoInfo data[1000];//记录1000个个人信息
int size;//大小:1000
}Book;
声明结构体后,需要在主函数main.c中定义一个结构体变量来存放通讯录内容:
Book myBook = {
0};//定义一个通讯录并初始化
1.3 增加通讯录信息函数
在case ADD:下编写一个实现增加通讯录信息功能的函数,该函数的参数应该传什么呢?是直接传我们定义的通讯录myBook吗?
当然不行,因为值传参传过去修改的时候只是在修改形参中定义的那个结构体的内容。因此需要传地址过去!
//增加通讯录信息功能函数:
void AddMsg(Book* pc)
{
printf("请输入姓名:");
scanf("%s", pc->data[pc->size].name);
printf("请输入年龄:");
scanf("%d", &(pc->data[pc->size].age));
printf("请输入性别:");
scanf("%s", pc->data[pc->size].sex);
printf("请输入电话:");
scanf("%s", pc->data[pc->size].tele);
printf("请输入地址:");
scanf("%s", pc->data[pc->size].addr);
pc->size++;
printf("增加成功\n");
}
上面的代码就是增加通讯录信息的功能函数。我们通过操作符“->”“.”依次将信息录入到通讯录中取。需要注意的是,在录入年龄的scanf函数中,要用取地址符。其他不用是因为其他的信息在定义时都被定义成数组,因此数组名本身就是地址,无需取地址。
当录入信息后,我们最好能打印浏览一下整个通讯录。为了能让打印的好看一点,要固定每个内容的打印宽度,并左对齐。例如%-10s,就是左对齐打印10个宽度的字符串。
打印内容时,可以直接使用for循环遍历打印,注意i的值应该小于size值,这样才会把所有的信息打印出来而不至于少打或者多打空信息。
//打印浏览通讯录信息功能函数:
void PrintMsg(Book* pc)
{
//打印标题栏:
printf("%-10s\t%-4s\t%-6s\t%-12s\t%-20s\n", "姓名", "年龄", "性别", "电话", "地址");
//打印内容:
for (int i = 0; i < pc->size; i++)
{
printf("%-10s\t%-4d\t%-6s\t%-12s\t%-20s\n", pc->data[i].name,
pc->data[i].age,
pc->data[i].sex,
pc->data[i].tele,
pc->data[i].addr);
}
}
1.4 删除通讯录信息函数与查找信息函数
为了删除某条通讯录信息,我们需要使用者确定到底要删除哪一条,因此在删除前也需要一个寻找函数,来找到我们要删的那一条。这里我们考虑以姓名来寻找,毕竟一般要寻找某个人的话都是通过姓名来寻找的。
而除了删除功能之外,查找、修改这些功能都需要先通过姓名寻找某条通讯录信息来进行操作,因此这里我们首先编写一个函数来实现通过姓名寻找通讯录信息!
这个函数的编写需要考虑到:找不到返回-1,通过这个返回值来提示姓名输入错误找不到。找到了就返回找到的下标,以使其他函数根据下标进行对这条信息的相应操作。同时,这个函数在其他文件用不到,因此可以考虑加上static关键字让函数更安全。
//通过姓名来寻找某条通讯录信息函数:
static int FindByNmae(Book *pc, char* name)
{
for (int i = 0; i < pc->size; i++)
{
if (strcmp(nam