目录
单链表实现
通讯录代码实现
初始化
初始化函数
添加
删除
展示
查找
修改
销毁
代码展示
main.c
text.c
text.h
list.c
list.h
和前面的通讯录实现差不多这次就是实现一个以单链表为底层的通讯录
单链表实现
数据结构:单链表-CSDN博客
通讯录代码实现
初始化
//初始化
void InitContact(contact** con) {//初始化函数initialize(con);
}
初始化函数
// 函数:initialize,用于初始化联系人指针数组con,并从文件中读取并导入联系人信息
void initialize(contact** con) {// 打开名为"Contact.txt"的文件以只读二进制模式 ("rb")FILE* fp = fopen("Contact.txt", "rb");// 检查文件是否成功打开,如果文件不存在或无法打开,则输出错误信息并返回if (fp == NULL) {perror("fopen"); // 输出错误信息return;}// 定义一个PeoInfo类型的变量info,用于存储从文件中读取的单个联系人信息PeoInfo info;// 使用while循环逐条读取文件中的联系人信息while (fread(&info, sizeof(info), 1, fp)) { // fread函数从文件中读取指定大小的数据到info变量// 调用Tail_insertion函数将读取到的联系人信息按照尾部插入的方式添加到通讯录(链表)中Tail_insertion(con, info);}// 当所有联系人信息成功插入后,输出提示信息printf("历史记录插入成功!\n");fclose(fp); // 在这里关闭文件
}
尾部插入函数请看我的链表博客
添加
// 函数:AddContact,用于添加新的联系人至通讯录
void AddContact(contact** con) {// 创建一个PeoInfo类型的结构体变量info,用于存储用户输入的新联系人信息PeoInfo info;// 提示用户输入新联系人的姓名,并使用scanf读取字符串printf("请输入添加联系人姓名:\n");scanf("%s", info.name);// 提示用户输入新联系人性别,并使用scanf读取字符串printf("请输入添加联系人性别:\n");scanf("%s", info.sex);// 提示用户输入新联系人年龄,并使用scanf读取整数printf("请输入添加联系人年龄:\n");scanf("%d", &info.age);// 提示用户输入新联系人电话号码,并使用scanf读取字符串printf("请输入添加联系人电话:\n");scanf("%s", info.tel);// 提示用户输入新联系人地址,并使用scanf读取字符串printf("请输入添加联系人地址:\n");scanf("%s", info.addr);// 将新获取的联系人信息通过调用Tail_insertion函数将其按尾部插入的方式添加到通讯录(链表)中Tail_insertion(con, info);// 提示用户添加联系人成功printf("添加成功\n");
}
删除
// 函数:DelContact,用于根据姓名删除联系人
void DelContact(contact** con) {// 定义一个字符数组name,用于存储用户想要删除的联系人姓名char name[NAME_MAX];// 提示用户输入要删除的联系人姓名,并使用scanf读取printf("请输入要删除人的姓名>\n");scanf("%s", name);// 调用查找函数fun_b,传入通讯录头结点的指针以及待查找姓名,寻找对应联系人contact* pos = fun_b(*con, name); // 解引用con,获取通讯录头结点// 如果查找函数返回NULL,说明没有找到该姓名对应的联系人if (pos == NULL) {printf("没找到这个name\n");return;}// 调用SLTErase函数,传入通讯录头结点指针和待删除节点的指针,执行删除操作SLTErase(con, pos);// 删除操作成功后,输出提示信息printf("已经删除此联系人\n");
}
展示
// 函数:ShowContact,用于展示通讯录中的所有联系人信息
void ShowContact(contact* con) {// 设置输出格式,依次为姓名(10个字符宽度)、性别(4个字符宽度)、年龄(4个字符宽度)、电话(15个字符宽度)和地址(20个字符宽度)printf("%-10s %-4s %-4s %15s %-20s\n", "姓名", "性别", "年龄", "电话", "地址");// 初始化指向当前联系人的指针pcur为通讯录头结点contact* pcur = con;// 当pcur不为空时,即遍历整个通讯录链表while (pcur) {// 按照预先设置的格式输出当前联系人的详细信息printf("%-10s %-4s %-4d %15s %-20s\n",pcur->data.name,pcur->data.sex,pcur->data.age,pcur->data.tel,pcur->data.addr);// 遍历完当前节点后,将pcur指向下一个联系人节点pcur = pcur->next;}
}
查找
// 函数:fun_b,用于查找通讯录中指定姓名的联系人节点
contact* fun_b(contact* con, char* p) {// 初始化指针pcur指向通讯录头结点contact* pcur = con;// 循环遍历整个通讯录链表while (pcur) {// 使用strcmp函数比较当前节点联系人的姓名与输入的姓名字符串是否相同if (strcmp(pcur->data.name, p) == 0) {// 若姓名相同,直接返回当前节点的地址return pcur; // 找到了直接返回这个节点的位置}// 若姓名不同,则将pcur指向下一个联系人节点继续比较pcur = pcur->next;}// 若遍历完整个链表都没有找到匹配的姓名,则返回NULLreturn NULL;
}
修改
// 函数:ModifyContact,用于修改通讯录中指定姓名的联系人信息
void ModifyContact(contact** con) {// 定义一个字符数组name,用于存储用户想要修改的联系人姓名char name[NAME_MAX];// 提示用户输入要修改的联系人姓名,并使用scanf读取printf("请输入要修改的人的姓名>\n");scanf("%s", name);// 调用查找函数fun_b,传入通讯录头结点的指针以及待查找姓名,寻找对应联系人contact* pos = fun_b(*con, name); // 解引用con,获取通讯录头结点// 如果查找函数返回NULL,说明没有找到该姓名对应的联系人if (pos == NULL) {printf("没找到这个name\n");return;}// 找到联系人后,提示用户并逐一输入新的联系人信息printf("找到了\n");printf("请输入修改后的联系人姓名:\n");scanf("%s", pos->data.name);printf("请输入修改后的联系人性别:\n");scanf("%s", pos->data.sex);printf("请输入修改后的联系人年龄:\n");scanf("%d", &(pos->data.age));printf("请输入修改后的联系人电话:\n");scanf("%s", pos->data.tel);printf("请输入修改后的联系人地址:\n");scanf("%s", pos->data.addr);// 此处并未显示,但在实际应用中,通常会在输入验证和更新信息后,输出修改成功的提示信息
}// 这段代码的作用是先查找通讯录中指定姓名的联系人,找到后让用户重新输入该联系人的各项信息,从而实现对联系人信息的修改。
销毁
// 函数:SaveContact,用于将通讯录数据保存到文件
void SaveContact(contact* con) {// 打开名为"Contact.txt"的文件以二进制写入模式 ("wb")FILE* pf = fopen("Contact.txt", "wb");// 检查文件是否成功打开,如果文件打开失败,则输出错误信息并返回if (pf == NULL) {perror("fopen");return;}// 遍历通讯录链表,将每个联系人的数据写入文件contact* pcur = con;while (pcur) {// 使用fwrite函数将当前联系人数据写入文件fwrite(&(pcur->data), sizeof(pcur->data), 1, pf);// 移动到下一个联系人节点pcur = pcur->next;}// 数据保存成功后输出提示信息printf("保存成功\n");fclose(pf);
}// 函数:DestroyContact,用于销毁通讯录数据结构并保存数据到文件后再清理内存
void DestroyContact(contact** con) {// 先调用SaveContact函数将当前通讯录数据保存到文件SaveContact(*con);// 调用链表销毁函数SListDesTroy,释放通讯录所占用的内存资源SListDesTroy(con);
}
代码展示
main.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"text.h"#if 0
void text1() {//创建4个节点SLTNode* a1 = (SLTNode*)malloc(sizeof(SLTNode));a1->data = 1;SLTNode* a2 = (SLTNode*)malloc(sizeof(SLTNode));a2->data = 2;SLTNode* a3 = (SLTNode*)malloc(sizeof(SLTNode));a3->data = 3;SLTNode* a4 = (SLTNode*)malloc(sizeof(SLTNode));a4->data = 4;//链接节点a1->next = a2;a2->next = a3;a3->next = a4; a4->next = NULL;SLTNode* ptr = a1;SLTPrint(ptr);
}void text2() {SLTNode* a1 = NULL;Tail_insertion(&a1,1);Tail_insertion(&a1,2);Tail_insertion(&a1,3);Tail_insertion(&a1,4);SLTPrint(&a1);SListDesTroy(&a1);SLTPrint(&a1);/*Head_insertion(&a1,0);SLTPrint(&a1);*//*Tail_delete(&a1);SLTPrint(&a1);*//*Head_delete(&a1);SLTPrint(&a1);*///SLTNode *p = Find(a1, 1);//SLTInsert(&a1, p, 5);//SLTPrint(&a1);/*SLTInsert(&a1, 1, 5);SLTPrint(&a1);*//*SLTInsertAfter(p,6);SLTPrint(&a1);*///if (p == NULL)//{// printf("NO");//}//else//{// printf("YES");//}/*SLTErase(&a1,p);SLTPrint(&a1);*//*SLTEraseAfter(p);SLTPrint(&a1);*/
}
int main() {//text1();text2();return 0;
}
#endif // 0
void eumn() {printf("****************************\n");printf("****1.add 2.DEL 3.FIND****\n");printf("****4.MOD 5.SHOW 6.SORT****\n");printf("****7.SAVE 0.EXIT****\n");printf("****************************\n");
}
int main() {contact* con = NULL;//创建一个为NULL的链表int input = -1;InitContact(&con);//初始化do{eumn();printf("请选择您的操作:\n");scanf("%d", &input);switch (input){case ADD:AddContact(&con);break;case DEL:DelContact(&con);break;case FIND:FindContact(con);break;case MOD:ModifyContact(&con);break;case SHOW:ShowContact(con);break;case SORT:Contacts_sort(con);break;case SAVE:DestroyContact(&con);break;case EXIT:break;default:printf("选择错误,请重新选择\n");break;}} while (input);return 0;
}
text.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"text.h"
#include"list.h"
//开辟内存函数
static SLTNode* SList_ina(SLTDataType x) {//申请空间SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));//判断是否为空if (newnode == NULL){perror("malloc");exit(EXIT_FAILURE);//中止程序}newnode->data = x;//将新节点的内容x赋值newnode->next = NULL;//将指向下一个地址设NULLreturn newnode;//返回节点
}
#if 0
//打印
void SLTPrint(SLTNode** Phead) {assert(Phead);SLTNode* pcur = *Phead;while (pcur){printf("%d->", pcur->data);//打印节点内容pcur = pcur->next;//下一个节点地址赋值(循环)}printf("NULL\n");
}
#endif // 0//尾插
void Tail_insertion(SLTNode** Phead, SLTDataType x){//判断链表是否为空//开辟新的节点SLTNode* newnode = SList_ina(x);if (*Phead == NULL){*Phead = newnode;}else{//用一个指针指向第一个元素SLTNode* pcur = *Phead;while (pcur->next){pcur = pcur->next;}pcur->next = newnode;}
}//头插
void Head_insertion(SLTNode** Phead, SLTDataType x) {//创建新的节点SLTNode* newcode = SList_ina(x);//将新开辟的节点的执政指向第一个节点newcode->next = *Phead;//由于Phead指向的是头部,然后我们在头部插入了一个节点,这个时候Phead指向的就不再是头部,我们直接将新节点的地址赋值给这个指向头部的指针*Phead = newcode;
}//尾删
void Tail_delete(SLTNode** Phead) {assert(Phead && *Phead);//判断是否是一个节点//如果是就直接释放掉//如果不是就找的尾节点,和为节点的上一个节点if ((*Phead)->next == NULL)//->优先级高于*{free(*Phead);*Phead = NULL;}else{//定义两个指针都指向头节点SLTNode* Tail_1 = *Phead;SLTNode* Tail_2 = *Phead;//当Tail_1 == NULL的时候我们的while循环就跳出去了,这样我们的Tail_2就存的尾节点的上一个位置while (Tail_1 ->next){Tail_2 = Tail_1;//存储尾节点的上一个位置Tail_1 = Tail_1->next;//找尾节点}free(Tail_1);Tail_1 = NULL;Tail_2->next = NULL;//要将指向的下一个元素设为NULL}
}//头删
void Head_delete(SLTNode** Phead) {assert(*Phead && Phead);//Phead -- 不能解引用空指针 *Phead -- 指向第一个节点的指针,第一个节点不能为空//存储第二个节点的地址SLTNode* next = (*Phead)->next;//直接释放掉第一个节点free(*Phead);//将我们存储的第二个的地址赋给*Phead(头指针),让他指向第二个节点,让第二个节点变成第二个节点*Phead = next;
}#if 0//查找
SLTNode *Find(SLTNode* Phead, SLTDataType x) {assert(Phead);SLTNode* pcur = Phead;//不改变我们的头,找个小弟帮他走 while (pcur){if (pcur->data == x) {return pcur;}//往后找pcur = pcur->next;}return NULL;
}#endif // 0#if 0//在指定位置之前插⼊数据
void SLTInsert(SLTNode** Phead, SLTNode* pos, SLTDataType x) {assert(Phead && *Phead);SLTNode* pcur = *Phead;//把第一个节点地址传给pcurSLTNode* newnode = SList_ina(x);SLTNode* poss = Find(*Phead,pos);if (*Phead == poss){Head_insertion(Phead,x);}else{//寻找pos前的一个点while (pcur->next != poss){assert(pcur->next);//判断下一个节点不能为空pcur = pcur->next;}//改变节点指针newnode->next = poss;pcur->next = newnode;}
}
#endif // 0//在指定位置之后插⼊数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x) {assert(pos && pos->next);SLTNode* newnode = SList_ina(x);SLTNode* pcur = pos->next;//将下一个节点地址给pcurnewnode->next = pcur;pos->next = newnode;
}//删除pos节点
void SLTErase(SLTNode** Phead, SLTNode* pos) {assert(Phead && *Phead);if (*Phead == pos)//要删除第一个节点{//直接调用头删除Head_delete(Phead);}else{//将第一个节点地址传入pcurSLTNode* pcur = *Phead;while (pcur->next != pos){pcur = pcur->next;}pcur->next = pos->next;//将pos中next指针指向的地址让pcur中next指针指向//释放posfree(pos);pos = NULL;}
}//删除pos之后的节点
void SLTEraseAfter(SLTNode* pos){assert(pos && pos->next);SLTNode* pcur = pos->next;//将第二个节点的地址存在pcurpos->next = pcur->next;//这边pcur->next指向的是第三个节点的地址free(pcur);pcur = NULL;
}//销毁链表
void SListDesTroy(SLTNode** Phead) {//这里的销毁我们需要将每一个节点都销毁掉assert(Phead && *Phead);SLTNode* pcur = *Phead;while (pcur){SLTNode* next = pcur->next;//将下一个位置存储一下free(pcur);pcur = next;}*Phead = NULL;
}
text.h
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
//#include<vld.h>
#include"list.h"typedef struct PersonInfo SLTDataType;
//定义链表节点结构
typedef struct SListNode
{SLTDataType data;//内容struct SListNode* next;//指向下一个节点的指针
}SLTNode;//打印
void SLTPrint(SLTNode** Phead);//尾部插入/头部插入
void Tail_insertion(SLTNode** Phead, SLTDataType x);
void Head_insertion(SLTNode** Phead, SLTDataType x);//尾部删除/头部删除
void Tail_delete(SLTNode** Phead);
void Head_delete(SLTNode** Phead);//查找
SLTNode* Find(SLTNode* Phead, SLTDataType x);//在指定位置之前插⼊数据
void SLTInsert(SLTNode** Phead, SLTNode* pos, SLTDataType x);
//在指定位置之后插⼊数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x);//删除pos节点
void SLTErase(SLTNode** Phead, SLTNode* pos);
//删除pos之后的节点
void SLTEraseAfter(SLTNode* pos);
//销毁链表
void SListDesTroy(SLTNode** Phead);
list.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"text.h"
#include"list.h"
void initialize(contact** con) {//读文件内容FILE* fp = fopen("Contact.txt", "rb");if (fp == NULL) {perror("fopen");return;}//创建一个通讯录结构PeoInfo info;//利用while循环进行导入while (fread(&info,sizeof(info),1,fp)){Tail_insertion(con,info);//尾插}fclose(fp);printf("历史记录插入成功!\n");}//初始化
void InitContact(contact** con) {//初始化函数initialize(con);
}//增加
void AddContact(contact** con){//创建一个通讯录结构PeoInfo info;printf("请输入添加联系人姓名:\n");scanf("%s", &info.name);printf("请输入添加联系人性别:\n");scanf("%s", &info.sex);printf("请输入添加联系人年龄:\n");scanf("%d", &info.age);printf("请输入添加联系人电话:\n");scanf("%s", &info.tel);printf("请输入添加联系人地址:\n");scanf("%s", &info.addr);//进行尾插Tail_insertion(con, info);printf("添加成功\n");
}//展示
void ShowContact(contact* con) {printf("%-10s %-4s %-4s %15s %-20s\n", "姓名", "性别", "年龄", "电话", "地址");contact* pcur = con;while (pcur){printf("%-10s %-4s %-4d %15s %-20s\n",pcur->data.name,pcur->data.sex,pcur->data.age,pcur->data.tel,pcur->data.addr);pcur = pcur->next;//每打印一个节点走向下一个节点}
}//查找函数
contact* fun_b(contact* con,char *p) {contact* pcur = con;while (pcur){if (strcmp(pcur->data.name, p) == 0) {return pcur;//找到了直接返回这个节点的位置}pcur = pcur->next;}return NULL;
}//删除
void DelContact(contact** con) {char name[NAME_MAX];printf("请输入要删除人的姓名>\n");scanf("%s", name);//查找函数contact* pos = fun_b(*con, name);//解引用conif (pos == NULL) {printf("没找到这个name\n");return;}
SLTErase(con, pos);
printf("已经删除此联系人\n");}//查找
void FindContact(contact* con) {char name[NAME_MAX];printf("请输入要查找人的姓名>\n");scanf("%s", name);//查找函数contact* pos = fun_b(con, name);if (pos == NULL){printf("没找到这个name");return;}printf("找到了\n");printf("%-10s %-4s %-4d %15s %-20s\n",pos->data.name,pos->data.sex,pos->data.age,pos->data.tel,pos->data.addr);
}//修改通讯录数据
void ModifyContact(contact** con) {char name[NAME_MAX];printf("请输入要修改的人的姓名>\n");scanf("%s", name);//查找函数contact* pos = fun_b(*con, name);//解引用conif (pos == NULL){printf("没找到这个name");return;}printf("找到了\n");printf("请输入添加联系人姓名:\n");scanf("%s", pos->data.name);printf("请输入添加联系人性别:\n");scanf("%s", pos->data.sex);printf("请输入添加联系人年龄:\n");scanf("%d", &(pos->data.age));printf("请输入添加联系人电话:\n");scanf("%s", pos->data.tel);printf("请输入添加联系人地址:\n");scanf("%s", pos->data.addr);
}int compareMyType_name(const void* a1, const void* a2) {return strcmp(((PeoInfo*)a1)->name, ((PeoInfo*)a2)->name);
}void compareMyType_age(void* a1, void* a2) {return ((PeoInfo*)a1)->age - ((PeoInfo*)a2)->age;
}
//排序//销毁之前的存储
void SaveContact(contact* con) {FILE* pf = fopen("Contact.txt","wb");if (pf == NULL){perror("fopen");return;}//将通讯录数据写⼊⽂件 contact* pcur = con;while (pcur){fwrite(&(pcur->data),sizeof(pcur->data),1,pf);pcur = pcur->next;//一直往后走}printf("保存成功\n");
}
//销毁
void DestroyContact(contact** con) {SaveContact(*con);//存储SListDesTroy(con);//直接调用链表中的销毁
}
list.h
#pragma once#pragma once
#define NAME_MAX 100
#define SEX_MAX 4
#define TEL_MAX 11
#define ADDR_MAX 100enum MyEnum
{EXIT,ADD,//增加DEL,//删除FIND,//查找MOD,//修改SHOW,//展示SORT,//排序SAVE,//保存
};//前置声明
typedef struct SListNode contact;//用户数据
typedef struct PersonInfo
{char name[NAME_MAX];char sex[SEX_MAX];int age;char tel[TEL_MAX];char addr[ADDR_MAX];
}PeoInfo;//初始化通讯录
void InitContact(contact** con);
//添加通讯录数据
void AddContact(contact** con);
//删除通讯录数据
void DelContact(contact** con);
//展示通讯录数据
void ShowContact(contact* con);
//查找通讯录数据
void FindContact(contact* con);
//修改通讯录数据
void ModifyContact(contact** con);
//销毁通讯录数据
void DestroyContact(contact** con);
//排序
//void Contacts_sort(contact* con);