hello,这期给大家带来C语言实现静态通讯录,主要也是建立起创建大项目的思维,与往期这两篇博客有点类似
C语言实现三子棋
C语言实现扫雷
文章目录
- 🤓通讯录介绍
- 😶🌫️效果演示
- 🤠主题框架
- 头文件
- 测试文件
- 函数实现
- 初始化
- 显示
- 添加
- 删除
- 查找
- 修改
- 排序
- 清空通讯录
- 😵不足之处
🤓通讯录介绍
通讯录存放的是100个联系人的信息,这些信息包括
- 姓名
- 年龄
- 性别
- 电话
- 地址
通讯录要实现的功能有
- 添加联系人
- 删除联系人
- 查找联系人
- 修改联系人的信息
- 给通讯录排序
- 打印通讯录
- 销毁通讯录(清空所有联系人)
😶🌫️效果演示
在正式开始实现之前,我们来看一下最终的完结通讯录是怎么样的
🤠主题框架
大文件至少分3个文件
- text.c->逻辑测试文件
- comtact.c->函数定义文件
- contact.h->函数声明,结构体定义等文件
头文件
知道了通讯录需要实现的功能后,我们就可以给出头文件
#pragma once
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#define MAX 100
#define MAX_NAME 20
#define MAX_SEX 5
#define MAX_TELE 13
#define MAX_ADDRESS 20
typedef struct PerInfo
{
char name[MAX_NAME];
int age;
char sex[MAX_SEX];
char tele[MAX_TELE];
char address[MAX_ADDRESS];
}PeoInfo;
typedef struct Contact
{
PeoInfo data[MAX];
int sz;
}Contact;
enum Opreation
{
EXIT,
ADD,
DELETE,
SEARCH,
MODIFY,
SHOW,
SORT,
DESTROY
};
void InitContact(Contact* con);
void AddContact(Contact* con);
//这里传地址节省开销
void ShowContact(const Contact* con);
void DeleteContact(Contact* con);
void SearchContact(const Contact* con);
void ModifyContact( Contact* con);
void SortContact(Contact* con);
void DestroyContact(Contact* con);
给出几点说明
- 通讯录是静态的,最大可以存放100个联系人的信息,所以我们需要定义变量sz表示当前有效元素的个数,所以通讯录类型应该是结构体,结构体成员是数组和有效元素个数
- 数组的每个元素都是联系人的信息,联系人的信息有姓名、年龄、性别、地址、电话等,所以数组元素也是一个结构体
- 将通讯录的功能设置为枚举变量~提高程序的可读性
- 数组元素定义为符号常量,方便后续统一同意更改
- 函数的参数都是结构体指针
- 这样参数传递时可以只开辟一个指针的大小
- 可以通过结构体指针改变结构体的内容
测试文件
#define _CRT_SECURE_NO_WARNINGS 1
#include "contact.h"
void menu()
{
printf("***********Contact***********\n");
printf("****1. add 2. delete ****\n");
printf("****3. search 4. modify ****\n");
printf("****5. show 6. sort ****\n");
printf("****7. destroy 0. exit ****\n");
printf("***********Contact***********\n");
}
int main()
{
Contact con;
InitContact(&con);
int input = 0;
do
{
menu();
printf("请输入需要进行的操作:>");
scanf("%d", &input);
switch (input)
{
case ADD:
AddContact(&con);
break;
case DELETE:
DeleteContact(&con);
break;
case SEARCH:
SearchContact(&con);
break;
case MODIFY:
ModifyContact(&con);
break;
case SHOW:
ShowContact(&con);
break;
case SORT:
SortContact(&con);
break;
case DESTROY:
DestroyContact(&con);
break;
case EXIT:
break;
default :
break;
}
} while (input);
}
函数实现
初始化
由于我们申请的是一个Contact
类型的变量,并没有初始化,所以我们需要将Contac
变量的sz(有效联系人个数)成员初始化为0,以及整个通讯录初始化为0
//初始化
void InitContact(Contact* con)
{
con->sz = 0;
memset(con->data, 0, sizeof(con->data));
}
显示
我们是通过成员sz来记录该通讯录有效联系人的个数,所以打印通讯录时也只需要通过sz来判断打印结束条件(即只用打印sz个联系人的信息)
//显示
void ShowContact(const Contact* con)
{
printf("%-10s%-10s%-10s%-20s%-20s\n",
"姓名", "年龄", "性别", "电话", "地址");
for (int i = 0; i < con->sz; i++)
{
printf("%-10s%-10d%-10s%-20s%-20s\n",
con->data[i].name, con->data[i].age, con->data[i].sex, con->data[i].tele, con->data[i].address);
}
}
这里处于安全考虑,形参用
const
修饰
添加
添加联系人需要完成2步
- 输入需要添加联系人的信息(姓名、年龄、性别、电话、地址)
- 将通讯录的有效成员个数+1
//添加
void AddContact(Contact* con)
{
if (con->sz == MAX)
{
printf("通讯录容量已经满了,无法添加\n");
return;
}
printf("请输入你需要添加人的姓名:");
scanf("%s", con->data[con->sz].name);
printf("请输出你需要添加人的年龄:");
scanf("%d", &(con->data[con->sz].age));
printf("请输入你需要添加人的性别:");
scanf("%s", con->data[con->sz].sex);
printf("请输入你需要添加人的电话:");
scanf("%s", con->data[con->sz].tele);
printf("请输入你需要添加人的地址:");
scanf("%s", con->data[con->sz].address);
con->sz++;
printf("添加成功!\n通讯录还可以容纳%d人\n", MAX - con->sz);
}
添加的联系人存储在当前有效联系人的最后面
删除
删除需要两步
- 删除首先需要找到该联系人(由于后面查找功能也需要查找联系人,所以将此时的查找定义为一个函数)
- 用后面的联系人的信息覆盖以该联系人开始后面信息(即删除)
//查找联系人的下标
static int findContact(const Contact* con, const char* name)
{
for (int i = 0; i < con->sz; i++)
{
if (strcmp(con->data[i].name, name) == 0)
{
return i;
}
}
return -1;
}
//删除
void DeleteContact(Contact* con)
{
if (con->sz == 0)
{
printf("当前通讯录没有联系人,无法删除\n");
return;
}
char name[20];
printf("请输出你需要删除联系人的姓名:");
scanf("%s", name);
//查找
int pos = findContact(con, name);
if (pos == -1)
{
printf("没有找到该联系人\n");
return;
}
//删除
memmove(&(con->data[pos]), &(con->data[pos]) + 1,
sizeof(con->data[0]) * (MAX - pos - 1));
con->sz--;
printf("删除成功!\n通讯录可容纳的人数%d\n", MAX - con->sz);
}
给出几点注意:
- 删除可以根据用户输入的姓名找到指定的联系人进行删除(不考虑删除),也可以根据用户输入的电话进行删除
这里实现根据用户输入的姓名进行删除- 删除可以将data数组后面的元素一个一个挪动到前面,我这里直接用
memmove
,大同小异,但是不能使用memcpy,因为内存有重复的- 删除后通讯录有效人数的数量-1
查找
- 找到该联系人的下标
- 打印该联系人的信息
//查找
void SearchContact(const Contact* con)
{
printf("请输出你需要查找联系人的姓名:");
char name[20];
scanf("%s", name);
int pos = findContact(con, name);
if (pos == -1)
{
printf("找不到该联系人\n");
return;
}
printf("查找结果如下:\n");
printf("%-10s%-10s%-10s%-20s%-20s\n",
"姓名", "年龄", "性别", "电话", "地址");
printf("%-10s%-10d%-10s%-20s%-20s\n",
con->data[pos].name, con->data[pos].age, con->data[pos].sex,
con->data[pos].tele, con->data[pos].address);
}
修改
当联系人不小心信息输入错了之后我们需要对它进行修改
修改分两步
- 找到需要修改的联系人的下标
- 录入修改后的信息
//修改
void ModifyContact(Contact* con)
{
printf("请输出需要修改联系人的名字:");
char name[20];
scanf("%s", name);
int pos = findContact(con, name);
if (pos == -1)
{
printf("找不到该联系人\n");
return;
}
printf("请输入修改后的姓名:");
scanf("%s", con->data[pos].name);
printf("请输入修改后的年龄:");
scanf("%d", &(con->data[pos].age));
printf("请输入修改后的性别:");
scanf("%s", con->data[pos].sex);
printf("请输入修改后的电话:");
scanf("%s", con->data[pos].tele);
printf("请输入修改后的地址:");
scanf("%s", con->data[pos].address);
}
排序
排序主要为2中方式
- 按照名字从小到大排序
- 按照年龄从小到大排序
//自定义名字比较函数
int CmpByName(const void* e1, const void* e2)
{
return (strcmp(((PeoInfo*)e1)->name , ((PeoInfo*)e2)->name));
}
//自定义年龄比较函数
int CmpByAge(const void* e1, const void* e2)
{
return ((PeoInfo*)e1)->age - ((PeoInfo*)e2)->age;
}
void SortContact(Contact* con)
{
int input = 0;
do
{
printf("请选择排序方式\n1. 姓名 2.年龄\n");
scanf("%d", &input);
if (input == 1)
{
qsort(con->data, con->sz, sizeof(con->data[0]), CmpByName);
printf("排序成功\n");
ShowContact(con);
}
else if (input == 2)
{
qsort(con->data, con->sz, sizeof(con->data[0]), CmpByAge);
printf("排序成功\n");
ShowContact(con);
}
else printf("选择错误\n");
} while (input != 1 && input != 2);
}
由于待排序数组是
结构体
数组,所以直接调用库函数qsort
注意比较函数是需要我们自己定义的
清空通讯录
比较简单,将结构体数组所有元素赋值为0,通讯录数组有效联系人置为0即可
void DestroyContact(Contact* con)
{
printf("你确定要清空通讯录吗?(YES/NO)\n");
char selection[MAX] = { 0 };
scanf("%s", selection);
fflush(stdin);
if (strcmp(selection, "YES") != 0) return;
memset(con->data, 0, sizeof(con->data));
con->sz = 0;
printf("清空成功!\n");
}
😵不足之处
此通讯录总体来说实现比较简单,类似于静态顺序表
次通讯录如下几方面不足,可以改进
- 通讯录的大小是固定的
- 通讯的内存存储是连续的(数组在空间中是连续存放的)
- 通讯录的存储是存储在内存中的,没有保存在文件中,程序结束后通讯录销毁
😋😋😋本章内容到这结束了,不足之处我们后续会逐渐改进在后续文章中随着更加系统的学习我们会逐渐优化😋😋😋
完整代码在静态通讯录