C 语 言 - - - 简 易 通 讯 录

news2025/5/17 19:19:02

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 语 言 和 理 解 程 序 设 计 有 所 帮 助,也 欢 迎 大 家 在 评 论 区 分 享 自 己 的 见 解 和 改 进 思 路!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2377919.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

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

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

五月份嵌入式面试总结

目录 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 企业版可以支持更多高级功能…

Tcping详细使用教程

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

【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…

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

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

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

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

从硬件角度理解“Linux下一切皆文件“,详解用户级缓冲区

目录 前言 一、从硬件角度理解"Linux下一切皆文件" 从理解硬件是种“文件”到其他系统资源的抽象 二、缓冲区 1.缓冲区介绍 2.缓冲区的刷新策略 3.用户级缓冲区 这个用户级缓冲区在哪呢&#xff1f; 解释关于fork再加重定向“>”后数据会打印两份的原因 4.内核缓冲…

游戏站的几种形式

游戏站点的主要形式&#xff1a;单品游戏站、游戏盒子站与单类型游戏盒子站 随着互联网的普及和游戏产业的快速发展&#xff0c;游戏站点作为玩家获取游戏资源和信息的重要平台&#xff0c;呈现出多种形式。本文将分析三种常见的游戏站点形式&#xff1a;单品游戏站、游戏盒子站…

打造智能化军工软件工厂,破解版本管理难题

在数字化浪潮席卷全球的当下&#xff0c;军工行业正经历着前所未有的软件工业化转型。作为这一进程的核心支撑&#xff0c;软件工厂模式正在重塑军工领域的研发体系。然而&#xff0c;传统版本管理方式已难以适应现代军工软件研发的复杂需求&#xff0c;成为制约行业发展的关键…

SpringbBoot nginx代理获取用户真实IP

为了演示多级代理场景&#xff0c;我们分配了以下服务器资源&#xff1a; 10.1.9.98&#xff1a;充当客户端10.0.3.137&#xff1a;一级代理10.0.4.105&#xff1a;二级代理10.0.4.129&#xff1a;三级代理10.0.4.120&#xff1a;服务器端 各级代理配置 以下是各级代理的基本配…