C++利用单向链表实现通讯录管理系统完整代码

林舟
林舟
发布时间:2026-10-15 | 4人浏览过
原创

介绍使用C++和Windows API构建基于链表的通讯录管理系统的完整方案,包含节点定义、动态内存分配及基础CRUD操作。

项目概述与设计思路

通讯录管理的核心在于数据的持久化存储(在运行期间)与快速检索。本方案采用C++作为开发语言,利用单向链表(Singly Linked List)作为底层数据结构。相比数组,链表无需预先分配固定大小的内存,能够根据新增联系人数量动态扩展存储空间。

该系统的目标是在Windows环境下运行,支持以下核心业务逻辑:

  • 存储结构:使用 struct 定义包含姓名、电话、生日、地址及录入时间的信息块,并封装为链表节点。
  • 数据处理:实现增(Insert)、删(Delete)、改(Update)、查(Search)四种基本操作。
  • 特殊功能:集成Windows API GetLocalTime 自动记录联系人添加时的系统日期。

第1步:定义基础数据结构与类型

首先引入必要的IO流、字符串处理及系统API头文件。为了保持代码清晰,我们将联系人的基本信息抽象为一个独立的 A 结构体,并将链表节点定义为 LNode。

#include <iostream>
#include <string>
#include <malloc.h> // 用于动态内存分配 new
#include <windows.h> // 调用 Windows API 获取系统时间
#include <sstream> // 用于数据类型转换 using std::stringstream

using namespace std;
#define OK 1
#define ERROR 0

typedef int Status;

// 1. 定义基础联系人员结构体
typedef struct info {
    string number;       // 电话号码
    string date;         // 插入日期
    string name;         // 姓名
    string adress;       // 地址
    string birthday;     // 生日
} A;

// 2. 定义链表节点结构体
typedef struct LNode {
    A data;              // 数据域
    struct LNode *next;  // 指针域
} LNode, *LinkList;

第2步:初始化链表

在使用链表前必须对其进行初始化,即创建一个不存有效数据的头节点(Head Node),并将其指针域设为NULL。

Status InitList(LinkList &L) {
    L = new LNode;       // 为头节点分配内存
    L->next = NULL;      // 初始状态为空表
    return OK;
}

第3步:实现前插法插入节点(新建联系人)

系统需要接收用户输入的各字段信息。考虑到Windows系统返回的时间数据类型为浮点型或其他数值型,这里先提供一个辅助函数将数字转换为字符串。

// 辅助函数:double转string
string doubleToString(double num) {
    stringstream ss;
    string str;
    ss << num;
    ss >> str;
    return str;
}

// 在链表头部插入新节点
Status ListInsert(LinkList &L, string name, string adress, string birthday, string date, string number) {
    LinkList p = new LNode;
    p->data.name = name;
    p->data.adress = adress;
    p->data.date = date;
    p->data.birthday = birthday;
    p->data.number = number;

    // 前插法:新节点指向原首元节点,头结点指向新节点
    p->next = L->next;
    L->next = p;
    return OK;
}

注意:插入时需要校验手机号长度是否为11位,并检查姓名是否已存在于系统中以防止重复。

第4步:多条件查询逻辑设计

为了提高程序的灵活性,查询函数接受一个标识符 i 和搜索内容 e。例如当 i=1 时按姓名查,i=5 时按电话查。这是一种典型的复用代码模式,避免了写五个相似的搜索函数。

LinkList SearchElemChar(LinkList L, int i, string e) {
    while (L != NULL) {
        bool match = false;
        if (i == 1 && L->data.name == e) match = true;
        else if (i == 2 && L->data.birthday == e) match = true;
        else if (i == 3 && L->data.adress == e) match = true;
        else if (i == 4 && L->data.date == e) match = true;
        else if (i == 5 && L->data.number == e) match = true;

        if (match) {
            return L; // 找到则返回当前节点指针
        } else {
            L = L->next;
        }
    }
    cout << "未查到数据!" << endl;
    return NULL;
}

第5步:删除与遍历显示

删除操作是链表中最易出错的部分。由于单链表只能向后指,要删除节点D,必须先拿到其前驱节点P(前插法的逻辑中需要注意)。此处通过 SearchElemBefore 寻找目标节点的前一个位置进行unlink。

// 查找目标值的前驱节点(用于删除)
LinkList SearchElemBefore(LinkList L, string e) {
    LinkList P = L; 
    while (L->next != NULL) {
        if (L->next->data.name == e) {
            return P; // 找到了前一个节点
        }
        L = L->next;
    }
    return NULL;
}

void foreachelem(LinkList L) {
    if (L->next == NULL) {
        cout << "通讯录里还没有联系人!" << endl;
        return;
    }
    while (L->next != NULL) {
        L = L->next;
        cout << "--- 联系人信息 ---" << endl;
        cout << "姓名: " << L->data.name << endl;
        cout << "电话: " << L->data.number << endl;
        cout << "地址: " << L->data.adress << endl;
        cout << "生日: " << L->data.birthday << endl;
        cout << "录入日: " << L->data.date << endl;
    }
}

第6步:主界面交互与系统流程集成

主函数中通过循环等待指令输入,并调用上述封装好的函数。利用 system("cls") 实现清屏刷新界面效果。

int ShowMenu() {
    int a;
    system("cls");
    cout << "========== 通讯录管理系统 ==========" << endl;
    cout << "-----请根据功能输入对应的序号--------" <<endl;
    cout << "-----------1.新建联系人--------------" <<endl;
    cout << "--------2.查看所有联系人-------------" <<endl;
    cout << "--------3.修改选定联系人-------------" <<endl;
    cout << "--------4.查询选定联系人-------------" <<endl;
    cout << "--------5.删除选定联系人-------------" <<endl;
    cout << "------------6.退出系统---------------" <<endl;
    cout << "请输入序号并按回车进入:" ;
    cin >> a;
    return a;
}

int main() {
    int choice;
    LinkList L;
    InitList(L);

    while (choice != 6) {
        choice = ShowMenu();
        switch (choice) {
            case 1: { // 新建
                string name, addr, birth, phone;
                cout << "请输入姓名:"; cin >> name;
                // ... 省略其他输入 ...
                SYSTEMTIME sys; 
                GetLocalTime( &sys );
                string today = to_string(sys.wYear) + "-" + to_string(sys.wMonth); // 简化示例
                ListInsert(L, name, addr, birth, today, phone);
                break;
            }
            case 2: foreachelem(L); break;
            // case 3, 4, 5 逻辑同前述章节
            default: cout << "无效选择!" << endl;
        }
        system("pause");
    }
    return 0;
}

关键技术点说明

  1. 内存安全:在 ListInsert 和 Delete 操作中,频繁涉及 new 分配。在实际工程级应用中,应在退出程序前递归释放所有节点内存,否则会导致内存泄漏(Memory Leak)。
  2. API兼容性:使用了 <windows.h> 中的 GetLocalTime,这意味着该代码仅能在Windows环境下编译通过。跨平台移植需替换为标准C库的时间函数。
  3. 健壮性:在进行修改或查询前,必须判断返回值是否非空,避免对 NULL 指针解引用导致段错误(Segmentation Fault)。

以上就是C++使用链表存储实现通讯录功能管理的详细内容,更多关于C++数据结构教程的资料请关注本站其它相关文章!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表