基于单向链表的C语言通讯录实现分析

前言

        在计算机编程中,数据结构的选择对程序的性能和功能实现有着至关重要的影响。今天我们来分析一个基于单向链表实现的C语言通讯录程序。这个程序展示了如何利用链表这种动态数据结构来管理联系人信息,实现了基本的增删改查功能。

目录

前言

正文

1. 整体架构设计

2. 核心数据结构

联系人信息结构

链表节点结构

3. 链表核心功能实现

节点创建

尾插法实现

链表销毁

4. 通讯录业务逻辑

添加联系人

查找功能辅助函数

删除联系人

5. 用户界面设计

总结

优点

可改进之处

扩展建议


正文

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;

经典的链表节点设计,使用typedefpeoInfo定义为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);
}

总结

优点

  1. 架构清晰:模块化设计使得各功能职责明确

  2. 内存管理完善:动态内存分配和释放处理得当

  3. 代码复用性好:查找等功能被多个操作复用

  4. 用户交互友好:操作提示和反馈明确

可改进之处

  1. 输入验证缺失:没有对用户输入进行有效性检查

  2. 错误处理不足:部分边界情况处理不够完善

  3. 功能扩展性:目前仅支持按姓名查找,可以增加更多查找方式

  4. 数据持久化:缺少文件存储功能,数据无法保存

扩展建议

  • 添加文件读写功能,实现数据持久化

  • 增加按电话号码、地址等多条件查询

  • 实现联系人排序功能

  • 添加输入验证和错误恢复机制

这个通讯录程序作为一个教学示例,很好地展示了单向链表在实际情况中的应用,为学习数据结构和C语言编程提供了很好的参考。

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值