小伙伴们好啊!
之前写过一个通讯录管理系统,今天,我用另一种方法----双向带头循环链表来实现这个项目,顺便也来复习一下其中的知识点。
希望对大家的课程设计作业有帮助!
项目要求:
用C语言编写一个通讯录程序,要求能够实现对联系人信息的增删查改、排序、清空,将联系人信息保存到文件中,下一次使用时可以读取文件中信息。
直接上代码
contact.h
#define _CRT_SECURE_NO_WARNINGS #pragma once #define NAME_MAX 20//姓名数组最大空间 #define SEX_MAX 4//性别数组最大空间 #define TEL_MAX 14//电话 #define ADDR_MAX 30//地址 #include#include #include #include typedef struct Contact//创建联系人结构体 { char name[NAME_MAX]; char sex[SEX_MAX]; char tele[TEL_MAX]; char addr[ADDR_MAX]; int age; }Contact; typedef struct Peoinfo//创建结点结构体 { Contact contact; struct Peoinfo* prev;//存储下一个结点地址 struct Peoinfo* next;//存储上一个结点地址 }Peoinfo; //对结点初始化 Peoinfo* Init(); //创建新结点 Peoinfo* Create(); //对链表进行尾插 void PushBack(Peoinfo* phead); //退出程序 void Exit(Peoinfo* phead); //增添联系人 void Add(Peoinfo* phead); //删除联系人 void Dele(Peoinfo* phead); //查找联系人 void Search(Peoinfo* phead); //修改联系人信息 void Multify(Peoinfo* phead); //显示所有联系人信息 void Show(Peoinfo* phead); //对联系人按名字首字母排序 void Sort(Peoinfo* phead); //清空通讯录 void Empty(Peoinfo* phead); //保存通讯录内容到文件中 void Save(Peoinfo* phead); //加载文件中信息到通讯录 void Load(Peoinfo* phead); //辅助菜单 void Menu2();
contact.c
#include "contact.h" Peoinfo* Init() { //创建头结点,该结点不存储有效数据,用做哨兵位 Peoinfo* phead = (Peoinfo*)malloc(sizeof(Peoinfo)); //判断空间是否开辟成功 if (phead == NULL) { printf("malloc failed!n"); return; } else { //将头结点的next和prev指向自己,保证双向循环结构 phead->next = phead; phead->prev = phead; return phead; } } Peoinfo* Create() { //为新节点开辟空间 Peoinfo* peo = (Peoinfo*)malloc(sizeof(Peoinfo)); if (peo == NULL) { printf("malloc failed!n"); return; } else { //输入新建联系人的信息 printf("请输入联系人姓名:>"); scanf("%s", peo->contact.name); printf("请输入联系人性别:>"); scanf("%s", peo->contact.sex); printf("请输入联系人电话:>"); scanf("%s", peo->contact.tele); printf("请输入联系人地址:>"); scanf("%s", peo->contact.addr); printf("请输入联系人年龄:>"); scanf("%d", &peo->contact.age); } //先将新结点的next和prev指向自己,避免出现野指针 peo->next = peo; peo->prev = peo; return peo; } void PushBack(Peoinfo* phead) { assert(phead); //先创建结点并输入信息,得到新节点的地址 Peoinfo* peo = Create(); //将新节点尾插到最后,并调整新节点和头结点的next和prev,保证双向循环结构 peo->next = phead; peo->prev = phead->prev; phead->prev->next = peo; phead->prev = peo; } void Exit(Peoinfo* phead) { assert(phead); //释放头结点,并将其置空 free(phead); phead = NULL; printf("退出程序!n"); //退出程序 exit(0); } void Add(Peoinfo* phead) { int n = 0; printf("请输入要增加的联系人的数量:>"); scanf("%d", &n); int i = 0; //创建n个新结点并输入信息 for (i = 0; i < n; i++) { printf("请输入第%d位联系人信息:>n", i + 1); //每创建一个新结点,就将其尾插到链表最后 PushBack(phead); } printf("添加成功!n"); } void Dele(Peoinfo* phead) { assert(phead); if (phead->next == phead) { printf("当前联系人信息为空!n"); return; } else { char name[NAME_MAX]; printf("请输入要删除的联系人姓名:>"); scanf("%s", name); //定义指针,用来记录遍历的结点的位置 Peoinfo* cur = phead->next; while (cur != phead) { if (strcmp(name, cur->contact.name) == 0) { //调整该结点前后两个结点的next和prev,使前后两个结点相连接 cur->next->prev = cur->prev; cur->prev->next = cur->next; //释放该结点 free(cur); cur = NULL; printf("删除成功!n"); return; } cur = cur->next; } printf("删除成功!n"); } } void Search(Peoinfo* phead) { assert(phead); if (phead->next == phead) { printf("当前通讯录为空,请先存储信息!n"); return; } else { char name[NAME_MAX]; printf("请输入要查找的联系人的姓名:>"); scanf("%s", &name); printf("%10s %10s %10s %20s %20sn", "姓名", "性别", "年龄", "电话", "地址"); Peoinfo* cur = phead->next; while (cur != phead) { if (strcmp(name, cur->contact.name) == 0) { printf("%10s %10s %10d %20s %20sn", cur->contact.name, cur->contact.sex, cur->contact.age, cur->contact.tele, cur->contact.addr); return; } cur = cur->next; } printf("该联系人不存在!n"); return; } } void Multify(Peoinfo* phead) { assert(phead); if (phead->next == phead) { printf("当前通讯录为空,请先添加联系人!n"); return; } else { Peoinfo* cur = phead->next; char name[NAME_MAX]; printf("请输入您要修改的联系人的名字:>"); scanf("%s", &name); while (cur != phead) { if (strcmp(name, cur->contact.name) == 0) { int input = 0; printf("该联系人当前信息如下:n"); printf("%10s %10s %10s %20s %20sn", "姓名", "性别", "年龄", "电话", "地址"); printf("%10s %10s %10d %20s %20sn", cur->contact.name, cur->contact.sex, cur->contact.age, cur->contact.tele, cur->contact.addr); do { //利用辅助菜单,改变用户想要改变的信息 Menu2(); printf("请选择要修改的信息:>"); scanf("%d", &input); //利用do while循环和switch语句可完成多次修改 switch (input) { case 1: printf("请输入修改后的姓名:>"); scanf("%s", &cur->contact.name); printf("修改成功!n"); break; case 2: printf("请输入修改后的性别:>"); scanf("%s", &cur->contact.sex); printf("修改成功!n"); break; case 3: printf("请输入修改后的年龄:>"); scanf("%d", &cur->contact.age); printf("修改成功!n"); break; case 4: printf("请输入修改后的电话:>"); scanf("%s", &cur->contact.tele); printf("修改成功!n"); break; case 5: printf("请输入修改后的地址:>"); scanf("%s", &cur->contact.addr); printf("修改成功!n"); break; case 0: printf("退出修改!n"); return; default: printf("选择错误,请重新选择!n"); //若输入错误,自动重新执行该函数 Multify(phead); } } while (input); return; } cur = cur->next; } printf("该联系人不存在!n"); return; } } void Show(Peoinfo* phead) { assert(phead); if (phead->next == phead) { printf("当前联系人数为0,请先存储联系人信息!n"); return; } else { Peoinfo* cur = phead->next; printf("%10s %10s %10s %20s %20sn", "姓名", "性别", "年龄", "电话", "地址"); //遍历链表并打印 while (cur != phead) { printf("%10s %10s %10d %20s %20sn", cur->contact.name, cur->contact.sex, cur->contact.age, cur->contact.tele, cur->contact.addr); cur = cur->next; } } } void Sort(Peoinfo* phead) { assert(phead); if (phead->next == phead) { printf("当前通讯录为空,请先存储联系人信息!n"); return; } else { //从第二个有效结点开始和第一个有效结点比较 Peoinfo* first = phead->next; Peoinfo* cur = phead->next->next; while (cur != phead) { if (strcmp(cur->contact.name, first->contact.name) < 0) { //若后面的结点名字首字母小于第一个有效结点 //则将该结点变为第一个有效结点 //即调整头结点和第一个有效结点,该结点和该结点前后两个结点的next和prev,保证双向循环结构 cur->prev->next = cur->next; cur->next->prev = cur->prev; phead->prev = cur->next; cur->next = first; cur->prev = phead; phead->next = cur; first->prev = cur; } cur = cur->next; } printf("排序成功!n"); } } void Empty(Peoinfo* phead) { assert(phead); if (phead->next == phead) { printf("当前通讯录为空!n"); return; } else { //释放头结点就相当于释放所有结点 free(phead->next); phead->next = phead; phead->prev = phead; printf("清空成功!n"); } } void Save(Peoinfo* phead) { assert(phead); if (phead->next == phead) { printf("当前通讯录为空,请先添加联系人信息!n"); return; } else { //判断文件打开是否成功 FILE* fp = fopen("contact.dat", "w"); if (fp == NULL) { printf("保存失败!n"); return; } else { Peoinfo* cur = phead->next; while (cur != phead) { //从第一个有效结点开始,依次将有效数据存入文件中 fwrite(&cur->contact, sizeof(Peoinfo), 1, fp); cur = cur->next; } printf("保存成功!n"); fclose(fp); fp = NULL; } } } void Load(Peoinfo* phead) { //分别为需要加载的数据创建空间和结点 Peoinfo* head = (Peoinfo*)malloc(sizeof(Peoinfo)); Contact* con = (Contact*)malloc(sizeof(Contact)); Peoinfo* p; Peoinfo* q; p = q = head; FILE* fp = fopen("contact.dat", "r"); if (fp == NULL) { printf("加载失败!n"); return; } while (fscanf(fp, "%s%s%s%s%d", con->name, con->sex, con->tele, con->addr, &con->age) != EOF) { //读取有效信息 q = (Peoinfo*)malloc(sizeof(Peoinfo)); q->contact = *con; p->next = q; p = q; } //将读取到的信息存入结点并进行尾插 p->next = phead; p->prev = phead->prev; phead->prev->next = p; phead->prev = p; printf("加载成功!n"); } void Menu2() { //辅助菜单,在修改函数中被调用 printf("***********************n"); printf("**** 1.姓名 2.性别 ****n"); printf("**** 3.年龄 4.电话 ****n"); printf("**** 5.地址 0.退出 ****n"); printf("***********************n"); }
work.c
#include "contact.h" void Menu() { printf("***********************************n"); printf("****** 0.退出 1.添加 ******n"); printf("****** 2.删除 3.查找 ******n"); printf("****** 4.修改 5.显示 ******n"); printf("****** 6.排序 7.清空 ******n"); printf("****** 8.加载 9.保存 ******n"); printf("***********************************n"); } void Work() { //先进行初始化,创建头结点 Peoinfo* phead = Init(); int choice = 0; //用函数指针数组简化代码,减少代码冗余 void (*p[10])(Peoinfo* phead) = { Exit, Add, Dele, Search, Multify, Show, Sort, Empty, Load, Save }; do { Menu(); printf("请选择:>"); scanf("%d", &choice); if (choice >= 0 && choice <= 9) { //对函数指针进行解引用,调用其指向的函数并传参 (*p[choice])(phead); } else { printf("选择错误,请重新选择!n"); return; } } while (choice); } int main() { Work(); return 0; }
运行结果截图:
由于之前已经解释过项目整体了,这里就不再赘述了,希望对大家有帮助!!!