【项目】—— 通讯录2(C语言)
通讯录是存储和管理我们的个人信息的程序应用。这里我们实现一个动态的通讯录(内存大小可变,当通讯录满了会扩容)
通讯录的功能
- 存储联系人的信息(姓名、性别、年龄、电话、地址)
- 添加联系人
- 删除联系人
- 修改联系人
- 根据姓名查寻联系人
- 根据姓名将联系人升序排序
- 展示所有联系人
目录
一、通讯录的结构
通讯录由C语言实现,实现环境是VS2019
编译程序,结构如下
- 头文件
contact.h
:引入头文件,并声明通讯录的数据类型和函数声明 - 源文件
contact.c
:实现通讯录各个功能的函数 - 测试文件
test.c
:写有main
函数,启动程序并实现业务逻辑
二、头文件
1. 头文件引用
#pragma once //防止头文件重复引用
#include <stdio.h> //标准输入输出,具体作用懂得都懂
#include <assert.h> //我们会对每个函数内部的一些变量进行断言,方便我们调试
#include <string.h> //我们会对字符串进行操作
#include <Windows.h> //我们会执行一些命令行
2. 结构体声明
//定义变量最大数量
#define MAX_NAME 20 //姓名占20字符
#define MAX_SEX 10 //性别占10字符
#define MAX_TEL 20 //电话占20字符
#define MAX_ADDR 50 //地址占50字符
//个人信息
typedef struct PeoInfo
{
char name[MAX_NAME]; //姓名
char sex[MAX_SEX]; //性别
int age; //年龄
char tel[MAX_TEL]; //电话
char addr[MAX_ADDR]; //地址
} PeoInfo;
//通讯录
typedef struct Contact
{
PeoInfo* data; //联系人
int count; //联系人数量
} Contact;
3. 函数声明
void menu(); // 菜单
void init(Contact* pc); // 初始化
void add(Contact* pc); // 添加
void del(Contact* pc); // 删除
void search(const Contact* pc); // 查寻
void alter(Contact* pc); // 修改
void sort(Contact* pc); // 排序
void show(const Contact* pc); // 展示
三、测试文件
当我们声明完变量和函数时,不要着急去实现函数,而是先构思好逻辑业务,构思好后再实现函数,边实现边调试,才能将BUG
消灭到最初的时候
int main()
{
Contact con;
init(&con);
menu();
int chose = 0;
do
{
printf("请输入您要进行的操作序号>");
scanf("%d", &chose);
system("cls"); //此时有一个清屏,让黑乎乎的界面更加美观,清完之后打印菜单,方便用户操作
menu();
switch (chose)
{
case 0:
printf("欢迎下次光临!\n");
destroy(&con);
return 0;
break;
case 1:
add(&con); //添加联系人
break;
case 2:
del(&con); //删除联系人
break;
case 3:
search(&con); //查找联系人
break;
case 4:
alter(&con); //修改联系人
break;
case 5:
show(&con); //展示所有联系人
break;
case 6:
sort(&con); //对联系人排序
break;
default:
printf("输入错误,请重新输入\n");
break;
}
} while (0 != chose);
return 0;
}
四、源文件
1. 菜单
用于提示用户操作信息,一套printf
输出行云流水直接操作。
void menu()
{
printf("========================================\n");
printf("==------------------------------------==\n");
printf("==------ 1.add 2.del ------==\n");
printf("==------ 3.search 4.alter ------==\n");
printf("==------ 5.show 6.sort ------==\n");
printf("==------ 0.exst ------==\n");
printf("==------------------------------------==\n");
printf("========================================\n");
}
2. 初始化
初始化通讯录,将数据域赋值为NULL,添加扩容时再分配内存,联系人数量和容量置为0
//初始化
void init(Contact* pc)
{
assert(pc != NULL);
pc->data = NULL;
pc->count = pc->capacity = 0;
}
3. 获取指定姓名的联系人下标
该函数会在查找、删除、修改联系人中使用,所以封装在一个函数,减少代码冗余
遍历查找联系人,并返回其下标,若是联系人不存在则返回-1
int getIndexByName(Contact* pc, const char* name)
{
int i = 0;
for (i = 0; i < pc->count; i++)
{
if (strcmp(pc->data[i].name, name) == 0)
{
return i;
}
}
return -1;
}
4. 添加联系人
先判断通讯录满了,则进行扩容操作,然后依次录入联系人信息
```c
void add(Contact* pc)
{
assert(pc != NULL);
if (pc->count >= pc->capacity)
{
int newcapacity = pc->capacity == 0 ? 4 : pc->capacity * 2;
PeoInfo* newdata = (PeoInfo*)realloc(pc->data, newcapacity * sizeof(PeoInfo));
if (newdata == NULL)
{
perror("relloc fail\n");
return;
}
pc->data = newdata;
pc->capacity = newcapacity;
}
printf("请输入姓名>");
scanf("%s", pc->data[pc->count].name);
printf("请输入性别>");
scanf("%s", pc->data[pc->count].sex);
printf("请输入年龄>");
scanf("%d", &(pc->data[pc->count].age));
printf("请输入电话>");
scanf("%s", pc->data[pc->count].tel);
printf("请输入地址>");
scanf("%s", pc->data[pc->count].addr);
pc->count++;
}
5. 删除联系人
判断联系人是否为空,若是不为空,则查找指定姓名的联系人下标,并用后面的联系人覆盖掉他的信息,联系人的数量减一,若是该联系人不存在,则给出提示。
void del(Contact* pc)
{
assert(pc != NULL);
if (pc->count == 0)
{
printf("通讯录为空\n");
return;
}
char name[20] = { 0 };
printf("请输入删除信息的姓名>");
scanf("%s", name);
int i = getIndexByName(pc, name);
if (i != -1)
{
for (i; i < pc->count - 1; i++)
{
memcpy(&(pc->data[i]), &(pc->data[i + 1]), sizeof(PeoInfo));
}
pc->count--;
printf("删除成功\n");
}
else
{
printf("查无此人\n");
}
}
6. 修改联系人
void alter(Contact* pc)
{
assert(pc != NULL);
if (pc->count == 0)
{
printf("通讯录为空\n");
return;
}
char name[20] = { 0 };
printf("请输入被修改联系人的姓名>");
scanf("%s", name);
int i = getIndexByName(pc, name);
if (i != -1)
{
printf("请输入姓名>");
scanf("%s", pc->data[i].name);
printf("请输入性别>");
scanf("%s", pc->data[i].sex);
printf("请输入年龄>");
scanf("%d", &(pc->data[i].age));
printf("请输入电话>");
scanf("%s", pc->data[i].tel);
printf("请输入地址>");
scanf("%s", pc->data[i].addr);
printf("修改成功\n");
}
else
{
printf("查无此人\n");
}
}
7. 查询联系人
根据姓名查询该联系人的所有信息
void search(const Contact* pc)
{
assert(pc != NULL);
if (pc->count == 0)
{
printf("通讯录为空\n");
return;
}
char name[20] = { 0 };
printf("请输入姓名>");
scanf("%s", name);
int i = getIndexByName(pc, name);
if (i != -1)
{
printf("%-20s\t%-10s\t%-10s\t%-20s\t%-20s\n",
"姓名", "性别", "年龄", "电话", "地址");
printf("%-20s\t%-10s\t%-10d\t%-20s\t%-20s\n",
pc->data[i].name, pc->data[i].sex, pc->data[i].age, pc->data[i].tel, pc->data[i].addr);
}
else
{
printf("查无此人\n");
}
}
8. 联系人排序
我们调用库函数qsort
帮助我们完成排序,在使用前我们先要实现两个联系人的比较规则,我们使用联系人姓名的升序作为排序规则
//比较规则
int cmp_info(const void* e1, const void* e2)
{
return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
}
// 排序
void sort(Contact* pc)
{
assert(pc != NULL);
if (pc->count == 0)
{
printf("通讯录为空\n");
return;
}
qsort(pc->data, pc->count, sizeof(PeoInfo), cmp_info);
printf("排序成功\n");
}
9. 展示所有联系人
利用printf
函数中的字宽限制展示的字符串长度,并使其左对齐,每个字段用制表符隔开,每个联系人信息占一行
void show(const Contact* pc)
{
assert(pc != NULL);
printf("%-20s\t%-10s\t%-10s\t%-20s\t%-20s\n",
"姓名", "性别", "年龄", "电话", "地址");
int i = 0;
for (i = 0; i < pc->count; i++)
{
printf("%-20s\t%-10s\t%-10d\t%-20s\t%-20s\n",
pc->data[i].name, pc->data[i].sex, pc->data[i].age, pc->data[i].tel, pc->data[i].addr);
}
}
10. 销毁通讯录
将动态分配的内存释放掉
//销毁
void destroy(Contact* pc)
{
assert(pc != NULL);
free(pc->data);
pc->capacity = 0;
pc->count = 0;
}
五、程序演示
- 启动程序时我们需要输入操作
- 增加联系人时,我们输入操作1,再依次输入信息即可
- 删除联系人时,我们输入操作2,再输入要删除的联系人姓名,若是联系人存在,则删除成功
- 查找联系人时,我们输入操作3,再输入查找联系人的姓名,若是联系人存在,则显示他的所有信息
- 修改联系人时,输入操作4,再输入修改联系人的姓名,依次输入联系人信息,则修改成功
- 展示所有联系人时,输入操作5即可
- 将联系人排序时,输入操作6,然后我们再输入5查看排序结果
- 退出时我们输入操作0即可
六、完整代码
代码存储于gitee中:点击查看完整代码