当前位置: 首页 > article >正文

C 语 言 - - - 简 易 通 讯 录

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,//0ADD,//1DELETE,//2SEARCH,//3MODIFY,//4SHOW,//5SORT//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,//0ADD,//1DELETE,//2SEARCH,//3MODIFY,//4SHOW,//5SORT//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 语 言 和 理 解 程 序 设 计 有 所 帮 助,也 欢 迎 大 家 在 评 论 区 分 享 自 己 的 见 解 和 改 进 思 路!

相关文章:

C 语 言 - - - 简 易 通 讯 录

C 语 言 - - - 简 易 通 讯 录 代 码 全 貌 与 功 能 介 绍通 讯 录 的 功 能 说 明通 讯 录 效 果 展 示代 码 详 解contact.hcontact.ctest.c 总 结 &#x1f4bb;作 者 简 介&#xff1a;曾 与 你 一 样 迷 茫&#xff0c;现 以 经 验 助 你 入 门 C 语 言 &#x1f4a1;个 …...

大模型MCP之UV安装使用

1.Windows安装 1.1 pip安装 pip install uv -i https://pypi.tuna.tsinghua.edu.cn/simple如果需要centos安装pip sudo yum install python3-pipCentOS 8开始使用dnf作为包管理器&#xff1a; sudo dnf install python3-pip对于基于Debian的系统&#xff08;如Ubuntu&#…...

【C++】多线程和多进程

在C++中,多线程通信(同一进程内的线程间交互)和进程间通信(IPC,不同进程间的数据交换)是构建并发系统的核心技术。以下是两种通信机制的详细介绍和典型实现: 一、多线程通信(线程间同步与数据共享) 1. 共享内存与同步原语 通过全局变量或对象成员变量实现数据共享,…...

Vue百日学习计划Day16-18天详细计划-Gemini版

重要提示&#xff1a; 番茄时钟&#xff1a; 每个番茄钟为25分钟学习&#xff0c;之后休息5分钟。每完成4个番茄钟&#xff0c;进行一次15-30分钟的长休息。动手实践&#xff1a; DOM 操作和事件处理的理解高度依赖于实际编码。请务必在浏览器中创建 HTML 页面&#xff0c;并配…...

从验证码绕过到信息轰炸:全面剖析安全隐患与防范策略

在数字化交互场景中&#xff0c;验证码作为区分人类操作与自动化程序的核心屏障&#xff0c;广泛应用于用户身份核验、操作权限确认等关键环节。其设计初衷是通过人机识别机制&#xff0c;保障信息系统交互的安全性与可控性。然而&#xff0c;当验证码验证机制出现异常突破&…...

机器学习知识自然语言处理入门

一、引言&#xff1a;当文字遇上数学 —— 自然语言的数字化革命 在自然语言处理&#xff08;NLP&#xff09;的世界里&#xff0c;计算机要理解人类语言&#xff0c;首先需要将文字转化为数学向量。早期的 One-Hot 编码如同给每个词语分配一个唯一的 “房间号”&#xff0c;例…...

LeetCode 820 单词的压缩编码题解

LeetCode 820 单词的压缩编码题解 题目描述 题目链接 给定一个单词列表&#xff0c;将其编码为一个索引字符串S&#xff0c;格式为"单词1#单词2#…"。要求当某个单词是另一个单词的后缀时&#xff0c;该单词可以被省略。求最终编码字符串的最小长度。 解题思路 逆…...

论信息系统项目的范围管理

论信息系统项目的范围管理 前言一、规划范围管理&#xff0c;收集需求二、定义范围三、创建工作分解结构四、确认范围五、控制范围 前言 为了应对烟草零售客户数量大幅度增长所带来的问题&#xff0c;切实履行控烟履约的相关要求&#xff0c;同时也为了响应国务院“放管服”政策…...

MySQL数据库——支持远程IP访问的设置方法总结

【系列专栏】&#xff1a;博主结合工作实践输出的&#xff0c;解决实际问题的专栏&#xff0c;朋友们看过来&#xff01; 《项目案例分享》 《极客DIY开源分享》 《嵌入式通用开发实战》 《C语言开发基础总结》 《从0到1学习嵌入式Linux开发》 《QT开发实战》 《Android开发实…...

Pageassist安装(ollama+deepseek-r1)

page-assist网站&#xff1a;https://github.com/n4ze3m/page-assist 首先电脑配置node.js&#xff0c;管理员打开命令窗口输入下面命令下载bun npm install -g buncd 到你想要安装page-assist的地方&#xff08;推荐桌面&#xff09; 输入下列命令 git clone https://gith…...

2025年渗透测试面试题总结-安恒[实习]安全服务工程师(题目+回答)

网络安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 安恒[实习]安全服务工程师 1. SQLMap爆出当前库名的参数是什么&#xff1f; 2. Nmap探测系统的参数&am…...

C#运算符

&#x1f9e0; 一、C# 运算符列表&#xff08;按类别分类&#xff09; 类别运算符一元运算符, -, , --, !, ~, (T) x算术运算符, -, *, /, %赋值运算符, , -, *, /, %, &, 比较/关系运算符, !, <, >, <, >逻辑/布尔运算符&&, 按位运算符&, 条件运…...

五月份嵌入式面试总结

目录 1、札记 1.1、芯片的bring up 主要做哪些工作&#xff1a; 2、Linux驱动八股文 中断与同步互斥 2.1.1 内核同步互斥的几种方式 2.1.2 互斥锁和自旋锁的区别 2.1.3 spin_lock 和 spin_lock_irqsave 的区别 2.1.4 进程上下文和中断上下文有什么区别 2.1.5 进行上下…...

数据库行业竞争加剧,MySQL 9.3.0 企业版开始支持个人下载

最新发现&#xff0c;Oracle 官方网站放开了 MySQL 9.3.0 企业版下载链接&#xff0c;个人用户也可以免费下载&#xff0c;不过只能用于学习、开发或者原型测试&#xff0c;不能用于生产环境。 通常我们都是下载 MySQL 社区版&#xff0c;不过 MySQL 企业版可以支持更多高级功能…...

Python实例题:Django搭建简易博客

目录 Python实例题 题目 1. 创建 Django 项目和应用 2. 配置项目 3. 设计模型 blog_app templates blog_app post_list.html admin.py models.py urls.py views.py blog_project urls.py 代码解释 models.py&#xff1a; admin.py&#xff1a; urls.py&…...

Tcping详细使用教程

Tcping详细使用教程 下载地址 https://download.elifulkerson.com/files/tcping/0.39/在windows环境下安装tcping 在以上的下载地中找到exe可执行文件&#xff0c;其中tcping.exe适用于32位Windows系统&#xff0c;tcping64.exe适用于64位Windows操作系统。 其实tcping是个…...

PyCharm 快捷键指南

PyCharm 快捷键指南 常用编辑快捷键 代码完成&#xff1a;Ctrl Space 提供基本的代码完成选项&#xff08;类、方法、属性&#xff09;导入类&#xff1a;Ctrl Alt Space 快速导入所需类语句完成&#xff1a;Ctrl Shift Enter 自动结束代码&#xff08;如添加分号&#…...

如何更改远程桌面连接的默认端口?附外网访问内网计算机方法

远程连接端口根据协议和场景不同有所差异&#xff0c;以下是常见远程连接端口的设置及修改方法&#xff0c;同时附外网访问内网计算机操作。 一、Windows远程桌面默认端口 ‌默认端口‌&#xff1a;3389&#xff08;TCP协议&#xff09;&#xff0c;用于Windows远程桌面服务&…...

【GAN网络入门系列】一,手写字MINST图片生成

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 博主简介&#xff1a;努力学习的22级本科生一枚 &#x1f31f;​&#xff1b;探索AI算法&#xff0c;C&#xff0c;go语言的世界&#xff1b;在迷茫中寻找光芒…...

ubuntu22鼠键失灵恢复记录笔记chatgpt解决

ChatGPT 说&#xff1a; 你提到“Ubuntu 22 鼠键失灵”&#xff0c;这个问题可能涉及以下几方面&#xff1a; &#x1f9ed; 先确认问题 是鼠标问题还是键盘问题&#xff0c;还是触控板&#xff1f; “鼠键”一般理解为“鼠标键”&#xff0c;请确认你是指鼠标左键/右键失灵&a…...

智能呼入:云蝠大模型赋能政府热线

政府热线作为连接政府与民众的重要桥梁&#xff0c;提升智能化水平&#xff0c;成为政府热线亟待解决的问题。 大模型呼入 大模型呼入技术基于先进的自然语言处理和机器学习算法&#xff0c;能够实现对海量语音数据的处理和理解。通过构建大规模的语言模型&#xff0c;系统可…...

STM32 ADC+DMA+TIM触发采样实战:避坑指南与源码解析

知识点1【TRGO的介绍】 1、TRGO的概述 TRGO&#xff1a;Trigger Output&#xff08;触发输出&#xff09;&#xff0c;是定时器的一种功能。 它可以作为外设的启动信号&#xff0c;比如ADC转换&#xff0c;DAC输出&#xff0c;DMA请求等。 对于ADC来说&#xff0c;可以通过…...

(1-4)Java Object类、Final、注解、设计模式、抽象类、接口、内部类

目录 1. Object类 1.1 equals 1.2 toString&#xff08;&#xff09; 2.final关键字 3.注解 4. 设计模式 4.1 单例模式 4.1.1 饿汉式 4.1.3 饿汉式 VS 懒汉式 5. 抽象类&抽象方法 6. 接口 7.内部类 7.1 成员内部类 7.2 静态内部类 7.3 方法内部类 7.4 匿名内…...

在服务器上安装AlphaFold2遇到的问题(3)_cat: /usr/include/cudnn_version.h: 没有那个文件或目录

[rootlocalhost ~]# cat /usr/include/cudnn_version.h cat: /usr/include/cudnn_version.h: 没有那个文件或目录这个错误表明系统找不到 cudnn_version.h 头文件&#xff0c;说明 cuDNN 的开发文件&#xff08;头文件&#xff09;没有正确安装。以下是完整的解决方案&#xff…...

实验-实现向量点积-RISC-V(计算机组成原理)

目录 一、实验内容 二、实验步骤 三、源代码 四、实现效果 五、实验环境 六、实验小结与思考 一、实验内容 首先&#xff0c;我们用一个简单的“向量点积”运算作为热身。你将拿到一个不完整的汇编代码“task2-向量点积”&#xff0c;我们的目标是按照C语言描述的功能&a…...

5.16本日总结

一、英语 背诵list30&#xff0c;复习list1 二、数学 学习14讲部分内容&#xff0c;订正30讲13讲题目 三、408 学习计网5.3知识点&#xff0c;完成5.1&#xff0c;5.2题目并订正 四、总结 高数对于基本定义概念类题目掌握不好&#xff0c;做题时往往不会下手&#xff0c…...

描述性统计工具 - AxureMost 落葵网

描述性统计工具是用于汇总和分析数据&#xff0c;以更好地了解数据特征的工具1。以下是一些常见的描述性统计工具简介&#xff1a; 描述性统计工具 Excel 基本统计函数&#xff1a;提供了丰富的函数用于计算描述性统计量。例如&#xff0c;AVERAGE 函数用于计算平均值&#xf…...

【AI学习】AI大模型技术发展研究月报的生成提示词

AI大模型技术发展研究月报生成提示词 请输出AI大模型技术发展研究月报&#xff0c;要求如下&#xff1a; —————————— 任务目标 在今天&#xff08;{{today}}&#xff09;往前连续 30 天内&#xff0c;检索已正式公开发表的、与AI大模型&#xff08;参数量 ≥10B&am…...

麒麟桌面系统文件保险箱快捷访问指南:让重要文件夹一键直达桌面!

往期文章链接&#xff1a;统信操作系统自定义快捷键配置音量调节功能指南 Hello&#xff0c;大家好啊&#xff0c;今天给大家带来一篇麒麟桌面操作系统上配置文件保险箱内文件夹桌面快捷方式的文章&#xff0c;欢迎大家分享点赞&#xff0c;点个在看和关注吧&#xff01;在日常…...

LearnOpenGL --- 你好三角形

你好&#xff0c;三角形的课后练习题 文章目录 你好&#xff0c;三角形的课后练习题一、创建相同的两个三角形&#xff0c;但对它们的数据使用不同的VAO和VBO 一、创建相同的两个三角形&#xff0c;但对它们的数据使用不同的VAO和VBO #include <glad/glad.h> #include &…...