当前位置: 首页 > news >正文

小白学习顺序表 之 通讯录实现

#前言

在日常编程学习中,实现一个联系人管理系统。

它不仅能让我们熟悉结构体、动态内存分配、文件操作等知识点,还能帮助我们更好地理解程序的模块化设计。

一、整体架构

这个联系人管理系统主要由以下几个文件组成:

        contactTest.cpp:主函数所在文件,负责程序的入口和菜单的显示,以及用户操作的分发。

        SeqList.h:定义了顺序表的结构和相关操作的函数声明。

        contact.h:定义了联系人的信息结构和联系人管理的相关函数声明。

        SeqList.cpp:实现了顺序表的各种操作,如初始化、插入、删除等。

        contact.cpp:实现了联系人的添加、删除、修改、查找等具体功能,以及联系人信息的文件读写操作。

二、核心数据结构

1. 联系人信息结构体(Info

typedef struct SLcontact {char name[NAME_MAX];int age;char gender[SEX_MAX];char phone[TEL_MAX];char addr[ADDR_MAX];
} Info;

这个结构体用于存储联系人的基本信息,包括姓名、年龄、性别、电话和地址。

2. 顺序表结构体(SL

typedef struct SeqList
{SLDataType* a;int size;      // 元素个数int capacity;  // 容量
} SL;

顺序表是一种线性数据结构,这里用它来存储联系人信息。

a 是一个指向 SLDataType 类型的指针,用于动态分配内存;

size 表示当前顺序表中元素的个数;capacity 表示顺序表的容量。

三、主要操作及原理

1. 顺序表初始化(SLInit

void SLInit(SL* ps) {ps->a = NULL;ps->capacity = ps->size = 0;
}

这个函数将顺序表的指针 a 置为 NULL,并将容量和元素个数都初始化为 0。

2. 顺序表扩容(SLdepend

void SLdepend(SL* ps) {if (ps->capacity == ps->size) {int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;SLDataType* ret = (SLDataType*)realloc(ps->a, newcapacity * sizeof(SLDataType));if (ret == NULL) {perror("realloc");exit(1);}ps->a = ret;ps->capacity = newcapacity;}
}

当顺序表的容量和元素个数相等时,需要进行扩容。

这里采用的是每次扩容为原来的 2 倍。

使用 realloc 函数重新分配内存,如果分配失败,会输出错误信息并退出程序。

3. 顺序表尾部插入(SLPushBack

void SLPushBack(SL* ps, SLDataType x) {assert(ps);SLdepend(ps);ps->a[ps->size++] = x;
}

在顺序表的尾部插入一个元素。首先使用 assert 函数确保指针 ps 不为 NULL,然后调用 SLdepend 函数检查是否需要扩容,最后将元素插入到顺序表的尾部,并将元素个数加 1。

4. 顺序表头部插入(SLPushFront

void SLPushFront(SL* ps, SLDataType x) {assert(ps);SLdepend(ps);for (int i = ps->size; i > 0; i--) {ps->a[i] = ps->a[i - 1];}ps->a[0] = x;ps->size++;
}

在顺序表的头部插入一个元素。同样先确保指针不为 NULL 并检查是否需要扩容,然后将顺序表中的所有元素依次向后移动一位,最后将元素插入到头部,并将元素个数加 1。

例如,原始顺序表:[A, B, C]
步骤 1:i=3,a [3] = a [2] → [A, B, C, C]
步骤 2:i=2,a [2] = a [1] → [A, B, B, C]
步骤 3:i=1,a [1] = a [0] → [A, A, B, C]
插入元素 X 后:[X, A, B, C]

5. 顺序表尾部删除(SLPopBack

void SLPopBack(SL* ps) {assert(ps);assert(ps->size > 0);ps->size--;  // 逻辑删除,只减少元素个数
}

删除顺序表的尾部元素。

使用 assert 函数确保指针不为 NULL 且顺序表中至少有一个元素,然后将元素个数减 1。

6. 顺序表头部删除(SLPopFront

void SLPopFront(SL* ps) {assert(ps);assert(ps->size > 0);// 将后面的元素依次向前移动一位for (int i = 0; i < ps->size - 1; i++) {ps->a[i] = ps->a[i + 1];}ps->size--;  // 减少元素个数
}

删除顺序表的头部元素。

先确保指针不为 NULL 且顺序表中至少有一个元素,然后将顺序表中的所有元素依次向前移动一位,最后将元素个数减 1。

例如,原始顺序表:[A, B, C]
步骤 1:i=0,a [0] = a [1] → [B, B, C]
步骤 2:i=1,a [1] = a [2] → [B, C, C]
删除后:[B, C]

7. 联系人添加(Addcontact

void Addcontact(Cont* pcon) {Info info;printf("请输入联系人的姓名:");scanf_s("%s", info.name, (unsigned)_countof(info.name));printf("请输入联系人的年龄:");scanf_s("%d", &info.age);printf("请输入联系人的性别:");scanf_s("%s", info.gender, (unsigned)_countof(info.gender));printf("请输入联系人的电话:");scanf_s("%s", info.phone, (unsigned)_countof(info.phone));printf("请输入联系人的地址:");scanf_s("%s", info.addr, (unsigned)_countof(info.addr));SLPushBack(pcon, info);
}

该函数用于添加联系人信息。

首先创建一个 Info 类型的变量 info,然后通过 scanf_s 函数从用户输入中获取联系人的姓名、年龄、性别、电话和地址,

最后调用 SLPushBack 函数将联系人信息插入到顺序表的尾部。

8. 联系人删除(Delcontact

void Delcontact(Cont* pcon) {printf("请输入要删除的联系人的姓名:\n");char name[NAME_MAX];scanf_s("%s", name, (unsigned)_countof(name)); int findIndex = FindByName(pcon, name);if (findIndex < 0) {printf("要删除的联系人不存在!\n");return;  }SLErase(pcon, findIndex);printf("联系人删除成功!\n");
}

该函数用于删除指定姓名的联系人。

首先获取用户输入的联系人姓名,然后调用 FindByName 函数查找该联系人在顺序表中的位置,

如果找不到则输出提示信息并返回;

如果找到,则调用 SLErase 函数删除该联系人,并输出删除成功的提示信息。

9. 联系人修改(Modifycontact

void Modifycontact(Cont* pcon) {char name[NAME_MAX];printf("请输入要修改的联系人的姓名:\n");scanf_s("%s", name);int findIndex = FindByName(pcon, name);if (findIndex < 0) {printf("要修改的联系人不存在!\n");return;}printf("请输入新的姓名:\n");scanf_s("%s", pcon->a[findIndex].name);printf("请输入新的年龄:\n");scanf_s("%d", &pcon->a[findIndex].age);printf("请输入新的性别:\n");scanf_s("%s", pcon->a[findIndex].gender);printf("请输入新的电话:\n");scanf_s("%s", pcon->a[findIndex].phone);printf("请输入新的地址:\n");scanf_s("%s", pcon->a[findIndex].addr);printf("联系人修改成功!\n");
}

该函数用于修改指定姓名的联系人信息。

首先获取用户输入的联系人姓名,然后调用 FindByName 函数查找该联系人在顺序表中的位置,

如果找不到则输出提示信息并返回;

如果找到,则通过 scanf_s 函数获取用户输入的新信息,并更新该联系人的信息,最后输出修改成功的提示信息。

10. 联系人查找(Findcontact

void Findcontact(Cont* pcon) {char name[NAME_MAX];printf("请输入要查找的用户的姓名:\n");scanf_s("%s", name);int findIndex = FindByName(pcon, name);if (findIndex < 0) {printf("该联系人不存在!\n");return;}// 找到了,打印一下找到的联系人的信息printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "电话", "地址");printf("%s %s %d %s %s\n",pcon->a[findIndex].name,pcon->a[findIndex].gender,pcon->a[findIndex].age,pcon->a[findIndex].phone,pcon->a[findIndex].addr);
}

该函数用于查找指定姓名的联系人信息。

首先获取用户输入的联系人姓名,然后调用 FindByName 函数查找该联系人在顺序表中的位置,

如果找不到则输出提示信息并返回;如果找到,则输出该联系人的信息。

11. 联系人信息保存到文件(SaveContactToFile

void SaveContactToFile(Cont* pcon, const char* filename) {FILE* file = fopen(filename, "wb");  // 以二进制写模式打开文件if (file == NULL) {perror("无法打开文件");return;}// 写入联系人数量fwrite(&pcon->size, sizeof(int), 1, file);// 写入每个联系人for (int i = 0; i < pcon->size; i++) {fwrite(&pcon->a[i], sizeof(Info), 1, file);}fclose(file);
}

该函数用于将联系人信息保存到文件中。

首先以二进制写模式打开文件,如果打开失败则输出错误信息并返回;

然后写入联系人的数量,再依次写入每个联系人的信息;最后关闭文件。

12. 从文件中加载联系人信息(LoadContactFromFile

void LoadContactFromFile(Cont* pcon, const char* filename) {FILE* file = fopen(filename, "rb");  // 以二进制读模式打开文件if (file == NULL) {printf("文件不存在,初始化通讯录\n");return;}// 销毁原有数据SLDestroy(pcon);SLInit(pcon);// 读取联系人数量int count;fread(&count, sizeof(int), 1, file);// 读取每个联系人for (int i = 0; i < count; i++) {Info info;fread(&info, sizeof(Info), 1, file);SLPushBack(pcon, info);}fclose(file);
}

该函数用于从文件中加载联系人信息。

首先以二进制读模式打开文件,如果文件不存在则输出提示信息并返回;

然后销毁原有数据并重新初始化顺序表,接着读取联系人的数量,再依次读取每个联系人的信息并插入到顺序表中;最后关闭文件。

四、注意事项

动态内存管理:在使用 realloc 函数进行内存扩容时,要注意检查内存分配是否成功,避免出现内存泄漏。

文件操作:在进行文件读写操作时,要确保文件能够正常打开和关闭,避免数据丢失。

输入验证:在获取用户输入时,要考虑输入的合法性,避免出现程序崩溃或数据错误。

函数调用:在调用函数时,要确保传递的参数类型和数量正确,避免出现编译错误或运行时错误。

五、总结知识点

结构体:使用结构体来组织和存储联系人的信息,方便对联系人进行管理。

顺序表:顺序表是一种线性数据结构,通过动态内存分配实现了可变长度的存储。

动态内存管理:使用 mallocrealloc 和 free 函数进行动态内存的分配和释放。

文件操作:使用 fopenfreadfwrite 和 fclose 函数进行文件的读写操作。

模块化设计:将不同的功能封装成不同的函数,提高了代码的可读性和可维护性。

相关文章:

  • 《算法导论(第4版)》阅读笔记:p115-p126
  • 【GPU并行计算】不同设备上的GPU性能分析
  • 移动安全Android——ROOT检测绕过
  • 大模型技术生态全景解析:从基础组件到AGI的演进之路
  • Python基础知识(IO编程)
  • PCB设计实践(二十六)贴片电容与插件电容的全面解析:差异、演进与应用场景
  • 霍尼韦尔HMR2300-D00-485数字模块
  • PPP 流程已经走到启动阶段并且成功进入了 “STAGE_START_PPP
  • 使用js 写一个函数 将base64 转换成file
  • STM32 SPI通信(硬件)
  • FM信号、900M蜂窝信号,2.6G蜂窝EMC设计要点
  • 字符串和常量池的进一步研究
  • 周界安全防护新突破:AI智能分析网关V4周界入侵检测算法的技术应用
  • 利用 DeepSeek 和摩笔马良设计一张海报
  • 从Android开发聊技术
  • Unitree 5. GO1 3D打印配件
  • VsCode配置
  • 学习日记-day14-5.23
  • 机会成本与沉没成本:如何做出理性经济决策
  • questions and answers_1
  • 网站专题页面案例/seo服务收费
  • 个人网站设计图片/seo搜索优化专员
  • 手机如何访问电脑做的asp网站/全网营销推广靠谱吗
  • 做网站的流程视频/个人做seo怎么赚钱
  • 网站加入悬浮客服/seo优化有哪些
  • 如何用WordPress建小说站/cps推广接单平台