文章目录
1. 创建通讯录的大概思路
- 首先,我们要知道,通讯录的大小(100个人或者10000个人等等),其次,我们要知道通讯录中,一个人的信息都有哪儿些,好让我们创造变量
- 实现通讯录的功能(增删查改……)
2. 静态版本
之所以叫静态版本,是因为通讯录的大小是固定的,也就是说,我们将使用数组直接定义通讯录的大小。
2.1 通讯录菜单
在一个程序运行过程中,我们需要提示用户,进行其想执行的的操作,所以,我们需要在程序开始时,先写一个菜单。
代码如下:
void meun()
{
printf("****************************************\n");
printf("****** 1. Add 2. Del *******\n");
printf("****** 3. Seek 4. Modify *******\n");
printf("****** 5. Show 6. Sort *******\n");
printf("****** 7. Empty 0. Exit *******\n");
printf("****************************************\n");
}
2.2 通讯录主函数
本次项目,我们依旧采用多窗口模式,主函数即我们的测试窗口。
为了使程序可以循环进行,我们选择使用do……while函数、switch……case函数相结合
代码如下:
int main()
{
int input = 0;
Contact con;
InitContact(&con);
do
{
meun();
printf("请输入:>");
scanf("%d", &input);
switch (input)
{
case Exit:
printf("退出通讯录\n");
break;
case Add:
AddContact(&con);
break;
case Del:
DelContact(&con);
break;
case Seek:
SeekContact(&con);
break;
case Modify:
ModifyContact(&con);
break;
case Show:
ShowContact(&con);
break;
case Sort:
SortContact(&con);
ShowContact(&con);
break;
case Empty:
EmptyContact(&con);
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
return 0;
}
有细心的uu发现,我们的case 后面跟的不是常数,而是一些“变量”,其实这是使用了
枚举
的结果
为什么使用枚举呢?
- 增加代码的可读性和可维护性
- 便于调试
- 枚举中的默认赋值,与我们的菜单相联系。
代码如下:
enum Option
{
Exit,
Add,
Del,
Seek,
Modify,
Show,
Sort,
Empty
};
2.3 结构体定义联系人和通讯录
//表示一个人的信息
typedef struct PeoInfo
{
char name[MAX_NAME];
int age;
char sex[MAX_SEX];
char tele[MAX_TELE];
char addr[MAX_ADDR];
}PeoInfo;
typedef struct contact
{
PeoInfo data[MAX];//存放数据
int sz;//记录通讯录中的有效信息的个数
}Contact;
2.4 全局变量的声明
有的uu发现,在上面结构体的定义中,数组中使用的时常变量,而不是数值,是因为,这有便于我们,后期的改值。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#define MAX 100
#define MAX_NAME 20
#define MAX_SEX 4
#define MAX_TELE 20
#define MAX_ADDR 30
2.5 初始化通讯录
void InitContact(Contact* pc)
{
assert(pc != NULL);
pc->sz = 0;
memset(pc->data, 0, sizeof(pc->data));
}
有的uu会上来就把 pc->data = 0; 这个代码写上去,这样是不是有点太草率了,我们data中存放的地址,而直接把其全部改成0,是否有些不妥呢?
所以我们使用了memset函数,pc->data中的每一个字节修改成0.
2.6 实现通讯录功能
做完上述的工作,我们终于该开始实现通讯录的各种功能了。
2.6.1 添加联系人
//非法返回-1,否则返回这个数
static int JudgeIsLegal(const Contact* pc)
{
assert(pc != NULL);
if (pc->sz >= MAX && pc->sz < 0)
return -1;
return pc->sz;
}
void AddContact(Contact* pc)
{
assert(pc != NULL);
if (JudgeIsLegal(pc) == -1)
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");
}
注意事项:
- 首先我们需要注意的时在输入过程中,scanf的参数类型时指针,我们需要传入地址,这里的年龄就需要我们使用&取地址操作符,而其他的变量因为都是数组,不需要&取地址符,所以可能会忽略年龄变量所需要的&取地址操作符。
- 这里因为以后会经常判断data数组是否合法(满了、为空(删除)),所以创建了一个数组,减少冗余的代码。
2.6.2 删除联系人
//找到了返回下标i,没找到返回0
static int FindMyCon(const Contact* pc, const char* name)
{
assert(pc != NULL);
assert(name != NULL);
int i = 0;
for (i = 0; i < pc->sz; i++)
{
if (0 == strcmp(pc->data[i].name, name))
return i;
}
return -1;
}
void DelContact(Contact* pc)
{
assert(pc != NULL);
char name[100] = { 0 };
if (JudgeIsLegal(pc) == 0 || JudgeIsLegal(pc) == -1)
return;
printf("请输入要删除人的姓名:>");
scanf("%s", name);
int pos = FindMyCon(pc, name);
if (-1 == pos)
{
printf("不存在要删除的联系人\n");
return;
}
int i = 0;
for (i = pos; i < pc->sz - 1; i++)
{
pc->data[i] = pc->data[i + 1];
}
pc->sz--;
printf("删除成功\n");
}
注意事项:
- 在判断是否合法函数中,data数组为0对于删除函数也是非法操作,所以小编这样写。
2.6.3 查找联系人
void SeekContact(const Contact* pc)
{
assert(pc != NULL);
char name[100] = { 0 };
printf("请输入要查找人的姓名:>");
scanf("%s", name);
int pos = FindMyCon(pc, name);
if (-1 == pos)
{
printf("不存在要查找的联系人\n");
return;
}
printf("%-10s %-4s %-5s %-12s %-30s\n",
"姓名", "年龄", "性别", "手机号", "地址");
printf("%-10s %-4d %-5s %-12s %-30s\n",
pc->data[pos].name,
pc->data[pos].age,
pc->data[pos].sex,
pc->data[pos].tele,
pc->data[pos].addr);
}
2.6.4 修改联系人
void ModifyContact(Contact* pc)
{
assert(pc != NULL);
char name[100] = { 0 };
printf("请输入要修改人的姓名:>");
scanf("%s", name);
int pos = FindMyCon(pc, name);
if (-1 == pos)
{
printf("不存在要修改的联系人\n");
return;
}
printf("请输入联系人姓名:>");
scanf("%s", pc->data[pos].name);
printf("请输入联系人年龄:>");
scanf("%d", &(pc->data[pos].age));
printf("请输入联系人性别:>");
scanf("%s", pc->data[pos].sex);
printf("请输入联系人电话:>");
scanf("%s", pc->data[pos].tele);
printf("请输入联系人地址:>");
scanf("%s", pc->data[pos].addr);
printf("修改成功\n");
}
2.6.5 显示通讯录
void ShowContact(const Contact* pc)
{
assert(pc != NULL);
printf("%-10s %-4s %-5s %-12s %-30s\n",
"姓名", "年龄", "性别", "手机号", "地址");
int i = 0;
for (i = 0; i < pc->sz; i++)
{
printf("%-10s %-4d %-5s %-12s %-30s\n",
pc->data[i].name,
pc->data[i].age,
pc->data[i].sex,
pc->data[i].tele,
pc->data[i].addr);
}
}
2.6.6 通讯录排序
int name_cmp(const void* e1, const void* e2)
{
return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
}
void SortContact(Contact* pc)
{
assert(pc != NULL);
qsort(pc, pc->sz, sizeof(PeoInfo), name_cmp);
}
注意:
- 在这里小编使用了qsort函数,如果对qsort函数不了解的uu可以看看小编的另一篇文章里边有实现。qsort函数的冒泡实现
3. 动态版本
- 之所以叫动态版本,是因为,其所占用内存的空间时可以变化的,这里,我们就使用了动态内存管理中的malloc、free、realloc等函数。
- 如果没有学过动态内存管理的uu也先不用着急,小编的下一篇就会更新有关动态内存管理的知识,可以先把静态通讯录自己敲一遍哦。
3.1 结构体定义联系人和通讯录
- 在静态中,我们定义通讯录时,使用了data数组,因为我们的目标时可变的,而数组是固定的内存空间,所以,我们不能使用数组来定义了,这里使用了malloc函数。
typedef struct PeoInfo
{
char Name[Name_Max];
int SZ;
char Sex[Sex_Max];
char Tele[Tele_Max];
char Addr[Addr_Max];
}PeoInfo;
typedef struct Contact
{
PeoInfo* data;
int sz;
int capacity;
}Contact;
细心的uu已经发现,我们在通讯录这个结构体中多了一个capacity(容量)变量,这是因为,在联系人的增加或者删减中,我们需要一直判断,内存是否足够,如果不够,我们则需要增加内存空间
这里在添加联系人函数
中有具体体现。
3.2 全局变量的声明
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#define Name_Max 20
#define Sex_Max 4
#define Tele_Max 13
#define Addr_Max 30
#define Inti_capacity 3
#define Add_capacity 2
3.3 初始化通讯录
void IntiContact(Contact* pc)
{
pc->data = (PeoInfo*)malloc(Inti_capacity * sizeof(PeoInfo));
if (pc->data = NULL)
{
perror("通信录初始化失败",errno);
return;
}
pc->capacity = Inti_capacity;
pc->sz = 0;
}
3.4 实现通讯录功能
3.4.1 添加联系人
这里的CheckCapacity函数
很重要
,请uu们认真思考。
static int CheckCapacity(Contact* pc)
{
if (pc->capacity == pc->sz)
{
PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + Add_capacity) * sizeof(PeoInfo));
if (ptr != NULL)
{
pc->data = ptr;
pc->capacity += Add_capacity;
return 1;
}
}
return 0;
}
void AddContact(Contact* pc)
{
if (0 == CheckCapacity(pc))
{
printf("空间不够,增容失败\n");
return;
}
else
{
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");
}
}
3.4.2 退出通讯录
void DestoryContact(Contact* pc)
{
free(pc->data);
pc->data = NULL;
pc->capacity = 0;
pc->sz = 0;
}
因为使用了动态内存,所以在不使用时,我们需要将其归还操作系统。