C 语 言 - - - 简 易 通 讯 录
- 代 码 全 貌 与 功 能 介 绍
- 通 讯 录 的 功 能 说 明
- 通 讯 录 效 果 展 示
- 代 码 详 解
- contact.h
- contact.c
- test.c
- 总 结
💻作 者 简 介:曾 与 你 一 样 迷 茫,现 以 经 验 助 你 入 门 C 语 言
💡个 人 主 页:@笑口常开xpr 的 个 人 主 页
📚系 列 专 栏:C 启新程
✨代 码 趣 语:糟 糕 的 程 序 员 关 注 代 码,优 秀 的 程 序 员 关 注 数 据 结 构 及 其 关 系。
💪代 码 千 行,始 于 坚 持,每 日 敲 码,进 阶 编 程 之 路。
📦gitee 链 接:gitee
在 编 程 的 世 界 里,每 一 行 代 码 都 可 能 隐 藏 着 无 限 的 可 能 性 。你 是 否 想 过,一 个 小 小 的 程 序 究 竟 能 改 变 什 么?它 可 以 是 解 决 复 杂 问 题 的 工 具 ,也 可 以 是 实 现 梦 想 的 桥 梁。今 天,就 让 我 们 一 起 走 进 C 语 言 通 讯 录 的 世 界,探 索 它 的 无 限 潜 力。
代 码 全 貌 与 功 能 介 绍
整 个 通 讯 录 项 目 由 三 个 主 要 文 件 构 成:contact.h、contact.c 和 test.c。这 种 多 文 件 的 架 构 设 计,有 助 于 将 不 同 功 能 模 块 分 离,提 高 代 码 的 可 读 性、可 维 护 性 与 可 扩 展 性。
contact.h
contact.h 包 含 了 通 讯 录 所 需 的 头 文 件 引 用、常 量 定 义 以 及 函 数 声 明。
test.c
test.c 是 通 讯 录 的 主 逻 辑 文 件,负 责 处 理 用 户 输 入 和 代 码 流 程 的 控 制。
contact.c
contact.c 则 实 现 了 通 讯 录 的 具 体 功 能 函 数。
下 面 展 示
完 整 代 码
。
读 者 可 以 将 这 段 代 码 复 制 到 自 己 的 编 译 器 中 运 行:
contact.h
#pragma once
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#define MAX 100
#define NAME_MAX 20 //姓名
#define SEX_MAX 5 //性别
#define ADDR_MAX 30 //地址
#define TELE_MAX 12 //电话
#define DEFAULT_SZ 3 //默认大小
#define INC_SZ 2 //增加大小
//人的信息
typedef struct PeoInfo
{
char name[NAME_MAX];
int age;
char sex[SEX_MAX];
char addr[ADDR_MAX];
char tele[TELE_MAX];
}PeoInfo;
//动态版本
typedef struct contact
{
PeoInfo* data;//指向存放当前人的信息的空间
int sz;//当前已经放的信息的总人数
int capacity;//当前通讯录的最大容量
}contact;
//通讯录初始化
void Initcontact(contact* pc);
//增加联系人
void Addcontact(contact* pc);
//显示通讯录
void Showcontact(const contact* pc);
//删除通讯录
void Delcontact(contact* pc);
//查找联系人
int find_by_name(const contact* pc, char name[MAX]);
//查找联系人
void SearchContact(const contact* pc);
//修改联系人
void Modifycontact(contact* pc);
//排序
void Sortcontact(contact* pc);
//检查容量
void check_capacity(contact* pc);
//销毁通讯录
void DestoryContact(contact* pc);
//保存通讯录中的信息到文件中
void SaveContact(contact* pc);
//加载文件信息到通讯录
void LoadContact(contact* pc);
//姓名排序
int cmp_contact_by_name(const void* e1, const void* e2);
//年龄排序
int cmp_contact_by_age(const void* e1, const void* e2);
//电话排序
int cmp_contact_by_tele(const void* e1, const void* e2);
test.c
#define _CRT_SECURE_NO_WARNINGS 0
#include "contact.h"
enum Option
{
EXIT,//0
ADD,//1
DELETE,//2
SEARCH,//3
MODIFY,//4
SHOW,//5
SORT//6
};
void menu()
{
printf("*******************************************\n");
printf("******** 1. add 2. delete ********\n");
printf("******** 3. search 4. modify ********\n");
printf("******** 5. show 6. sort ********\n");
printf("******** 0. exit ********\n");
printf("*******************************************\n");
}
void test()
{
int input = 0;
//创建通讯录
contact con;
//初始化通讯录
Initcontact(&con);
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case EXIT:
//保存通讯录信息到文件中
SaveContact(&con);
DestoryContact(&con);//销毁通讯录
printf("退出通讯录\n");
break;
case ADD:
Addcontact(&con);
break;
case DELETE:
Delcontact(&con);
break;
case SEARCH:
SearchContact(&con);
break;
case MODIFY:
Modifycontact(&con);
break;
case SHOW:
Showcontact(&con);
break;
case SORT:
Sortcontact(&con);
break;
default:
printf("选择错误,请重新输入\n");
break;
}
} while (input);
}
int main()
{
test();
return 0;
}
contact.c
#define _CRT_SECURE_NO_WARNINGS 0
#include "contact.h"
//初始化通讯录
void Initcontact(contact* pc)
{
assert(pc);
pc->sz = 0;
PeoInfo* ptr = (PeoInfo*)calloc(DEFAULT_SZ, sizeof(PeoInfo));
if (ptr == NULL)
{
perror("Initcontact");
return;
}
pc->data = ptr;
pc->capacity = DEFAULT_SZ;
//加载文件信息到通讯录
LoadContact(pc);
}
//动态添加人数
void Addcontact(contact* pc)
{
assert(pc);
check_capacity(pc);
printf("请输入名字:>\n");
scanf("%s", pc->data[pc->sz].name);
printf("请输入年龄:>\n");
scanf("%d", &(pc->data[pc->sz].age));
printf("请输入性别:>\n");
scanf("%s", pc->data[pc->sz].sex);
printf("请输入地址:>\n");
scanf("%s", pc->data[pc->sz].addr);
printf("请输入电话:>\n");
scanf("%s", pc->data[pc->sz].tele);
pc->sz++;
}
//显示通讯录
//声明和定义保持一致
void Showcontact(const contact* pc)
{
assert(pc);
int i = 0;
printf("%-20s\t%-4s\t%-5s\t%-20s\t%-12s\n", "姓名", "年龄", "性别", "地址", "电话");
for (i = 0;i < pc->sz;i++)
{
printf("%-20s\t%-4d\t%-5s\t%-20s\t%-12s\n", pc->data[i].name, pc->data[i].age,
pc->data[i].sex, pc->data[i].addr, pc->data[i].tele);
}
}
//删除指定联系人
void Delcontact(contact* pc)
{
assert(pc);
if (pc->sz == 0)
{
printf("通讯录为空,无法删除\n");
return;
}
char name[MAX] = { 0 };
printf("请输入要删除人的名字:>\n");
scanf("%s", name);
int ret = find_by_name(pc, name);
int i = 0;
if (-1 == ret)
{
printf("要删除的人不存在\n");
return;
}
else
{
//删除
for (i = ret;i < pc->sz - 1;i++)
{
pc->data[i] = pc->data[i + 1];
}
pc->sz--;
printf("删除成功\n");
}
}
//查找联系人
int find_by_name(const contact* pc, char name[MAX])
{
assert(pc);
int i = 0;
for (i = 0;i < pc->sz;i++)
{
if (strcmp(pc->data[i].name, name) == 0)
{
return i;
}
}
return -1;
}
void SearchContact(const contact* pc)
{
assert(pc);
char name[NAME_MAX] = { 0 };
printf("请输入要查找人的名字:>\n");
scanf("%s", name);
int pos = find_by_name(pc, name);
if (-1 == pos)
{
printf("要查找的人不存在\n");
return;
}
else
{
printf("%-20s\t%-4s\t%-5s\t%-20s\t%-12s\n", "姓名", "年龄", "性别", "地址", "电话");
printf("%-20s\t%-4d\t%-5s\t%-20s\t%-12s\n",
pc->data[pos].name, pc->data[pos].age,
pc->data[pos].sex, pc->data[pos].addr,
pc->data[pos].tele);
}
}
//修改通讯录
void Modifycontact(contact* pc)
{
assert(pc);
char name[NAME_MAX] = { 0 };
printf("请输入要修改人的名字:>\n");
scanf("%s", name);
int pos = find_by_name(pc, name);
if (-1 == pos)
{
printf("要修改的人不存在\n");
return;
}
printf("请输入名字:>\n");
scanf("%s", pc->data[pos].name);
printf("请输入年龄:>\n");
scanf("%d", &(pc->data[pos].age));
printf("请输入性别:>\n");
scanf("%s", pc->data[pos].sex);
printf("请输入地址:>\n");
scanf("%s", pc->data[pos].addr);
printf("请输入电话:>\n");
scanf("%s", pc->data[pos].tele);
printf("修改完成\n");
}
//增加容量
void check_capacity(contact* pc)
{
if (pc->sz == pc->capacity)
{
//增加容量
PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(PeoInfo));
if (ptr == NULL)
{
perror("check_capacity::realloc");
return;
}
pc->data = ptr;
pc->capacity += INC_SZ;
}
}
//销毁通讯录
void DestoryContact(contact* pc)
{
free(pc->data);
pc->data = NULL;
pc->capacity = 0;
pc->sz = 0;
}
void SaveContact(contact* pc)
{
//写数据
//打开文件
FILE* pf = fopen("contact.txt", "wb");
if (pf == NULL)
{
perror("SaveContact");
}
else
{
//写数据
int i = 0;
for (i = 0; i < pc -> sz; i++)
{
fwrite(pc->data + i, sizeof(PeoInfo), 1, pf);
}
fclose(pf);
pf = NULL;
}
printf("保存数据成功\n");
}
//加载文件信息到通讯录
void LoadContact(contact* pc)
{
//打开文件
FILE* pf = fopen("contact.txt", "rb");
if (NULL == pf)
{
perror("LoadContact");
}
else
{
//读数据
PeoInfo tmp = { 0 };
int i = 0;
while (fread(&tmp, sizeof(PeoInfo), 1, pf))
{
check_capacity(pc);
pc->data[i] = tmp;
pc->sz++;
i++;
}
fclose(pf);
pf = NULL;
}
}
void Sortcontact(contact* pc)
{
assert(pc);
int input = 0;
printf("请输入你想以0. 名字 1. 电话 2. 年龄 排序的数字:>\n");
scanf("%d", &input);
if (input == 0)
{
qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp_contact_by_name);
Showcontact(pc);
}
else if (input == 1)
{
qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp_contact_by_tele);
Showcontact(pc);
}
else if (input == 2)
{
qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp_contact_by_age);
Showcontact(pc);
}
else
{
printf("无效输入\n");
}
}
int cmp_contact_by_name(const void* e1, const void* e2)
{
return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
}
int cmp_contact_by_age(const void* e1, const void* e2)
{
return ((PeoInfo*)e1)->age - ((PeoInfo*)e2)->age;
}
int cmp_contact_by_tele(const void* e1, const void* e2)
{
return strcmp(((PeoInfo*)e1)->tele, ((PeoInfo*)e2)->tele);
}
通 讯 录 的 功 能 说 明
初 始 化 功 能:通 过 Initcontact 函 数 为 通 讯 录 分 配 内 存 并 设 置 初 始 状 态,并 从 文 件 中 加 载 已 有 联 系 人 信 息。
添 加 联 系 人:Addcontact 函 数 用 于 向 通 讯 录 中 添 加 新 的 联 系 人。它 会 检 查 通 讯 录 的 容 量,若 已 满 则 进 行 扩 容,然 后 获 取 用 户 输 入 的 联 系 人 信 息,包 括 姓 名、年 龄、性 别、地 址 和 电 话,并 将 其 添 加 到 通 讯 录 中。
显 示 联 系 人:Showcontact 函 数 用 于 显 示 通 讯 录 中 所 有 联 系 人 的 信 息。它 会 遍 历 通 讯 录,以 格 式 化 的 方 式 输 出 每 个 联 系 人 的 姓 名、年 龄、性 别、地 址 和 电 话。
删 除 联 系 人:Delcontact 函 数 用 于 删 除 通 讯 录 中 指 定 的 联 系 人。
查 找 联 系 人:SearchContact 函 数 用 于 查 找 通 讯 录 中 指 定 的 联 系 人。
修 改 联 系 人:Modifycontact 函 数 用 于 修 改 通 讯 录 中 指 定 联 系 人 的 信 息。
排 序 功 能:Sortcontact 函 数 用 于 对 通 讯 录 中 的 联 系 人 进 行排 序。用 户 可 以 选 择 按 姓 名、年 龄 或 电 话 进 行 排 序,根 据 用 户 的 选 择 调 用 相 应 并 显 示 排 序 后 的 通 讯 录。
数 据 持 久 化:SaveContact 函 数 用 于 将 通 讯 录 中 的 联 系 人 信 息 保 存 到 文 件 中,LoadContact 函 数 用 于 从 文 件 中 加 载 联 系 人 信 息 到 通 讯 录,实 现 数 据 的 持 久 化 存 储。
错 误 处 理:在 内 存 分 配 和 文 件 操 作 中 使 用 了 perror 等 方 式 进 行 错 误 处 理,当 操 作 失 败 时 输 出 相 应 的 错 误 信 息。
通 讯 录 效 果 展 示
菜 单 展 示
每 次 循 环 开 始 时 会 显 示 菜 单,内 容 包 括:
add: 添 加 联 系 人
delete: 删 除 联 系 人
search:查 找 联 系 人
modify:修 改 联 系 人
show: 显 示 所 有 联 系 人
sort: 对 联 系 人 进 行 排 序
exit: 退 出 通 讯 录
添 加 联 系 人(add)
输 入 1 后,程 序 会 提 示 用 户 依 次 输 入 名 字、年 龄、性 别、地 址 和 电 话。输 入 完 成 后,联 系 人 信 息 被 添 加 到 通 讯 录 中。如 果 通 讯 录 已 满,会 自 动 增 加 容 量。
显 示 联 系 人(show)
输 入 5 后,程 序 会 显 示 所 有 已 添 加 的 联 系 人 信 息。
删 除 联 系 人(delete)
输 入 2 后,程 序 会 提 示 用 户 输 入 要 删 除 的 联 系 人 名 字。如 果 找 到 该 联 系 人,将 其 从 通 讯 录 中 移 除;如 果 未 找 到,提 示 联 系 人 不 存 在。
查 找 联 系 人(search)
输 入 3 后,程 序 会 提 示 用 户 输 入 要 查 找 的 联 系 人 名 字。如 果 找 到 该 联 系 人,显 示 其 详 细 信 息;如 果 未 找 到,提 示 联 系 人 不 存 在。
修 改 联 系 人(modify)
输 入 4 后,程 序 会 提 示 用 户 输 入 要 修 改 的 联 系 人 名 字。如 果 找 到 该 联 系 人,提 示 用 户 依 次 输 入 新 的 名 字、年 龄、性 别 、地 址 和 电 话,完 成 修 改;如 果 未 找 到,提 示 联 系 人 不 存 在。
排 序 联 系 人(sort)
输 入 6 后,程 序 会 提 示 用 户 选 择 排 序 方 式:
0:按 名 字 排 序
1:按 电 话 排 序
2:按 年 龄 排 序
根 据 用 户 选 择 的 排 序 方 式 对 通 讯 录 中 的 联 系 人 进 行 排 序 , 并 显 示 排 序 后 的 结 果。
退 出 通 讯 录(exit)
输 入 0 后,程 序 会 将 通 讯 录 中 的 信 息 以 二 进 制 形 式 保 存 到 文 件 “contact.txt” 中。释 放 内 存 并 销 毁 通 讯 录, 然 后 退 出 程 序。
代 码 详 解
contact.h
#pragma once
#pragma once 是 一 个 预 处 理 指 令,用 于 防 止 头 文 件 被 重 复 包 含。在 大 型 项 目 中,多 个 源 文 件 可 能 会 包 含 同 一 个 头 文 件,使 用 #pragma once 可 以 确 保 头 文 件 的 内 容 只 被 编 译 一 次,避 免 重 复 定 义 的 错 误。
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include 指 令 用 于 引 入 必 要 的 标 准 库 头 文 件:
stdio.h:提 供 标 准 输 入 输 出 函 数,如 printf、scanf 等。
string.h:提 供 字 符 串 处 理 函 数,如 strcmp、strcpy 等。
assert.h:提 供 assert 宏,用 于 在 运 行 时 检 查 程 序 的 断 言 条 件。
stdlib.h:提 供 内 存 管 理 函 数(如 malloc、realloc、free)和 其 他 标 准 库 函 数。
#define MAX 100
#define NAME_MAX 20
#define SEX_MAX 5
#define ADDR_MAX 30
#define TELE_MAX 12
#define DEFAULT_SZ 3 //默认大小
#define INC_SZ 2 //增加大小
#define 指 令 用 于 定 义 一 些 常 量:
MAX:用 于 表 示 通 讯 录 可 容 纳 的 最 大 联 系 人 数 量。
NAME_MAX:定 义 联 系 人 姓 名 的 最 大 长 度 为 20 个 字 符。
SEX_MAX:定 义 联 系 人 性 别 信 息 的 最 大 长 度 为 5 个 字 符。
ADDR_MAX:定 义 联 系 人 地 址 的 最 大 长 度 为 30 个 字 符。
TELE_MAX:定 义 联 系 人 电 话 号 码 的 最 大 长 度 为 12 个 字 符。
DEFAULT_SZ:定 义 通 讯 录 的 初 始 容 量 为 3 个 联 系 人。
INC_SZ:定 义 每 次 扩 展 通 讯 录 容 量 时 增 加 的 数 量 为 2 个 联 系 人。
//人的信息
typedef struct PeoInfo
{
char name[20];
int age;
char sex[5];
char addr[30];
char tele[12];
} PeoInfo;
定 义 了 一 个 名 为 PeoInfo 的 结 构 体,用 于 存 储 单 个 联 系 人 的 信 息:
name:字 符 数 组,用 于 存 储 联 系 人 姓 名。
age:整 数,用 于 存 储 联 系 人 年 龄。
sex:字 符 数 组,用 于 存 储 联 系 人 性 别。
addr:字 符 数 组,用 于 存 储 联 系 人 地 址。
tele:字 符 数 组,用 于 存 储 联 系 人 电 话 号 码。
//动态版本
typedef struct contact
{
PeoInfo* data;//指向存放当前人的信息的空间
int sz;//当前已经放的信息的总人数
int capacity;//当前通讯录的最大容量
}contact;
定 义 了 一 个 名 为 contact 的 结 构 体,用 于 表 示 整 个 通 讯 录:
data:指 向 PeoInfo 结 构 体 的 指 针,用 于 动 态 分 配 内 存 来 存 储 联 系 人 信 息。
sz:整 数,记 录 当 前 通 讯 录 中 已 存 储 的 联 系 人 数 量。
capacity:整 数,记 录 当 前 通 讯 录 的 最 大 容 量。
//通讯录初始化
void Initcontact(contact* pc);
//增加联系人
void Addcontact(contact* pc);
//显示通讯录
void Showcontact(const contact* pc);
//删除联系人
void Delcontact(contact* pc);
//查找联系人
int find_by_name(const contact* pc, char name[MAX]);
//查找联系人
void SearchContact(const contact* pc);
//修改联系人
void Modifycontact(contact* pc);
//排序
void Sortcontact(contact* pc);
//检查容量
void check_capacity(contact* pc);
//销毁通讯录
void DestoryContact(contact* pc);
//保存通讯录中的信息到文件中
void SaveContact(contact* pc);
//加载文件信息到通讯录
void LoadContact(contact* pc);
//姓名排序
int cmp_contact_by_name(const void* e1, const void* e2);
//年龄排序
int cmp_contact_by_age(const void* e1, const void* e2);
//电话排序
int cmp_contact_by_tele(const void* e1, const void* e2);
函 数 声 明,定 义 了 对 通 讯 录 进 行 操 作 的 各 个 功 能 函 数:
Initcontact:用 于 初 始 化 通 讯 录,分 配 内 存 并 设 置 初 始 状 态。
Addcontact:用 于 向 通 讯 录 中 添 加 新 的 联 系 人。
Showcontact:用 于 显 示 通 讯 录 中 已 有 的 联 系 人 信 息。
Delcontact:用 于 从 通 讯 录 中 删 除 指 定 的 联 系 人。
find_by_name:根 据 姓 名 查 找 联 系 人,返 回 联 系 人 在 通 讯 录 中 的 位 置,找 不 到 返 回 特 定 值。
SearchContact:根 据 姓 名 查 找 并 显 示 联 系 人 信 息。
Modifycontact:用 于 修 改 通 讯 录 中 指 定 联 系 人 的 信 息。
Sortcontact:用 于 对 通 讯 录 中 的 联 系 人 按 照 指 定 方 式( 如 姓 名、年 龄、电 话)进 行 排 序。
check_capacity:检 查 通 讯 录 的 容 量,必 要 时 进 行 扩 容。
DestoryContact:用 于 销 毁 通 讯 录,释 放 分 配 的 内 存。
SaveContact:将 通 讯 录 中 的 信 息 保 存 到 文 件 中。
LoadContact:从 文 件 中 加 载 联 系 人 信 息 到 通 讯 录。
cmp_contact_by_name:比 较 两 个 联 系 人 的 姓 名,用 于 排 序。
cmp_contact_by_age:比 较 两 个 联 系 人 的 年 龄,用 于 排 序。
cmp_contact_by_tele:比 较 两 个 联 系 人 的 电 话,用 于 排 序。
contact.c
内 存 管 理 机 制
//初始化
void Initcontact(contact* pc)
{
assert(pc);
pc->sz = 0;
PeoInfo* ptr = (PeoInfo*)calloc(DEFAULT_SZ, sizeof(PeoInfo));
if (ptr == NULL)
{
perror("Initcontact");
return;
}
pc->data = ptr;
pc->capacity = DEFAULT_SZ;
//加载文件信息到通讯录
LoadContact(pc);
}
//扩容
void check_capacity(contact* pc)
{
if (pc->sz == pc->capacity)
{
//增加容量
PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(PeoInfo));
if (ptr == NULL)
{
perror("check_capacity::realloc");
return;
}
pc->data = ptr;
pc->capacity += INC_SZ;
}
}
初 始 化:使 用 calloc 分 配 初 始 空 间 并 清 零。
动 态 扩 容:每 次 容 量 不 足 时 增 加 INC_SZ(2) 个 空 间。
内 存 安 全:通 过 realloc 确 保 数 据 连 续 性,失 败 时 输 出 错 误 信 息。
联 系 人 核 心 操 作
添 加 联 系 人
void Addcontact(contact* pc)
{
assert(pc);
check_capacity(pc);
printf("请输入名字:>\n");
scanf("%s", pc->data[pc->sz].name);
printf("请输入年龄:>\n");
scanf("%d", &(pc->data[pc->sz].age));
printf("请输入性别:>\n");
scanf("%s", pc->data[pc->sz].sex);
printf("请输入地址:>\n");
scanf("%s", pc->data[pc->sz].addr);
printf("请输入电话:>\n");
scanf("%s", pc->data[pc->sz].tele);
pc->sz++;
}
自 动 扩 容:添 加 前 检 查 容 量,不 足 时 自 动 扩 展。
输 入 处 理:使 用 scanf 获 取 用 户 输 入,未 处 理 包 含 空 格 的 字 符 串。
删 除 联 系 人
void Delcontact(contact* pc)
{
assert(pc);
if (pc->sz == 0)
{
printf("通讯录为空,无法删除\n");
return;
}
char name[MAX] = { 0 };
printf("请输入要删除人的名字:>\n");
scanf("%s", name);
int ret = find_by_name(pc, name);
int i = 0;
if (-1 == ret)
{
printf("要删除的人不存在\n");
return;
}
else
{
//删除
for (i = ret;i < pc->sz - 1;i++)
{
pc->data[i] = pc->data[i + 1];
}
pc->sz--;
printf("删除成功\n");
}
}
查 找 联 系 人:通 过 find_by_name 定 位 联 系 人。
数 据 迁 移:删 除 后 将 后 续 元 素 前 移 覆 盖。
数 据 持 久 化
void SaveContact(contact* pc)
{
//写数据
//打开文件
FILE* pf = fopen("contact.txt", "wb");
if (pf == NULL)
{
perror("SaveContact");
}
else
{
//写数据
int i = 0;
for (i = 0; i < pc -> sz; i++)
{
fwrite(pc->data + i, sizeof(PeoInfo), 1, pf);
}
fclose(pf);
pf = NULL;
}
printf("保存数据成功\n");
}
//加载文件信息到通讯录
void LoadContact(contact* pc)
{
//打开文件
FILE* pf = fopen("contact.txt", "rb");
if (NULL == pf)
{
perror("LoadContact");
}
else
{
//读数据
PeoInfo tmp = { 0 };
int i = 0;
while (fread(&tmp, sizeof(PeoInfo), 1, pf))
{
check_capacity(pc);
pc->data[i] = tmp;
pc->sz++;
i++;
}
fclose(pf);
pf = NULL;
}
}
二 进 制 存 储:使 用 fwrite 和 fread 进 行 整 块 数 据 读 写。
自 动 加 载:初 始 化 时 自 动 从 文 件 恢 复 数 据。
容 量 管 理:加 载 时 动 态 扩 展 内 存 确 保 足 够 空 间。
排 序 功 能
void Sortcontact(contact* pc)
{
assert(pc);
int input = 0;
printf("请输入你想以0. 名字 1. 电话 2. 年龄 排序的数字:>\n");
scanf("%d", &input);
if (input == 0)
{
qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp_contact_by_name);
Showcontact(pc);
}
else if (input == 1)
{
qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp_contact_by_tele);
Showcontact(pc);
}
else if (input == 2)
{
qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp_contact_by_age);
Showcontact(pc);
}
else
{
printf("无效输入\n");
}
}
int cmp_contact_by_name(const void* e1, const void* e2)
{
return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
}
int cmp_contact_by_age(const void* e1, const void* e2)
{
return ((PeoInfo*)e1)->age - ((PeoInfo*)e2)->age;
}
int cmp_contact_by_tele(const void* e1, const void* e2)
{
return strcmp(((PeoInfo*)e1)->tele, ((PeoInfo*)e2)->tele);
}
标 准 库 排 序:利 用 qsort 实 现 通 用 排 序。
函 数 指 针:符 合 qsort 要 求 的 比 较 函 数 原 型。
字 符 串 比 较:使 用 strcmp 进 行 字 典 序 比 较。
数 值 比 较:直 接 相 减 获 取 大 小 关 系。
查 找 联 系 人
int find_by_name(const contact* pc, char name[MAX])
{
assert(pc);
int i = 0;
for (i = 0;i < pc->sz;i++)
{
if (strcmp(pc->data[i].name, name) == 0)
{
return i;
}
}
return -1;
}
函 数 功 能:在 通 讯 录 中 查 找 指 定 姓 名 的 联 系 人,返 回 其 索 引(未 找 到 返 回 - 1)。
test.c
菜 单 界 面
void menu() {
printf("*******************************************\n");
printf("******** 1. add 2. delete ********\n");
printf("******** 3. search 4. modify ********\n");
printf("******** 5. show 6. sort ********\n");
printf("******** 0. exit ********\n");
printf("*******************************************\n");
}
使 用 星 号 (*) 绘 制 边 框,提 供 清 晰 的 视 觉 界 面。
数 字 编 号 对 应 不 同 功 能,方 便 用 户 选 择。
枚 举 常 量
enum Option
{
EXIT,//0
ADD,//1
DELETE,//2
SEARCH,//3
MODIFY,//4
SHOW,//5
SORT//6
};
定 义 了 一 个 枚 举 类 型 Option,其 中 包 含 了 EXIT(退 出)、ADD(添 加 联 系 人)、DELETE(删 除 联 系 人)、SEARCH(查 找 联 系 人)、MODIFY(修 改 联 系 人)、SHOW(显 示 联 系 人 信 息)、SORT(对 联 系 人 进 行 排 序)等 枚 举 常 量,这 些 常 量 用 于 表 示 不 同 的 操 作 选 项,每 个 常 量 对 应 一 个 整 数 值。
主 循 环 逻 辑
void test()
{
int input = 0;
//创建通讯录
contact con;
//初始化通讯录
Initcontact(&con);
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case EXIT:
//保存通讯录信息到文件中
SaveContact(&con);
DestoryContact(&con);//销毁通讯录
printf("退出通讯录\n");
break;
case ADD:
Addcontact(&con);
break;
case DELETE:
Delcontact(&con);
break;
case SEARCH:
SearchContact(&con);
break;
case MODIFY:
Modifycontact(&con);
break;
case SHOW:
Showcontact(&con);
break;
case SORT:
Sortcontact(&con);
break;
default:
printf("选择错误,请重新输入\n");
break;
}
} while (input);
}
初 始 化:调 用 Initcontact 分 配 内 存 并 加 载 文 件 数 据。
循 环 控 制:使 用 do-while 确 保 至 少 执 行 一 次 菜 单 选 择。
选 项 处 理:通 过 switch 分 支 调 用 不 同 功 能 函 数。
安 全 退 出:退 出 前 保 存 数 据 并 释 放 内 存,防 止 资 源 泄 漏。
程 序 入 口
int main()
{
test();
return 0;
}
main 函 数 是 程 序 的 入 口 点,调 用 test 函 数 启 动 通 讯 录 管 理 系 统,最 后 返 回 0 表 示 程 序 正 常 结 束。
总 结
通 过 对 这 个 C 语 言 通 讯 录 管 理 系 统 代 码 的 分 析,我 们 深 入 了 解 了 如 何 运 用 C 语 言 的 各 种 特 性 实 现 一 个 实 用 的 小 型 项 目。在 实 际 开 发 中,不 断 优 化 和 扩展 这 样 的 项 目,有 助 于 我 们 提 升 编 程 技 能,积 累 项 目 经 验。希 望 这 篇 博 客 能 对 大 家 学 习 C 语 言 和 理 解 程 序 设 计 有 所 帮 助,也 欢 迎 大 家 在 评 论 区 分 享 自 己 的 见 解 和 改 进 思 路!