
🔥小龙报:个人主页
🎬作者简介:C++研发,嵌入式,机器人等方向学习者
❄️个人专栏:《C语言》《【初阶】数据结构与算法》
✨ 永远相信美好的事情即将发生

文章目录
- 前言
- 一、通讯录框架Test.c
- 二、通讯录
-
- [2.1 Contacts.h](#2.1 Contacts.h)
- [2.2 Contacts.c](#2.2 Contacts.c)
- 三、底层逻辑
- [3.1 SeqList.h](#3.1 SeqList.h)
- [3.2 SeqList.c](#3.2 SeqList.c)
- 四、基于顺序表的通讯录实现效果
-
- [4.1 添加](#4.1 添加)
- [4.2 删除](#4.2 删除)
- [4.3 修改](#4.3 修改)
- [4.4 查找](#4.4 查找)
- [4.5 展示](#4.5 展示)
- 总结与每日励志
前言
通讯录是 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 语言项目开发的核心是模块化与逻辑闭环,每一次指针操作、每一次内存管理,都是对编程思维的打磨。✨ 沉下心吃透数据结构的底层逻辑,你的代码终将兼具功能性与健壮性,在编程之路上稳步前行!
