用 C 语言中的顺序表(Sequential List)实现一个简易但功能完整的通讯录管理系统。不仅是一个实用的小项目,更是理解线性数据结构、掌握动态内存管理和结构体操作的绝佳实践。
🎯 学习目标:
- 理解顺序表的基本原理
- 掌握结构体与动态数组的设计方法
- 实现通讯录的增、删、查、改、显等核心功能
一、多文件结构设计
和之前一样,为了使代码结构清晰、易于维护,我们采用三个文件来组织代码:
- contact_0820.h:头文件,用于声明结构体、宏定义和函数原型
- fun_0820.c:源文件,用于实现顺序表的各种操作函数
- contact_0820.c:测试文件,用于编写主函数,测试顺序表的各种功能
二、数据结构设计:两个关键结构体
我们将在头文件 contact_0820.h 中定义两个结构体,分别表示单个联系人信息和整个通讯录管理器。
1. 联系人结构体:Contact
// 定义字段最大长度(便于后期维护)
#define NAME_MAX 50
#define GENDER_MAX 10
#define PHONE_MAX 20
typedef struct {
char name[NAME_MAX]; // 姓名
int age; // 年龄
char gender[GENDER_MAX]; // 性别
char phonenum[PHONE_MAX]; // 电话号码
} Contact;
💡 小贴士:使用宏定义长度,便于统一修改;避免硬编码。
2. 通讯录管理结构体:Contact_List
这个结构体是“笔记本的封面”,负责管理所有联系人。
#define datatype Contact // 类型别名,方便后续扩展为其他类型
typedef struct {
datatype* contacts; // 动态数组,存储联系人数据
int size; // 当前已存储的联系人数量
int capacity; // 数组当前最大容量
} Contact_List;
🔍 关键字段说明:
contacts:指向堆上分配的内存,保存所有联系人size:表示有效数据个数capacity:表示最大容量,用于判断是否需要扩容
三、核心功能实现(contact_0820.c)
接下来我们逐步实现通讯录的各项功能,遵循“初始化 → 增删查改 → 销毁”的生命周期。
1. 初始化通讯录:init_contact_list
首次使用前必须初始化结构体,避免野指针。
void init_contact_list(Contact_List* list) {
list->contacts = NULL; // 初始无内存
list->size = 0; // 当前人数为0
list->capacity = 0; // 容量为0
}
✅ 初始化后即可调用
check_space自动分配空间。
2. 自动扩容机制:check_space
当容量不足时,自动扩展数组大小,保证插入顺利进行。
void check_space(Contact_List* list) {
// 若当前数量 >= 容量,则需要扩容
if (list->size >= list->capacity) {
// 首次分配100个空间,之后每次翻倍
int new_capacity = (list->capacity == 0) ? 100 : list->capacity * 2;
datatype* new_contacts = (datatype*)realloc(list->contacts,
new_capacity * sizeof(datatype));
if (new_contacts == NULL) {
fprintf(stderr, "内存分配失败!程序终止。\n");
exit(EXIT_FAILURE);
}
list->contacts = new_contacts;
list->capacity = new_capacity;
}
}
⚠️ 注意:
realloc可能失败,必须检查返回值!
3. 添加联系人:insert_contact
支持在指定位置插入新联系人(如插队、追加等)。
int insert_contact(Contact_List *list, int location, const char *name, int age, const char *gender, const char *phonenum)
{
check_space(list);
if (location < 0 || location > list->size)
{
fprintf(stderr, "Invalid location\n");
return -1;
}
for (int i = list->size; i > location; i--)
{
list->contacts[i] = list->contacts[i - 1];
}
list->size++;
strcpy_s(list->contacts[location].name, name_max,name);
list->contacts[location].age = age;
strcpy_s(list->contacts[location].gender,gender_max,gender);
strcpy_s(list->contacts[location].phonenum, phone_max,phonenum);
return 0;
}
🔄 插入时间复杂度:O(n),因需移动元素。
4. 显示所有联系人:print_contact_list
遍历并打印所有联系人信息。
void print_contact_list(const Contact_List* list) {
if (list->size == 0) {
printf("通讯录为空,暂无联系人。\n");
return;
}
printf("当前共有 %d 位联系人:\n\n", list->size);
for (int i = 0; i < list->size; i++) {
printf(" 序号: %d:\n", i + 1);
printf(" 姓名: %s\n", list->contacts[i].name);
printf(" 年龄: %d\n", list->contacts[i].age);
printf(" 性别: %s\n", list->contacts[i].gender);
printf(" 电话: %s\n", list->contacts[i].phonenum);
printf("------------------------\n");
}
}
✅ 加入提示语和格式美化,提升用户体验。
5. 删除联系人:delete_contact
删除指定位置的联系人:前移后续元素填补空位。
int delete_contact(Contact_List* list, int loc) {
if (loc < 0 || loc >= list->size) {
fprintf(stderr, "错误:无法删除位置 %d 的联系人(范围 [0, %d))\n", loc, list->size);
return -1;
}
// 从删除位置开始,前面覆盖后面
for (int i = loc; i < list->size - 1; i++) {
list->contacts[i] = list->contacts[i + 1];
}
list->size--; // 数量减1
return 0;
}
🔄 删除时间复杂度同样是 O(n)。
6. 查找联系人:find_contact
按姓名精确查找,返回索引位置。
int find_contact(const Contact_List* list, const char* name) {
for (int i = 0; i < list->size; i++) {
if (strcmp(list->contacts[i].name, name) == 0) {
printf("找到了\n");
printf(" 位置: 第 %d 位\n", i + 1);
printf(". 姓名: %s\n", list->contacts[i].name);
printf(" 年龄: %d\n", list->contacts[i].age);
printf(" 性别: %s\n", list->contacts[i].gender);
printf(" 电话: %s\n", list->contacts[i].phonenum);
return i; // 返回索引
}
}
printf("未找到名为 '%s' 的联系人。\n", name);
return -1;
}
🔎 可扩展为模糊搜索或支持多个字段查询。
7. 销毁通讯录:destroy_contact_list
void destroy_contact_list(Contact_List* list) {
if (list->contacts != NULL) {
free(list->contacts);
list->contacts = NULL;
}
list->size = 0;
list->capacity = 0;
printf("🗑️ 通讯录已销毁,内存释放完成。\n");
}
✅ 必须调用!尤其在长期运行程序中尤为重要。
四、测试驱动:验证功能完整性(test_0820.c)
最后,我们编写主函数来测试所有功能。
#include "contact_0820.h"
int main()
{
Contact_List contact_list;
init_contact_list(&contact_list);
insert_contact(&contact_list, 0, "zhangsan", 3, "male", "111-2222-3333");
insert_contact(&contact_list, 0, "lisi", 5, "male", "111-5555-3333");
insert_contact(&contact_list, contact_list.size, "wangxiaoer", 7, "female", "111-6666-3333");
insert_contact(&contact_list, contact_list.size, "cuihua", 9, "female", "111-2222-8888");
insert_contact(&contact_list, contact_list.size-1, "woshidashabi", 11, "male", "123-2222-8888");
print_contact_list(&contact_list);
printf("+++++++++++++++++++++++\n");
delete_contact(&contact_list, 0);
print_contact_list(&contact_list);
printf("+++++++++++++++++++++++\n");
delete_contact(&contact_list, contact_list.size-1);
print_contact_list(&contact_list);
find_contact(&contact_list, "woshidashabi");
printf("+++++++++++++++++++++++\n");
find_contact(&contact_list, "cuihua");
destroy_contact_list(&contact_list);
return 0;
}
✅ 运行结果
当前共有 5 位联系人:
序号: 1:
姓名: lisi
年龄: 5
性别: male
电话: 111-5555-3333
------------------------
序号: 2:
姓名: zhangsan
年龄: 3
性别: male
电话: 111-2222-3333
------------------------
序号: 3:
姓名: wangxiaoer
年龄: 7
性别: female
电话: 111-6666-3333
------------------------
序号: 4:
姓名: woshidashabi
年龄: 11
性别: male
电话: 123-2222-8888
------------------------
序号: 5:
姓名: cuihua
年龄: 9
性别: female
电话: 111-2222-8888
------------------------
+++++++++++++++++++++++
当前共有 4 位联系人:
序号: 1:
姓名: zhangsan
年龄: 3
性别: male
电话: 111-2222-3333
------------------------
序号: 2:
姓名: wangxiaoer
年龄: 7
性别: female
电话: 111-6666-3333
------------------------
序号: 3:
姓名: woshidashabi
年龄: 11
性别: male
电话: 123-2222-8888
------------------------
序号: 4:
姓名: cuihua
年龄: 9
性别: female
电话: 111-2222-8888
------------------------
+++++++++++++++++++++++
当前共有 3 位联系人:
序号: 1:
姓名: zhangsan
年龄: 3
性别: male
电话: 111-2222-3333
------------------------
序号: 2:
姓名: wangxiaoer
年龄: 7
性别: female
电话: 111-6666-3333
------------------------
序号: 3:
姓名: woshidashabi
年龄: 11
性别: male
电话: 123-2222-8888
------------------------
找到了
位置: 第 3 位
. 姓名: woshidashabi
年龄: 11
性别: male
电话: 123-2222-8888
+++++++++++++++++++++++
未找到名为 'cuihua' 的联系人。
五、总结与思考
通过本次实践,我们完成了以下目标:
✅ 核心收获:
- 掌握了顺序表作为线性结构的基本实现方式
- 学会了使用
realloc进行动态扩容 - 理解了插入/删除操作中元素移动的逻辑
- 实践了 C 语言中结构体 + 指针 + 内存管理的综合应用
❗ 局限性分析:
- 插入/删除效率较低(O(n))
- 频繁扩容可能带来性能开销
- 不支持重复姓名(当前查找只返回第一个)

被折叠的 条评论
为什么被折叠?



