📚 前言:通讯录是C语言数据结构应用的经典案例,而顺序表作为线性表的基础存储结构,凭借随机访问效率高的特点,非常适合实现小型通讯录。本文将从需求分析、结构设计、功能实现到实战测试,完整讲解基于顺序表的通讯录开发过程,适合C语言初学者和数据结构入门者参考。
一、需求分析:通讯录需要实现哪些功能?📋
一个基础的通讯录应满足日常联系人管理需求,核心功能包括:
✅ 新增联系人:录入姓名、电话、地址等信息
✅ 查找联系人:按姓名/电话快速检索
✅ 修改联系人:更新已有联系人的信息
✅ 删除联系人:移除不需要的联系人
✅ 显示所有联系人:遍历展示通讯录内容
✅ 清空通讯录:快速删除所有数据
二、核心设计:顺序表如何适配通讯录?🔧
顺序表的本质是用数组存储数据,结合通讯录的需求,我们需要设计两个核心结构:
1. 联系人信息结构(Contact)
存储单个联系人的详细信息,可根据需求扩展字段:
#define MAX_NAME 20 // 姓名最大长度 #define MAX_PHONE 15 // 电话最大长度(含区号) #define MAX_ADDR 50 // 地址最大长度 // 联系人信息结构体 typedef struct { char name[MAX_NAME]; // 姓名 char phone[MAX_PHONE]; // 电话 char addr[MAX_ADDR]; // 地址 int age; // 年龄(可选扩展) } Contact;
2. 顺序表通讯录结构(SeqListAddrBook)
管理联系人数据的顺序表,包含数据数组、当前长度和最大容量:
#define MAX_SIZE 100 // 通讯录最大容量(可调整) // 顺序表通讯录结构体 typedef struct { Contact data[MAX_SIZE]; // 存储联系人的数组 int length; // 当前通讯录中的联系人数量 } SeqListAddrBook;
三、功能实现:逐步搭建通讯录核心逻辑 🛠️
接下来按功能模块实现,每个模块对应一个函数,逻辑清晰易维护。
1. 初始化通讯录
将顺序表长度初始化为0,确保后续操作的安全性:
// 初始化通讯录 void InitAddrBook(SeqListAddrBook *ab) { if (ab == NULL) return; // 避免空指针 ab->length = 0; // 初始长度为0,无联系人 printf("📌 通讯录初始化成功!\n"); }
2. 新增联系人
先判断通讯录是否已满,未满则录入联系人信息并更新长度:
// 新增联系人 void AddContact(SeqListAddrBook *ab) { if (ab == NULL) return; // 判断通讯录是否已满 if (ab->length >= MAX_SIZE) { printf("❌ 通讯录已满,无法新增联系人!\n"); return; } // 录入联系人信息 Contact newContact; printf("请输入联系人姓名:"); scanf("%s", newContact.name); printf("请输入联系人电话:"); scanf("%s", newContact.phone); printf("请输入联系人地址:"); getchar(); // 吸收缓冲区的换行符 fgets(newContact.addr, MAX_ADDR, stdin); // 去除fgets读取的换行符 newContact.addr[strcspn(newContact.addr, "\n")] = '\0'; printf("请输入联系人年龄:"); scanf("%d", &newContact.age); // 存入顺序表 ab->data[ab->length] = newContact; ab->length++; printf("✅ 联系人新增成功!\n"); }
3. 查找联系人(按姓名)
遍历顺序表,对比姓名实现查找,找到后返回索引(未找到返回-1):
// 按姓名查找联系人,返回索引(-1表示未找到) int FindContactByName(SeqListAddrBook *ab, char *name) { if (ab == NULL || name == NULL) return -1; // 遍历顺序表查找 for (int i = 0; i < ab->length; i++) { if (strcmp(ab->data[i].name, name) == 0) { return i; // 找到,返回索引 } } return -1; // 未找到 } // 显示查找结果 void ShowFindResult(SeqListAddrBook *ab, int index) { if (index == -1) { printf("❌ 未找到该联系人!\n"); return; } printf("📋 查找结果:\n"); printf("姓名:%s\n", ab->data[index].name); printf("电话:%s\n", ab->data[index].phone); printf("地址:%s\n", ab->data[index].addr); printf("年龄:%d\n", ab->data[index].age); }
4. 修改联系人
先通过查找获取联系人索引,再更新对应字段的信息:
// 修改联系人信息 void ModifyContact(SeqListAddrBook *ab) { if (ab == NULL) return; char name[MAX_NAME]; printf("请输入要修改的联系人姓名:"); scanf("%s", name); int index = FindContactByName(ab, name); if (index == -1) { printf("❌ 未找到该联系人!\n"); return; } // 重新录入信息(可优化为选择修改单个字段) printf("请输入新的姓名:"); scanf("%s", ab->data[index].name); printf("请输入新的电话:"); scanf("%s", ab->data[index].phone); printf("请输入新的地址:"); getchar(); fgets(ab->data[index].addr, MAX_ADDR, stdin); ab->data[index].addr[strcspn(ab->data[index].addr, "\n")] = '\0'; printf("请输入新的年龄:"); scanf("%d", &ab->data[index].age); printf("✅ 联系人信息修改成功!\n"); }
5. 删除联系人
找到联系人后,通过“后续元素前移”的方式覆盖待删除元素,实现删除效果:
// 删除联系人 void DeleteContact(SeqListAddrBook *ab) { if (ab == NULL) return; char name[MAX_NAME]; printf("请输入要删除的联系人姓名:"); scanf("%s", name); int index = FindContactByName(ab, name); if (index == -1) { printf("❌ 未找到该联系人!\n"); return; } // 后续元素前移,覆盖待删除元素 for (int i = index; i < ab->length - 1; i++) { ab->data[i] = ab->data[i + 1]; } ab->length--; // 长度减1 printf("✅ 联系人删除成功!\n"); }
6. 显示所有联系人
遍历顺序表,依次输出所有联系人信息,无数据时给出提示:
// 显示所有联系人 void ShowAllContacts(SeqListAddrBook *ab) { if (ab == NULL) return; if (ab->length == 0) { printf("📭 通讯录为空,暂无联系人!\n"); return; } printf("📋 通讯录所有联系人(共%d人):\n", ab->length); printf("----------------------------------------\n"); for (int i = 0; i < ab->length; i++) { printf("第%d人:\n", i + 1); printf("姓名:%s\n", ab->data[i].name); printf("电话:%s\n", ab->data[i].phone); printf("地址:%s\n", ab->data[i].addr); printf("年龄:%d\n", ab->data[i].age); printf("----------------------------------------\n"); } }
7. 清空通讯录
直接将顺序表长度置为0,无需清空数组(后续操作会覆盖):
// 清空通讯录 void ClearAddrBook(SeqListAddrBook *ab) { if (ab == NULL) return; ab->length = 0; // 长度置0,相当于清空所有数据 printf("✅ 通讯录清空成功!\n"); }
四、实战测试:设计主菜单交互 🖥️
通过主菜单让用户选择功能,实现完整的交互流程:
#include <stdio.h> #include <string.h> // 此处粘贴上述所有结构体定义和函数声明 int main() { SeqListAddrBook ab; InitAddrBook(&ab); int choice; while (1) { // 循环菜单 printf("\n======= 顺序表通讯录 =======\n"); printf("1. 新增联系人\n"); printf("2. 查找联系人(按姓名)\n"); printf("3. 修改联系人\n"); printf("4. 删除联系人\n"); printf("5. 显示所有联系人\n"); printf("6. 清空通讯录\n"); printf("0. 退出程序\n"); printf("===========================\n"); printf("请输入你的选择:"); scanf("%d", &choice); switch (choice) { case 1: AddContact(&ab); break; case 2: { char name[MAX_NAME]; printf("请输入要查找的联系人姓名:"); scanf("%s", name); int index = FindContactByName(&ab, name); ShowFindResult(&ab, index); break; } case 3: ModifyContact(&ab); break; case 4: DeleteContact(&ab); break; case 5: ShowAllContacts(&ab); break; case 6: ClearAddrBook(&ab); break; case 0: printf("👋 程序退出,感谢使用!\n"); return 0; default: printf("❌ 输入错误,请重新选择!\n"); break; } } return 0; }
五、注意事项与优化方向 ⚠️
📌 顺序表的局限性:最大容量固定(MAX_SIZE),满了无法扩容,可优化为动态顺序表(用malloc/realloc动态分配内存)。
📌 查找效率:按姓名查找是线性查找,时间复杂度O(n),可通过排序+二分查找优化(需先实现通讯录排序功能)。
📌 数据持久化:当前程序退出后数据丢失,可添加文件操作(fwrite/fread),将数据保存到本地文件。
📌 输入校验:目前未对输入数据合法性校验(如电话长度、年龄范围),实际开发中需补充。
六、总结 📝
本文通过顺序表实现了通讯录的核心功能,从结构体设计到函数实现,再到主菜单交互,完整覆盖了小型项目的开发流程。顺序表的“数组存储”特性让通讯录的随机访问更高效,但也存在容量固定的缺陷,适合入门学习数据结构的实际应用。
如果需要进一步优化,可以尝试实现动态顺序表、数据持久化或更高效的查找算法,欢迎大家在评论区交流讨论!
#C语言 #数据结构 #顺序表 #通讯录实现 #C语言实战项目