单链表的应用 --> 简单通讯录的实现

个人主页 : 流年如梦

专栏 : 《C语言》 《数据结构》

文章目录

Ladies and gentlemen,此次基于单链表来简单实现通讯录,将会涉及到的知识点分别有:结构体、动态内存管理、单链表、文件操作

一.基于单链表实现通讯录

1.1通讯录主要功能要求

  1. 存储最多100人信息
  2. 姓名、性别、年龄、电话、地址
  3. 要有增、删、查、改、显示(核心功能)
  4. 程序退出数据不会丢失(文件需要保存下来)
  5. 底层采用单链表存储,不使用顺序表

二.整体结构

分别有:

  1. SList.h/c --> 动态顺序表底层
  2. contact.h/c --> 通讯录业务逻辑
  3. 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;
}

最终运行结果(部分截图) :

🎯总结

  1. 底层采用单链表存储,无扩容、无空间浪费
  2. 支持增删查改+文件持久化,数据退出不丢失
  3. 删除或修改只需找到节点,不用移动数据,效率更高
  4. 代码模块化,分文件清晰,易于维护与扩展
  5. 相比顺序表,更适合频繁增删的业务场景

⚠️易错点

  1. 链表操作未使用二级指针,导致头指针修改失效
  2. 删除或查找时未判断用户是否存在,直接操作报错
  3. 字符串比较未使用strcmp,判断错误
  4. 文件打开未判空,导致崩溃或数据丢失
  5. 销毁时未保存文件,数据无法持久化
  6. 释放节点后未置空,产生野指针

👀 关注 我们一路同行,从入门到大师,慢慢沉淀、稳步成长
❤️ 点赞 鼓励原创,让优质内容被更多人看见
⭐ 收藏 收好核心知识点与实战技巧,需要时随时查阅
💬 评论 分享你的疑问或踩坑经历,一起交流避坑、共同进步

相关推荐
David猪大卫16 小时前
【C++修炼】map 和 set 的使用
c语言·开发语言·数据结构·c++·经验分享·笔记·学习
vvilkim18 小时前
数据结构类型、区别与应用场景:一篇讲透选型逻辑
数据结构
Starry-sky(jing)19 小时前
Vidda ADB 开启:R Pro 65 找不到开发者选项的处理与连接验收
android·adb·远程控制·电视·vidda
雨白20 小时前
从原理到实践:使用 Kotlin KSP 仿写 ButterKnifeLite
android·kotlin
疯华整猫21 小时前
把二十七个 init 从首帧前面搬走:用 Trae 把启动优化钉进 CI
android·性能优化·ai编程
传奇开心果1 天前
【探秘 Jetpack Compose 框架深水区】 第8课:rememberSaveable 的边界在哪里?——Bundle、Saver 与什么值得跨进程存活
android·kotlin
阿卡好可爱1 天前
Lua 热更新不生效:改完文件线上仍是旧逻辑,package.loaded 与 upvalue 重绑定详解
数据结构·分布式·后端·架构·lua
Frankie_stand1 天前
测试自动化实战指南:从脚本化、自动判据到 CI 集成的完整落地路径
android·ci/cd·自动化
恋猫de小郭1 天前
Flutter 大小核优化又被回滚,然后又开新提案?
android·前端·flutter