小伙伴们好啊!
之前写过一个通讯录管理系统,今天,我用另一种方法----双向带头循环链表来实现这个项目,顺便也来复习一下其中的知识点。
希望对大家的课程设计作业有帮助!
项目要求:
用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 %20s\n", "姓名", "性别", "年龄", "电话", "地址");
Peoinfo* cur = phead->next;
while (cur != phead)
{
if (strcmp(name, cur->contact.name) == 0)
{
printf("%10s %10s %10d %20s %20s\n", 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 %20s\n", "姓名", "性别", "年龄", "电话", "地址");
printf("%10s %10s %10d %20s %20s\n", 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 %20s\n", "姓名", "性别", "年龄", "电话", "地址");
//遍历链表并打印
while (cur != phead)
{
printf("%10s %10s %10d %20s %20s\n", 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;
}
由于之前已经解释过项目整体了,这里就不再赘述了,希望对大家有帮助!!!