C语言通讯录
文章目录
1.需求分析
首先我们要自己写一个通讯录,我们就要先知道通讯录大概包括哪些内容。
这里我们就设计一个最简单的版本,可以容纳1000个人的静态通讯录。
通讯录中要包含:联系人名字、年龄、性别、电话、住址五个基本信息。
我们对通讯录的操作无非就是增、删、查、改四大类。
所以接下来我们要实现以下几个模块:
- 添加联系人信息
- 删除指定联系人信息
- 查找指定联系人信息
- 修改指定联系人信息
- 显示所有联系人信息
- 清空所有联系人
- 排序
综上我们采用分为三个文件进行编写
test.c
用于测试程序。
contact.h
用于头文件的引用,类型定义,函数声明等。
contact.c
用于实现程序的主要业务代码等。
2. 具体实现
2.1 准备工作
当我们得到以上信息时就可以逐步开始实现我们的通讯录了。
首先通讯录要包含:联系人名字、年龄、性别、电话、住址五个基本信息。显而易见我们要定义一个结构体来表示一个人的信息
// 联系人的信息
typedef struct PeoInfo
{
char name[10];
int age;
char sex[5];
char tele[12];
char addr[30];
} PeoInfo;
我们这样写真的是好的写法吗?就比如名字我们只给了10个字符,如果有的名字超过了10个字符,那我们程序不就错了吗?
所以我们采用定义符号常量的方法,来便于以后不够了对其进行修改。
#define NAME_MAX 10 // 名字最大容量
#define SEX_MAX 5 // 性别最大容量
#define TELE_MAX 12 // 电话最大容量
#define ADDR_MAX 30 // 地址最大容量
// 联系人的信息
typedef struct PeoInfo
{
char name[NAME_MAX]; // 名字
int age; // 年龄
char sex[SEX_MAX]; // 性别
char tele[TELE_MAX]; // 电话
char addr[ADDR_MAX]; // 住址
} PeoInfo;
这时我们已经定义好了每个联系人的结构体类型,可以创建我们的数组了。
#define MAX_SIZE 1000 // 通讯录最大容量
PeoInfo con[MAX_SIZE]; // 存储1000个人的信息
这样写总觉得少了些什么?我们并不知道此时通讯录中到底有多少数据。这里有人可能直接说那还不简单,我再定义一个变量 sz 来表示当前通讯录中有效信息的个数不就可以了吗? 这个办法可以是可以,但是欠缺考虑。比如我们在对函数传参时 ,不仅要把结构体传过去,还要单独再传一个 sz表示当前通讯录中有效信息的个数 ,这就比较麻烦了。
所以我们选择再封装一个结构体类型来表示通讯录。
// 通讯录
typedef struct contact
{
PeoInfo data[MAX_SIZE]; // 存放添加进来的人的信息
int sz; // 记录的是当前通讯录中有效信息的个数
} contact;
这下我们终于大功告成可以创建我们的通讯录了。
contact con;
2.2 初始化通讯录
通讯录创建好之后,我们要先对其进行初始化之后才可以正常使用,不然里面存储的全部都随机值。
我们创建一个函数来初始化通讯录,对于结构体而言我们普遍采用传地址的方式来实现,并不是说传值调用不可以,只是不好,在传值调用时形参是实参的一份临时拷贝,修改形参的值不会影响实参,传值调用只适用于不对结构体进行修改的函数。同时当我们的结构体非常大时,在函数传参时会造成极大的开销,不像我们传址调用,仅传递一个指向该结构体的指针。
// 初始化通讯录
void InitContact(contact* pc)
{
assert(pc); // 断言保证指针不为空
memset(pc->data, 0, sizeof(pc->data)); // 将数组中每个元素置为0
pc->sz = 0;
}
函数的实现就比较简单了,将所有变量全部置为0即可。
到此准备步骤已经全部完成了,让我们来开始实现具体细节吧!
首先程序一开始执行应该给我们显示一个菜单来供我们来选择进行什么操作。
// 打印菜单
void menu()
{
printf("*****************************\n");
printf("***** 1.add 2.del *****\n");
printf("***** 3.search 4.modify *****\n");
printf("***** 5.print 6.remove *****\n");
printf("***** 7.sort 0.exit *****\n");
printf("*****************************\n");
}
接下来我们就根据输入的值,进行判断执行什么操作,因为可能并不只使用一次,所以我们要写一个循环。
enum Option
{
EXIT, // 0
ADD, // 1
DEL, // 2
SEARCH, // 3
MODIFY, // 4
PRINT, // 5
REMOVE, // 6
SORT // 7
};
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 PRINT:
PrintContact(&con); // 显示
break;
case REMOVE:
RemoveContact(&con); // 清除
break;
case SORT:
SortContact(&con); // 排序
break;
case EXIT:
printf("退出通讯录\n");
break;
default:
printf("选择错误\n");
break;
}
} while (input);
这里的 switch … case 语句中的 case 语句表达示使用枚举常量来代替。增强了程序的可读性。
接下来就是一步一步实现各个模块
2.3 增加一个联系人
// 增加联系人
void AddContact(contact* pc)
{
assert(pc); // 断言保证指针不为空
if (pc->sz == MAX_SIZE)
{
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("添加成功\n");
}
在增加新的联系人之前,我们应先判断当前通讯录是否已满,如果已满,提示用户无法添加,函数直接结束即可。
未满则添加一条新记录,我们的 sz (sz 从0开始)正好指向当前应添加记录的下标,直接输入对应数据,添加成功后,sz++ 表示当前通讯录新增了一条数据。
同时也可以提醒一下用户,添加成功。
2.4 打印通讯录
// 打印联系人信息
void PrintContact(const contact* pc)
{
assert(pc);
int i = 0;
// 打印标题
printf("%-15s %-5s %-5s %-11s %-30s\n", "名字", "年龄", "性别", "电话", "地址");
// 打印记录
for (i = 0; i < pc->sz; i++)
{
printf("%-15s %-5d %-5s %-11s %-30s\n",
pc->data[i].name,
pc->data[i].age,
pc->data[i].sex,
pc->data[i].tele,
pc->data[i].addr);
}
}