用C语言和文本文件实现一个简单的,可保存的通讯录

news/2025/11/28 22:42:42/文章来源:https://www.cnblogs.com/827-s/p/19284329

我们先思考一个通讯录都有那些信息,很明显通讯录记录的是人
人有哪些信息呢
这里我就写5个吧,分别是姓名,年龄,电话,性别,地址
然后我们把他们写成一个结构体,最好定义在头文件里,这样在使用的时候更方便
我们还可以把要使用的一些常用的或者要修改的常量定义成枚举,后续有什么要添加的,直接就能在枚举中添加

//枚举的一些关键常量
enum NUM
{NAME = 20, //名字TELE = 12,  //电话SEX = 10,  //性别ADDR = 100,  //地址ADD = 1,DEL = 2,SEARCH = 3,MODIFY = 4,SHOW = 5,SORT = 6,EXIT = 0,CLEAR =7,//菜单//ARRDATA = 100SIZE = 3,  //通讯录初始容量大小SIZEADD = 2  //每次扩容的增加量
};
//人的信息
typedef struct peo
{char name[NAME];   //姓名int age;            //年龄char tele[TELE];  //电话char sex[SEX];    //性别char addr[ADDR];   //地址
}peo; 

这里的枚举内容我直接把参数都写里了,后续在思考的过程就不再改动了
接下来思考,光有一个人的信息是不够的,无法完整表达一个通讯录,当然,由于我们只是简单实现,我们就只再记录通讯录的人员数量和通讯录的容量
把他们简单封装成一个结构体

typedef struct contact
{peo* data;          //人的相关数据size_t count;     //成员数量size_t capacity;   //通讯录容量
}contact;

这里我都是进行了重命名了的,方便后续的使用
这些都是头文件里面要用的,还有一些库函数,我就在这里提前定义了

#include<stdio.h>
#include<string.h>
#include<assert.h>
#include<stdlib.h>
#include<errno.h>

然后我们写主函数
我们考虑通讯录运行一定要有菜单
然后我们用switch语句写分支结构
我们需要用户主动输入选择,所以我们还需要一个变量接收,这个变量还可以用于switch语句的判断
菜单我们可以单独封装一个函数
可以实现增加联系人,删除联系人,查找联系人,修改联系人,显示联系人,按名字排序联系人,清空联系人,和退出保存,将数据保存到文件中,把这些功能都封装成函数
还得创建一个结构体变量用于存放通讯录的数据
还需要一个函数对结构体进行初始化,并把保存在文件中的信息放入到结构体中
于是主函数如下

#include"contact.h"
static void menu()   //菜单函数
{printf("****************************************\n");printf("****************************************\n");printf("****************************************\n");printf("***       1.add      2.del        ******\n");printf("***       3.search   4.modify     ******\n");printf("***       5.show     6.sort       ******\n");printf("***       0.exit     7.clear      ******\n");printf("****************************************\n");printf("****************************************\n");printf("****************************************\n");
}
int main()
{int input = 0;        //存放菜单选择输入的值contact con;     //创建存放通讯录数据的结构体init_contact(&con);//初始化结构体do{menu();printf("请输入要进行的功能\n");scanf("%d", &input);        //菜单的选择输入switch (input)            //菜单选择的判断逻辑{case ADD:add_contact(&con);       //添加函数break;case DEL:del_contact(&con);       //删除函数break;case SEARCH:search_contact(&con);    //查找函数break;case MODIFY:modify_contact(&con);    //修改函数break; case SHOW:show_contact(&con);        //显示函数break;case SORT:sort_contact(&con);       //排序函数break;case CLEAR:clear_contact(&con);   //清空函数break;case EXIT:save_contact(&con);       //保存函数destroycontact(&con);      //推出后内存的销毁printf("程序退出\n");break;default:printf("选择错误,请重新选择\n");//其他逻辑判断break;}} while (input)//do while的循环判断return 0;
}

主函数别忘了引用头文件
接下来我们封装函数
把函数单独放在一个源文件

#include"contact.h"
//初始化函数
//对于初始化我们不仅要初始化结构体内容,还要把之前保存在文件中的信息放入到结构体中,当然还涉及到一个动态扩容的问题,我们的结构体初始设计的是有容量的,如果里面放的信息多了,还要涉及到扩容的问题,
这些都要设计函数
static void expandCapacity(contact* con)  //扩容
{assert(con);  //断言防止空指针if (con->capacity == con->count)   //容量判断{void* p = 0;p = realloc(con->data, (con->capacity + SIZEADD) * sizeof(peo));//追加信息容量if (NULL == p) //报错判断{printf("expandCapacity::%s\n", strerror(errno));return;}con->capacity += SIZEADD;//修改容量标记con->data = (peo*)p;       //修改指针位置(其实没变)printf("增容成功\n");}
}
static int read_conact(contact* con)   //读取文件中保存的通讯录信息
{static void expandCapacity(contact * con);   //函数声明FILE* p = fopen("contact.txt", "r");      //打开文件if (p == NULL)                          //报错判断{return 1;}peo a = { 0 };while (fread(&a, sizeof(peo), 1, p) == 1)//循环提取信息,这里取决于保存函数的写法{expandCapacity(con);  //扩容con->data[con->count] = a;//赋值(con->count)++;//成员计数加一}fclose(p);p = NULL;//关闭文件,指针空置return 0;
}
void init_contact(contact* con)    //初始化函数
{static int read_conact(contact * con);//函数调用声明assert(con);////断言防止空指针con->count = 0;//初始化人数void* p = 0;//内存指针p = calloc(SIZE, sizeof(peo));//创建内存if (NULL == con->data)//报错判断{printf("init_contact:calloc:%s\n", strerror(errno));return;}con->data = (peo*)p;//初始化人信息指针con->capacity = SIZE;//初始化容量if (read_conact(con) == 1)//读取之前的数据{perror("读取旧数据失败");//问题判断return;}
}//初始化我们已经完成了
//接下来我们写添加函数
void add_contact(contact* con)
{static void expandCapacity(contact * con);  //函数声明assert(con);//断言防止空指针expandCapacity(con);//扩容判断printf("请输入姓名\n");scanf("%s", (con->data+con->count)->name);printf("请输入年龄\n");scanf("%d", &((con->data + con->count)->age));printf("请输入电话\n");scanf("%s", con->data[con->count].tele);printf("请输入性别\n");scanf("%s", con->data[con->count].sex);printf("请输入地址\n");scanf("%s", con->data[con->count].addr);//这些都是信息是输入con->count++;//计数加一printf("增加成功\n");
}//接下来我们写显示函数
void show_contact(contact* con)
{assert(con);//断言防止空指针int i = 0;printf("%-20s\t%-5s\t%5s\t%12s\t%30s\n", "姓名", "年龄", "电话", "性别", "地址");//显示时方便观察,打印一个标签for (i = 0; i < con->count; i++)//循环打印人员信息{printf("%-20s\t%-5d\t%-5s\t%-12s\t%-30s\n",con->data[i].name,con->data[i].age,con->data[i].tele,con->data[i].sex,con->data[i].addr);}
}//查找函数
//查找函数不仅要能查找,还要能显示,我们分成两个函数,因为删除也需要查找
//查找部分
static int find_by_name(const contact* con,const char* arr)  //依据名字查找
{assert(con && arr);//断言防止空指针int i = 0;for (i = 0; i < con->count; i++)//循环遍历查找{if (0 == strcmp(con->data[i].name, arr)){return i;//找到返回下标}}return -1;//找不到返回-1
}//查找
void search_contact(contact* con)
{static int find_by_name(const contact * con, const char* arr);//函数声明assert(con);//断言防止空指针if (con->count == 0)//无联系人情况{printf("无联系人可查找\n");return;}printf("请输入要查找的人的姓名\n");char arr[NAME] = { 0 };scanf("%s", arr);int a = find_by_name(con, arr);//套用查找函数if (a == -1){printf("要查找的人不存在\n");return;}printf("%-20s\t%-5s\t%-5s\t%-12s\t%-30s\n", "姓名", "年龄", "电话", "性别", "地址");printf("%-20s\t%-5d\t%-5s\t%-12s\t%-30s\n",con->data[a].name,con->data[a].age,con->data[a].tele,con->data[a].sex,con->data[a].addr);//显示部分
}//删除
void del_contact(contact* con)
{static int find_by_name(const contact * con, const char* arr);//函数声明assert(con);//断言防止空指针char arr[NAME] = { 0 };if (con->count == 0)//无联系人情况{printf("无联系人可删除\n");return;}printf("请输入要删除人的名字\n");scanf("%s", arr);int a = find_by_name(con,arr);//函数回调if (a == -1){printf("要删除的人不存在\n");return;}for (; a < con->count - 1; a++)//删除本质就是把后面的信息提前,覆盖掉这个下标的信息//当然也还有其他的写法{con->data[a] = con->data[a + 1];}con->count--;printf("删除成功\n");
}//修改函数
//修改也要先找void modify_contact(contact* con)
{static int find_by_name(const contact * con, const char* arr);//函数声明assert(con);//断言防止空指针if (con->count == 0){printf("无联系人可修改\n");//没有的情况判断return;}printf("请输入要修改的人的姓名\n");char arr[NAME] = { 0 };scanf("%s", arr);int a = find_by_name(con, arr);//调用函数if (a == -1){printf("要修改的人不存在\n");return;}printf("请输入修改姓名\n");scanf("%s", con->data[a].name);printf("请输入修改年龄\n");scanf("%d", &(con->data[a].age));printf("请输入修改电话\n");scanf("%s", con->data[a].tele);printf("请输入修改性别\n");scanf("%s", con->data[a].sex);printf("请输入修改地址\n");scanf("%s", con->data[a].addr);printf("修改成功\n");//信息的修改
}//排序函数
//这里我使用了qsort
static int cmp_peo_by_name(const void* e1, const void* e2)//qsort调用的排序函数
{return strcmp(((const peo*)e1)->name, ((const peo*)e2)->name);
}void sort_contact(contact* con)//本体
{static int cmp_peo_by_name(const void* e1, const void* e2);//函数调用assert(con);//断言if (con->count == 0){printf("无联系人可排序\n");//没人的情况return;}printf("正在按姓名排序\n");qsort(con->data, con->count, sizeof(peo), cmp_peo_by_name);//qsort排序printf("排序成功\n");show_contact(con);//这里不声明是因为这些函数都会在头文件中声明,但内些静态函数不会
}//销毁,保存,清空
void destroycontact(contact* con)//释放内存
{assert(con);free(con->data);con->data = NULL;
}void save_contact(const contact* con)//保存信息
{assert(con);FILE* p = fopen("contact.txt", "wb");if (p == NULL){perror("保存失败:save_contact:fopen");return;}fwrite(con->data, sizeof(peo), con->count, p);fclose(p);p = NULL;
}void clear_contact(contact* con)//清空文件信息和当前结构体信息
{assert(con);FILE* p = fopen("contact.txt", "w");if (p == NULL)return;fclose(p);p = NULL;con->count = 0;memset(con->data,0, sizeof(peo));printf("已全部清除\n");
}

最后我们再在头文件中声明一下这些函数,为了调用

//初始化通讯录
void init_contact(contact* con);//增加联系人
void add_contact(contact* con);//显示
void show_contact(contact* con);//删除
void del_contact(contact* con);//查找
void search_contact(contact* con);//修改
void modify_contact(contact* con);//排序
void sort_contact(contact* con);//退出后内存销毁
void destroycontact(contact* con);//退出后的保存
void save_contact(const contact*con);//清空通讯录
void clear_contact(contact*con);

静态函数不用声明,主要和一些商业习惯有关

好了,这样就结束了,比较简陋,旨在练手,大佬勿喷

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

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

相关文章

Ai元人文:价值的惊险一跃——当AI伦理告别“救火”迈向“共生”

Ai元人文:价值的惊险一跃——当AI伦理告别“救火”迈向“共生” 我们正身处一个巨大的认知裂谷之上。裂谷的一侧,是日新月异、以“数值优化”为圭臬的技术逻辑;另一侧,是古老而复杂、以“意义判断”为内核的人类价…

HarmonyOS 应用开发:深入探索截屏与录屏API的创新实践 - 详解

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

11.28每日总结

今天主要的课程有人机交互技术和机器学习,人机交互的实验做完了,机器学习的实验跟着课程在做,明天去驾校报名,在剩下的大学时间里把能做的多做些

搞定多数据源 + 统一数据格式!用工厂 / 策略 / 适配器模式解决用户端与管理端协同开发痛点

作为一名后端开发,最近在做会员活动相关的用户端系统时,踩了个典型的协同开发坑:管理端和用户端并行开发,管理端负责配置活动规则,用户端需要根据配置展示活动内容,但测试数据源和生产数据源格式不统一、切换数据…

测试档案

测试1测试文章测试标头测试代码

GPU内存层次结构如何影响计算体验

本文深入探讨GPU内存层次结构对计算性能的影响,涵盖CUDA内存类型、缓存层级以及H100系列的新特性,帮助开发者优化内存访问延迟、最大化内存带宽并降低功耗,实现GPU性能的极致发挥。GPU内存层次结构:隐藏的性能瓶颈…

P13270 【模板】最小表示法

题目背景 原模板题:P1368 工艺。 题目描述 若长度为 \(n\) 的字符串 \(s\) 中可以选择一个位置 \(i\),使得 \(\overline{s_i\cdots s_ns_1\cdots s_{i-1}}=t\),则称 \(s\) 与 \(t\) 循环同构。字符串 \(s\) 的最小表…

P5357 【模板】AC 自动机

题目背景 本题原为“AC 自动机(二次加强版)”。完成本题前可以先完成 AC 自动机(简单版) 和 AC 自动机(简单版 II) 两道题,为 AC 自动机更简单的应用。 题目描述 给你一个文本串 \(S\) 和 \(n\) 个模式串 \(T_{…

分布式Session会话实现优秀的方案

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

Revive Adserver存储型XSS漏洞技术分析

本文详细分析了Revive Adserver广告管理系统中的存储型XSS安全漏洞,涉及inventory-retrieve.php和campaign-edit.php文件,包含漏洞复现步骤、技术原理和修复方案,已分配CVE-2025-52667编号。Revive Adserver存储型X…

2025年终总结

多好的35岁,人生黄金期,全盛时期。 2024年年终总结写道:“2024年是个好年份。今年过得真好,有滋有味。果真人生至味是清欢。2024年第一次享受平静,第一次没有大的目标、计划、挑战。只是静心生活,学习,做事。20…

局域网---局域网传输文件及共享桌面

我想要在局域网内进行文件传输以及共享桌面,可以使用文件快传:https://transfer.52python.cn/软件界面:该软件可以联网使用,可以本地部署使用:打完收工!

P2709 【模板】莫队 / 小B的询问

题目描述 小 B 有一个长为 \(n\) 的整数序列 \(a\),值域为 \([1,k]\)。 他一共有 \(m\) 个询问,每个询问给定一个区间 \([l,r]\),求: \[\sum\limits_{i=1}^k c_i^2 \]其中 \(c_i\) 表示数字 \(i\) 在 \([l,r]\) 中…

并不打算的

光和热发货的干扰星级酒店工业

P1903 【模板】带修莫队 / [国家集训队] 数颜色 / 维护队列

题目描述 墨墨购买了一套 \(N\) 支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问。墨墨会向你发布如下指令:\(Q\ L\ R\) 代表询问你从第 \(L\) 支画笔到第 \(R\) 支画笔中共有几种不同颜色的画笔…

P1883 【模板】三分 / 函数

题目描述 给定 \(n\) 个二次函数 \(f_1(x),f_2(x),\dots,f_n(x)\)(均形如 \(ax^2+bx+c\)),设 \(F(x)=\max\{f_1(x),f_2(x),...,f_n(x)\}\),求 \(F(x)\) 在区间 \([0,1000]\) 上的最小值。 输入格式 输入第一行为正…

CSP2025 T4

Sol 赛时是不是多想想就会了??? 考虑 \(f_{i,j,k}\) 表示前 \(i\) 个位置,干掉了 \(j\) 个人,然后有 \(k\) 个位置已经被钦定了。 如果 \(s_i=1\),令 \(c_i\) 表示忍耐度 \(\le i\) 的人数,那么当前可以选的人数…

Day5 Scrum冲刺博客

Day5 Scrum冲刺博客 1. 团队会议 todo补充会议照片 1)昨天已完成的工作前端初步完成了四个通知栏目页的搭建后端完成了数据库建立的完整流程 完成了时间信息的提取函数 规定了部分接口格式测试检查新加入各代码文件格…

台达变频器与西门子1200 PLC互联借Modbus RTU转Profinet推动工业物联网

一、案例项目背景 在工业自动化与工业物联网深度融合的趋势下,新能源电池行业作为战略性新兴产业,正朝着高效化、智能化方向快速发展。某头部新能源电池生产企业新建一条方形动力电池模组装配线,核心控制单元采用西…

2025-11-28

CF Problem - 1766C - Codeforces(1300)(dp)(模拟) 一笔画,要经过所有黑色,并且有且仅有一次 不能经过白色 #include <bits/stdc++.h> using namespace std; #define LL long long const LL mod = 9982443…