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

个人主页流年如梦

专栏《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. 释放节点后未置空,产生野指针

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

相关推荐
流年如夢3 小时前
单链表Ⅲ(LeetCode)
数据结构·算法·leetcode·职场和发展
洛水水4 小时前
【数据结构】红黑树详解
数据结构·红黑树
炸膛坦客4 小时前
嵌入式 - 数据结构与算法:(1-9)数据结构 - 队列(Queue)
c语言·数据结构
AbandonForce5 小时前
哈希表(HashTable,散列表)个人理解
开发语言·数据结构·c++·散列表
代码中介商5 小时前
栈结构完全指南:顺序栈实现精讲
c语言·开发语言·数据结构
样例过了就是过了5 小时前
LeetCode热题100 编辑距离
数据结构·c++·算法·leetcode·动态规划
khalil10206 小时前
代码随想录算法训练营Day-46 动态规划13 | 647. 回文子串、516.最长回文子序列、动态规划总结
数据结构·c++·算法·leetcode·动态规划·回文子串·回文子序列
Raink老师7 小时前
用100道题拿下你的算法面试(链表篇-4):合并 K 个有序链表
算法·链表·面试
用户86022504674727 小时前
Jetpack ViewModel 入门与实践
android