C语言实现动态通讯录:结构体、动态扩容与排序详解
利用C语言的动态内存管理与结构体,可以构建一个包含增删改查、置顶及多种排序功能的高效通讯录程序。
结果与目标
本教程旨在展示如何模块化地编写一个具有动态空间开辟能力的通讯录系统。该系统支持以下核心功能:
- 基础维护:添加、删除、修改联系人的基本信息(姓名、性别、年龄、电话、住址)。
- 信息查询:精确查找联系人并按格式化表格打印。
- 特色功能:支持联系人置顶显示,以及按性别、年龄或电话号码进行自定义排序。
- 内存管理:使用
malloc初始化和realloc动态扩容,确保程序高效运行且不发生内存泄漏。
准备条件
在开始编码前,需要配置开发环境并明确数据结构定义。由于涉及到Windows平台下的清屏操作,建议使用Visual Studio或其他支持Windows.h头文件的环境进行编译。
第1步:引入必要头文件与宏定义
目的是建立统一的全局常量与函数接口声明,便于后续模块调用。主要涉及输入输出、内存分配、字符串比较及断言检查。
代码实现包括预处理指令屏蔽警告、定义容量常量、枚举类型以增强代码可读性,以及核心结构体的定义。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Windows.h>
#include <assert.h>
// 常量定义:初始人数、每次扩容增量、各字段最大长度
#define INIT_NUM_PEOPLE 10
#define ONCE_TIME 5
#define MAX_NAME 10
#define MAX_SEX 4
#define MAX_TELE 11
#define MAX_ADD 40
// 功能枚举:用英文缩写代替数字,提升代码可读性
enum MyEnum {
MENU = 1,
ADD,
DEL,
PRIN,
FIND,
CHANGE,
FIRST,
SORT,
EXIT
};
// 联系人结构体:新增first字段用于标记是否置顶
typedef struct People {
char name[MAX_NAME];
char sex[MAX_SEX];
int age;
char tele[MAX_TELE];
char add[MAX_ADD];
int first; // 0: 未置顶, 1: 已置顶
} People;
// 通讯录结构体:维护指针、当前人数及总容量
typedef struct Contact {
People* peoples; // 动态数组指针
int sz; // 当前实际存储人数
int all; // 当前分配的总容量
} Contact;
第2步:实现通讯录初始化
使用init_con函数为通讯录分配初始内存。此步骤是避免空指针访问的第一步,同时设置计数器归零。
void init_con(Contact* con) {
assert(con); // 断言指针有效
// 分配初始空间
con->peoples = (People*)malloc(INIT_NUM_PEOPLE * sizeof(People));
if (con->peoples == NULL) {
perror("warning::function::9"); // 错误处理
return;
}
memset(con->peoples, 0, INIT_NUM_PEOPLE * sizeof(People)); // 清零内存
con->sz = 0;
con->all = INIT_NUM_PEOPLE;
}
真实操作顺序:功能函数逻辑
第3步:实现动态扩容与添加联系人
在添加数据前,必须先检查当前容量是否已满。如果已满,需使用realloc扩大空间,防止内存溢出。
void add_people(Contact* con) {
assert(con);
system("cls");
// 检查并扩容
if (con->sz == con->all) {
People* ptr = (People*)realloc(con->peoples,
(con->all + ONCE_TIME) * sizeof(People));
if (ptr == NULL) {
perror("worning::function::40");
return;
}
con->peoples = ptr;
ptr = NULL;
// 对新增加的空间进行清零处理
memset(con->peoples + con->sz, 0, ONCE_TIME * sizeof(People));
con->all += ONCE_TIME;
printf("\n扩容成功\n");
}
// 录入信息
printf("请输入姓名:"); scanf("%s", con->peoples[con->sz].name);
printf("请输入性别:"); scanf("%s", con->peoples[con->sz].sex);
printf("请输入年龄:"); scanf("%d", &con->peoples[con->sz].age);
printf("请输入电话:"); scanf("%s", con->peoples[con->sz].tele);
printf("请输入住址:"); scanf("%s", con->peoples[con->sz].add);
con->sz++; // 人数加一
system("cls");
printf("添加成功!\n");
}
第4步:实现通用查找与删除逻辑
删除与修改均依赖精确的姓名匹配。通过遍历数组返回下标索引;若找不到则返回当前总数sz作为无效标志。
注意:删除后需将后续元素向前移动一位以保持数组连续。
// 通用查找函数
int find(Contact* con, const char* name) {
for (int i = 0; i < con->sz; i++) {
if (strcmp(name, con->peoples[i].name) == 0)
return i;
}
return con->sz; // 未找到返回sz
}
// 删除函数示例
void del_people(Contact* con) {
assert(con);
if (con->sz == 0) { printf("通讯录为空\n"); return; }
char h[10]; printf("请输入要删除的姓名:"); scanf("%s", h);
int flag = find(con, h);
if (flag == con->sz) { printf("未找到联系人\n"); return; }
// 覆盖法前移元素
for (int i = flag; i < con->sz - 1; i++) {
People tmp = con->peoples[i];
con->peoples[i] = con->peoples[i + 1];
con->peoples[i + 1] = tmp;
}
con->sz--;
printf("删除成功!\n");
}
第5步:实现排序与置顶功能
利用标准库qsort实现多条件排序。排序函数需传入比较函数的指针,分别针对性别(逆序排列)、年龄(升序)和电话进行对比。
// qsort比较函数示例
int sex_cmp(const void* str1, const void* str2) {
People* a = (People*)str1;
People* b = (People*)str2;
return -strcmp(a->sex, b->sex); // 负数表示降序(女男)
}
void sort_people(Contact* con) {
// 用户选择排序维度...
// qsort(con->peoples, con->sz, sizeof(People), sex_cmp);
}
完成判断
在主程序中,通过switch-case语句根据用户输入的枚举值调用对应函数。循环结束后,务必释放申请的动态内存:free(con.peoples)并将指针置为NULL。
简要总结
上述即为C语言动态通讯录实现的详细内容,更多关于C语言动态通讯录的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













