
个人主页 : 流年如梦
文章目录
- 一.基于单链表实现通讯录
- 二.整体结构
- 三.通讯录的实现
-
- [3.1SList.h --> 顺序表声明](#3.1SList.h --> 顺序表声明)
- [3.2contact.h --> 通讯录声明](#3.2contact.h --> 通讯录声明)
- [3.3contact.c --> 核心功能(增、删、查、改、显示等)](#3.3contact.c --> 核心功能(增、删、查、改、显示等))
- [3.4test.c --> 主函数](#3.4test.c --> 主函数)
- 🎯总结
- ⚠️易错点
Ladies and gentlemen,此次基于单链表来简单实现通讯录,将会涉及到的知识点分别有:结构体、动态内存管理、单链表、文件操作
一.基于单链表实现通讯录
1.1通讯录主要功能要求
- 存储最多100人信息
- 姓名、性别、年龄、电话、地址
- 要有增、删、查、改、显示(核心功能)
- 程序退出数据不会丢失(文件需要保存下来)
- 底层采用单链表存储,不使用顺序表
二.整体结构
分别有:
SList.h/c--> 动态顺序表底层contact.h/c--> 通讯录业务逻辑test.c--> 测试菜单
三.通讯录的实现
3.1SList.h --> 顺序表声明
SList.c单链表的实现在这里👉单链表
c
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "contact.h"
typedef struct PersonInfo SLTDataType;
typedef struct SListNode
{
SLTDataType data;
struct SListNode* next;
}SLTNode;
//打印
void SLTPrint(SLTNode* phead);
//尾插头插
void SLTPushBack(SLTNode** pphead, SLTDataType x);
void SLTPushFront(SLTNode** pphead, SLTDataType x);
//尾删头删
void SLTPopBack(SLTNode** pphead);
void SLTPopFront(SLTNode** pphead);
//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x);
//在pos之前之后插入
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
void SLTInsertAfter(SLTNode* pos, SLTDataType x);
//删除pos节点
void SLTErase(SLTNode** pphead, SLTNode* pos);
//删除pos后的节点
void SLTEraseAfter(SLTNode* pos);
//销毁
void SListDesTroy(SLTNode** pphead);
3.2contact.h --> 通讯录声明
c
#define NAME_MAX 100
#define SEX_MAX 4
#define TEL_MAX 11
#define ADDR_MAX 100
typedef struct SListNode contact;
typedef struct PersonInfo
{
char name[NAME_MAX];
char sex[SEX_MAX];
int age;
char tel[TEL_MAX];
char addr[ADDR_MAX];
}PeoInfo;
//初始化
void InitContact(contact** con);
//添加
void AddContact(contact** con);
//删除
void DelContact(contact** con);
//显示
void ShowContact(contact* con);
//查找
void FindContact(contact* con);
//修改
void ModifyContact(contact** con);
//销毁
void DestroyContact(contact** con);
3.3contact.c --> 核心功能(增、删、查、改、显示等)
其中核心功能可参考👉顺序表的应用 --> 简单通讯录的实现
c
#include "contact.h"
#include "SList.h"
void LoadContact(contact** con)
{
FILE* pf = fopen("contact.txt", "rb");
if (pf == NULL)
{
perror("fopen error!\n");
return;
}
PeoInfo info;
while (fread(&info, sizeof(info), 1, pf))
{
SLTPushBack(con, info);
}
printf("历史数据导入通讯录成功!\n");
fclose(pf);
}
void InitContact(contact** con)
{
*con = NULL;
LoadContact(con);
}
void AddContact(contact** con)
{
PeoInfo info;
printf("请输入姓名:");
scanf("%s", info.name);
printf("请输入性别:");
scanf("%s", info.sex);
printf("请输入年龄:");
scanf("%d", &info.age);
printf("请输入电话:");
scanf("%s", info.tel);
printf("请输入地址:");
scanf("%s", info.addr);
SLTPushBack(con, info);
printf("插入成功!\n");
}
contact* FindByName(contact* con, char name[])
{
contact* cur = con;
while (cur)
{
if (strcmp(cur->data.name, name) == 0)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
void DelContact(contact** con)
{
char name[NAME_MAX];
printf("请输入要删除的姓名:");
scanf("%s", name);
contact* pos = FindByName(*con, name);
if (pos == NULL)
{
printf("用户不存在,删除失败!\n");
return;
}
SLTErase(con, pos);
printf("删除成功!\n");
}
void ShowContact(contact* con)
{
printf("%-10s %-4s %-4s %-15s %-20s\n",
"姓名", "性别", "年龄", "电话", "地址");
contact* cur = con;
while (cur)
{
printf("%-10s %-4s %-4d %-15s %-20s\n",
cur->data.name,
cur->data.sex,
cur->data.age,
cur->data.tel,
cur->data.addr);
cur = cur->next;
}
}
void FindContact(contact* con)
{
char name[NAME_MAX];
printf("请输入要查找的姓名:");
scanf("%s", name);
contact* pos = FindByName(con, name);
if (pos == NULL)
{
printf("用户不存在!\n");
return;
}
printf("查找成功!\n");
printf("%-10s %-4s %-4d %-15s %-20s\n",
pos->data.name,
pos->data.sex,
pos->data.age,
pos->data.tel,
pos->data.addr);
}
void ModifyContact(contact** con)
{
char name[NAME_MAX];
printf("请输入要修改的姓名:");
scanf("%s", name);
contact* pos = FindByName(*con, name);
if (pos == NULL)
{
printf("用户不存在,修改失败!\n");
return;
}
printf("请输入新姓名:");
scanf("%s", pos->data.name);
printf("请输入新性别:");
scanf("%s", pos->data.sex);
printf("请输入新年龄:");
scanf("%d", &pos->data.age);
printf("请输入新电话:");
scanf("%s", pos->data.tel);
printf("请输入新地址:");
scanf("%s", pos->data.addr);
printf("修改成功!\n");
}
void SaveContact(contact* con)
{
FILE* pf = fopen("contact.txt", "wb");
if (pf == NULL)
{
perror("fopen error!\n");
return;
}
contact* cur = con;
while (cur)
{
fwrite(&cur->data, sizeof(cur->data), 1, pf);
cur = cur->next;
}
printf("通讯录保存成功!\n");
fclose(pf);
}
void DestroyContact(contact** con)
{
SaveContact(*con);
SListDesTroy(con);
}
3.4test.c --> 主函数
c
#include "SList.h"
#include "contact.h"
void menu()
{
contact* con = NULL;
InitContact(&con);
int op = -1;
do
{
printf("***********************************\n");
printf("***** 1、添加 2、删除 *****\n");
printf("***** 3、查找 4、修改 *****\n");
printf("***** 5、显示 0、退出 *****\n");
printf("***********************************\n");
printf("请选择操作:");
scanf("%d", &op);
switch (op)
{
case 1: AddContact(&con); break;
case 2: DelContact(&con); break;
case 3: FindContact(con); break;
case 4: ModifyContact(&con); break;
case 5: ShowContact(con); break;
case 0: DestroyContact(&con); break;
default: printf("输入错误,请重试\n");
}
} while (op != 0);
}
int main()
{
menu();
return 0;
}
最终运行结果(部分截图) :

🎯总结
- 底层采用单链表存储,无扩容、无空间浪费
- 支持增删查改+文件持久化,数据退出不丢失
- 删除或修改只需找到节点,不用移动数据,效率更高
- 代码模块化,分文件清晰,易于维护与扩展
- 相比顺序表,更适合频繁增删的业务场景
⚠️易错点
- 链表操作未使用二级指针,导致头指针修改失效
- 删除或查找时未判断用户是否存在,直接操作报错
- 字符串比较未使用
strcmp,判断错误- 文件打开未判空,导致崩溃或数据丢失
- 销毁时未保存文件,数据无法持久化
- 释放节点后未置空,产生野指针
👀 关注 我们一路同行,从入门到大师,慢慢沉淀、稳步成长
❤️ 点赞 鼓励原创,让优质内容被更多人看见
⭐ 收藏 收好核心知识点与实战技巧,需要时随时查阅
💬 评论 分享你的疑问或踩坑经历,一起交流避坑、共同进步