【数据结构与算法】别再只会写 demo!C 语言通讯录实战:从单链表底层到业务功能全封装

🔥小龙报:个人主页

🎬作者简介:C++研发,嵌入式,机器人等方向学习者

❄️个人专栏:《C语言》《【初阶】数据结构与算法》
永远相信美好的事情即将发生

文章目录

前言

通讯录是 C 语言数据结构的经典实战场景,本文基于单链表(你文中 "顺序表" 为笔误)实现完整的通讯录功能,从框架搭建、头文件设计到底层链表操作层层拆解。通过typedef前置声明解决头文件互相包含的编译问题,封装添加、删除、修改、查找、展示等核心功能,结合可运行的代码示例和效果演示,帮助初学者理解 "数据结构底层逻辑 + 业务功能封装" 的开发思路,掌握 C 语言项目的模块化设计方法。


一、通讯录框架Test.c

csharp 复制代码
#include "SList.h"

void menu()
{
	printf("******************通讯录******************\n");
	printf("*******1.增加联系人   2.删除联系人********\n");
	printf("*******3.修改联系人   4.查找联系人********\n");
	printf("*******5.展示联系人   0.   退出  *********\n");
	printf("******************************************\n");
}

int main()
{
	int op;
	contact* con = NULL;
	do
	{
		menu();
		printf("请输入你的选择:");
		scanf("%d", &op);

		switch (op)
		{
		case 1:
			AddContact(&con);
			break;
		case 2:
			DelContact(&con);
			break;
		case 3:
			ModifyContact(&con);
			break;
		case 4:
			FindContact(con);
			break;
		case 5:
			ShowContact(con);
			break;
		case 0:
			printf("退出通讯录....\n");
			break;
		default:
			printf("输入错误,请重新选择您的操作!\n");
			break;
		}
	} while (op);
	DestroyContact(&con);
	return 0;
}

二、通讯录

2.1 Contacts.h

csharp 复制代码
#define Max_Name 30
#define Max_Gende 30
#define Max_Tel 30
#define Max_Addr 30

//姓名 性别 年龄 电话 地址
typedef struct PersonalInfor
{
	char name[Max_Name];
	char gender[Max_Gende];
	int age;
	char tel[Max_Tel];
	char addr[Max_Addr];
}peoInfo;


typedef struct SLTNode contact;


//添加通讯录数据
void AddContact(contact** con);
//删除通讯录数据
void DelContact(contact** con);
//展示通讯录数据
void ShowContact(contact* con);
//查找通讯录数据
void FindContact(contact* con);
//修改通讯录数据
void ModifyContact(contact** con);
//销毁通讯录数据
void DestroyContact(contact** con);

注意

(1)为什么要typedef struct SeqList contact;这么写

答:前置声明:因为在SList.h和contacts.h里互相包含头文件会编译错误所以这么写。

前置声明:前向声明(前置声明)只是告诉编译器 "这个类型存在",但如果要实际使用这个类型(比如访问成员、创建非指针变量、调用相关函数),就必须要有完整的定义

(2)为什么不能typedef SL contacts;这么写

答:因为在Contacts.h里面并没有包含SList.h这个头文件,所以无法识别SL是什么,它只认识 int、struct XXX 这类 "基础标识",不认识自定义的别名。

2.2 Contacts.c

本质:底层依赖顺序表的实现逻辑。

csharp 复制代码
#include "SList.h"

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 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.gender,
            cur->data.age,
            cur->data.tel,
            cur->data.addr);
        cur = cur->next;
    }
}

//添加通讯录数据
void AddContact(contact** con) 
{
    peoInfo info;
    printf("请输入姓名:\n");
    scanf("%s", info.name);
    printf("请输入性别:\n");
    scanf("%s", info.gender);
    printf("请输入年龄:\n");
    scanf("%d", &info.age);
    printf("请输入联系电话:\n");
    scanf("%s", info.tel);
    printf("请输入地址:\n");
    scanf("%s", info.addr);

    SLTPushBack(con, info);
    printf("插入成功!\n");

}


//删除通讯录数据
void DelContact(contact** con)
{
    char name[Max_Name];
    printf("请输入要删除的用户姓名:\n");
    scanf("%s", name);

    contact* pos = FindByName(*con, name);
    if (pos == NULL)
    {
        printf("要删除的用户不存在,删除失败!\n");
        return;
    }
    SLTErase(con, pos);
    printf("删除成功!\n");
}

//查找通讯录数据
void FindContact(contact* con) {
    char name[Max_Name];
    printf("请输入要查找的用户姓名:\n");
    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.gender,pos->data.age,pos->data.tel,pos->data.addr);
}

//修改通讯录数据
void ModifyContact(contact** con) 
{
    char name[Max_Name];
    printf("请输入要修改的用户名称:\n");
    scanf("%s", &name);

    contact* pos = FindByName(*con, name);
    if (pos == NULL) 
    {
        printf("要查找的用户不存在,修改失败!\n");
        return;
    }
    printf("请输入要修改的姓名:\n");
    scanf("%s", pos->data.name);
    printf("请输入要修改的性别:\n");
    scanf("%s", pos->data.gender);
    printf("请输入要修改的年龄:\n");
    scanf("%d", &pos->data.age);
    printf("请输入要修改的联系电话:\n");
    scanf("%s", pos->data.tel);
    printf("请输入要修改的地址:\n");
    scanf("%s", pos->data.addr);

    printf("修改成功!\n");
}


//销毁通讯录数据
void DestroyContact(contact** con)
{
    SLTDestory(con);
}

三、底层逻辑

3.1 SeqList.h

csharp 复制代码
#include <stdio.h>
#include <stdlib.h> 
#include <assert.h>
#include "Contacts.h"

typedef peoInfo SLTDataType;

typedef struct SLTNode
{
	SLTDataType data; //数值域 
	struct SLTNode* next; //指针域外
}SLTNode;

void SLTDestory(SLTNode** pphead); //销毁
void SLTPushBack(SLTNode** pphead,SLTDataType x); //尾插
void SLTErase(SLTNode** pphead, SLTNode* pos); //在指定位置删除

3.2 SeqList.c

csharp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1

#include "SList.h"



//节点申请
SLTNode* SLTBuyNode(SLTDataType x)
 {
    SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
    if (newnode == NULL)
	{
		printf("开辟失败!\n");
		exit(-1);
	}
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}

//销毁
void SLTDestory(SLTNode** pphead)
{
	SLTNode* pcur = *pphead;
	while (pcur)
	{
		SLTNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	*pphead = NULL; //手动置空,避免成为野指针
}

//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);
	SLTNode* newnode = SLTBuyNode(x); //申请节点
    if (*pphead == NULL) //链表为空,新节点为首节点
      	*pphead = newnode;
	else
	{
		SLTNode* pcur = *pphead;
		while (pcur->next != NULL) //寻找尾节点
			pcur = pcur->next;

		pcur->next = newnode;
	}
}

//在指定位置删除
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead && pos);
	//头结点
	if (pos == *pphead)
	{
		free(pos);
		*pphead = pos = NULL;
	}
	else
	{
		//寻找该位置前一个节点
		SLTNode* prev = *pphead;
		while (prev->next != pos)
			prev = prev->next;

		prev->next = pos->next;
		free(pos);
		pos = NULL;
	}
}

四、基于顺序表的通讯录实现效果

4.1 添加

4.2 删除

4.3 修改

4.4 查找

4.5 展示

总结与每日励志

✨本文基于单链表实现了通讯录的全功能开发:从头文件的前置声明、结构体设计,到链表尾插 / 删除 / 销毁的底层逻辑,再到添加、修改、查找等业务功能的封装,完整呈现了 "底层数据结构支撑上层业务" 的开发逻辑。C 语言项目开发的核心是模块化与逻辑闭环,每一次指针操作、每一次内存管理,都是对编程思维的打磨。✨ 沉下心吃透数据结构的底层逻辑,你的代码终将兼具功能性与健壮性,在编程之路上稳步前行!

相关推荐
寻寻觅觅☆6 小时前
东华OJ-基础题-106-大整数相加(C++)
开发语言·c++·算法
YJlio7 小时前
1.7 通过 Sysinternals Live 在线运行工具:不下载也能用的“云端工具箱”
c语言·网络·python·数码相机·ios·django·iphone
fpcc7 小时前
并行编程实战——CUDA编程的Parallel Task类型
c++·cuda
偷吃的耗子7 小时前
【CNN算法理解】:三、AlexNet 训练模块(附代码)
深度学习·算法·cnn
l1t7 小时前
在wsl的python 3.14.3容器中使用databend包
开发语言·数据库·python·databend
赶路人儿7 小时前
Jsoniter(java版本)使用介绍
java·开发语言
2013编程爱好者8 小时前
【C++】树的基础
数据结构·二叉树··二叉树的遍历
NEXT068 小时前
二叉搜索树(BST)
前端·数据结构·面试
化学在逃硬闯CS8 小时前
Leetcode1382. 将二叉搜索树变平衡
数据结构·算法
ceclar1238 小时前
C++使用format
开发语言·c++·算法