前言
在计算机编程中,数据结构的选择对程序的性能和功能实现有着至关重要的影响。今天我们来分析一个基于单向链表实现的C语言通讯录程序。这个程序展示了如何利用链表这种动态数据结构来管理联系人信息,实现了基本的增删改查功能。
目录
正文
1. 整体架构设计
该通讯录程序采用模块化设计,主要分为三个核心模块:
-
SList:通用单向链表实现
-
contact:通讯录业务逻辑
-
test:用户界面和测试代码
这种分层架构使得代码结构清晰,便于维护和扩展。
2. 核心数据结构
联系人信息结构
typedef struct personInfo
{
char name[NAME_MAX];
char gender[GENDER_MAX];
int age;
char tel[TEL_MAX];
char addr[ADDR_MAX];
}peoInfo;
这个结构体定义了联系人的完整信息,包含姓名、性别、年龄、电话和地址,字段设计合理,涵盖了通讯录的基本需求。
链表节点结构
typedef struct SListNode
{
SLTDataType data;
struct SListNode* next;
}SLTNode;
经典的链表节点设计,使用typedef将peoInfo定义为SLTDataType,使得链表可以通用化。
3. 链表核心功能实现
节点创建
SLTNode* SLTBuyNode(SLTDataType x)
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
if (newnode == NULL)
{
perror("malloc fail!");
exit(1);
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
这段代码实现了节点的动态内存分配,包含必要的错误检查,确保程序在内存不足时能够优雅退出。
尾插法实现
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
assert(pphead);
SLTNode* newnode = SLTBuyNode(x);
if (*pphead == NULL)
{
*pphead = newnode;
}
else
{
SLTNode* ptail = *pphead;
while (ptail->next)
{
ptail = ptail->next;
}
ptail->next = newnode;
}
}
尾插法的实现考虑了空链表和非空链表两种情况,逻辑清晰。不过这里有个小瑕疵:没有对传入的数据进行校验。
链表销毁
void SListDestroy(SLTNode** pphead)
{
assert(pphead && *pphead);
SLTNode* pcur = *pphead;
while (pcur)
{
SLTNode* next = pcur->next;
free(pcur);
pcur = next;
}
*pphead = NULL;
}
内存管理很完善,确保不会出现内存泄漏,这是很多初学者容易忽略的地方。
4. 通讯录业务逻辑
添加联系人
void ContactAdd(contact** con)
{
peoInfo info;
printf("请输入要添加的联系人的姓名:\n");
scanf("%s", info.name);
// ... 其他字段输入
SLTPushBack(con, info);
printf("插入成功!\n");
}
用户交互设计友好,但缺乏输入验证,在实际使用中可能会因为非法输入导致问题。
查找功能辅助函数
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;
}
这个函数被多个操作复用(删除、查找、修改),体现了良好的代码复用思想。使用strcmp进行字符串比较是正确的做法。
删除联系人
void ContactDel(contact** con)
{
char name[NAME_MAX];
printf("请输入要删除的联系人姓名:\n");
scanf("%s", name);
contact* pos = FindByName(*con, name);
if (pos == NULL)
{
printf("要删除的联系人数据不存在\n");
return;
}
SLTErase(con, pos);
printf("删除成功!\n");
}
先查找后删除的模式确保了操作的安全性,用户反馈也很明确。
5. 用户界面设计
void menu()
{
printf("******************************************\n");
printf("********* 通讯录 *********\n");
printf("********1.添加联系人 2.删除联系人******\n");
printf("********3.修改联系人 4.查找联系人******\n");
printf("********5.展示联系人 0.退出通讯录******\n");
printf("******************************************\n");
}
菜单设计简洁明了,功能划分清晰。主循环使用switch-case结构,是控制台程序的典型做法。
测试代码提供了完整的用户交互流程:
void SListTest02()
{
contact* con = NULL;
int n = 0;
do
{
menu();
printf("请输入你的选择:\n");
scanf("%d", &n);
switch (n)
{
case 1:
ContactAdd(&con);
break;
// ... 其他case
case 0:
printf("退出通讯录!\n");
break;
default:
printf("输入错误,请重新输入:\n");
break;
}
} while (n);
ContactDesTroy(&con);
}
总结
优点
-
架构清晰:模块化设计使得各功能职责明确
-
内存管理完善:动态内存分配和释放处理得当
-
代码复用性好:查找等功能被多个操作复用
-
用户交互友好:操作提示和反馈明确
可改进之处
-
输入验证缺失:没有对用户输入进行有效性检查
-
错误处理不足:部分边界情况处理不够完善
-
功能扩展性:目前仅支持按姓名查找,可以增加更多查找方式
-
数据持久化:缺少文件存储功能,数据无法保存
扩展建议
-
添加文件读写功能,实现数据持久化
-
增加按电话号码、地址等多条件查询
-
实现联系人排序功能
-
添加输入验证和错误恢复机制
这个通讯录程序作为一个教学示例,很好地展示了单向链表在实际情况中的应用,为学习数据结构和C语言编程提供了很好的参考。
974





