0


单链表、顺序表实操小项目---通讯录

通讯录

🐬前言

学了数据的存储结构,是时候该拿出来遛一遛了~
收获:将进一步加深对单链表,顺序表的理解。也可以以这个为模板去设计,图书管理系统,学生信息管理系统等,当然啦这些都是比较粗糙的、简陋的。

我们可以在日后学了更多的知识,再将其改造升级,我想这是一件比较有意义的事情,因为它见证了你的成长不是吗?

🍑通讯录

不管是实现任何项目,请记住一句话,思想大于实现

🍉通讯录的逻辑

实现的逻辑要正确,才能开始下手。
这样设计对吗(单链表的设计)?

typedefstructPeopleInfo{char name[20];int age;char tele[12];char address[30];}People;typedefstructContact{
    People contacts;//联系人int lenth;//通讯录的长度structContact* next;//指针域}Contact;

错的,上面实现的是每个通讯录中,存放一个联系人,这样的通讯录存在多个。(顺序表设计的时候也要注意到这点)尽管这样写也是可以实现,但仍然是一个失败品。

typedefstructPeopleInfo{char name[20];int age;char tele[12];char address[30];structPeople* next;//指向下一个联系人}People;typedefstructContact{int lenth;//通讯录的长度structPeople* Con;//指针域}Contact;

通讯录的正确逻辑是一个通讯录中存多个联系人。也可以添加一些通讯录的属性,比如通讯录的中当前存放联系人的个数和存放最大的联系人的个数。

🍉通讯录存储结构的选择

实现逻辑定下来了,该选取存储的结构了

不管选取哪种结构的,数据最终都是在内存中存储的,最后程序退出的时候,这些内容都不会存在了。

如果你想将你的数据持久化,学一些文件操作即可。将这里的数据都存储进文件中。但是嘞,正儿八经的工作中都是用数据库的。

🍊顺序表

🍎数组

想到多个联系人,一般都可以想到通过数组的形式来存储数据,数组本质上是一种顺序存储(地址连续)。

不过这种设计方式,存在一个弊端,大小被固定死了,如果把数组一开始的空间开辟的足够大,那又太浪费空间了。
这样设计出来的可以称为静态版本的通讯录

🍎在堆区开辟的空间

还存在哪些顺序存储的结构呢?

这里将涉及到动态内存的部分知识。
以下设计出来的通讯录为动态版本的通讯录

在堆区开辟的多个联系人的空间,这块空间根据联系人结构体的大小而定。
比如说刚开始设计通讯录的大小为3个人,那就先开辟三个联系人结构体大小的空间,这三块空间的地址是连续的。
当存满三个,再去扩容。如何扩容呢,与等下介绍的函数

realloc

有关

🍎柔性数组

可能有人没有听过这个概念(在c99中引入),什么是柔性数组呢?
柔性数组必须和结构体连用,并且柔性数组必须作为结构体的最后一个成员,它[]中写0或者不写。结构体中除了柔性数组外至少还得有一个成员。

例如:

structS{int a;char data[];//char data[0]; };

这就是一个柔性数组。柔性数组虽然是在这个结构体中,但是求结构体的大小

sizeof(struct S)

时,柔性数组的大小是不会算进去的。

怎么分配空间呢?也是通过动态内存函数去分配空间。
一般先把结构体的空间分配出来,再分配柔性数组的空间。一般这样写

structS* s =(structS*)malloc(sizeof(structS)+sizeof(char)*10)
sizeof(struct S)

是为结构体分配的空间,

sizeof(char)*10

就是为柔性数组分配的空间。
在存储的时候它们地址也是连续的。

简单了解了下这些概念,接下来介绍三个函数

realloc,malloc,free

。下文会使用。

realloc,malloc

都是在堆区中开辟空间,

free

是释放在堆区开辟的空间。

malloc

在这里插入图片描述

返回值是在堆区开辟空间的起始地址。因为地址类型是

void*

,所以需要根据具体情况去强制类型转换成一样的地址类型。
形参为需要开辟空间的大小

realloc

在这里插入图片描述

对原来已经在堆区开辟的空间,进行增加或者减少。
第一个形参是起始地址(原先在堆区开辟空间的起始地址)
第二个形参,扩容后或减少后的总大小。比如原先有10字节的空间,扩容2个字节,第二个形参就为12。
返回值也是堆区开辟空间的起始地址。

使用

realloc

进行扩容或者缩减时,原有的数据是不会改变的。

realloc

进行扩容,原有空间的后面如果没有足够大的空间进行扩容,那会在堆区重新开辟一块空间(空间是扩容后的),同样的原有的数据不会改变,但地址发生改变!

free

在这里插入图片描述

只能释放在堆区的空间,参数为在堆区开辟空间的起始地址

🍊单链表

单链表实现的通讯录就没有必要扩容,增加一个联系人,开辟一个节点,非常节约空间。

实现项目的时候,代码比较多采用多文件的形式。

🍑单链表实现通讯录

基于之前一篇博客单链表的基本操作和一些复杂操作来实现通讯录的。这里的任何单链表中的操作都在那篇博客中,所以只会介绍实现逻辑以及注意事项。

🍒进入页面设计

这里的使用了贪吃蛇博客中介绍的任意位置输出。

在这里插入图片描述

代码效果,看起来比较有那味了😄

通讯录

开始的时候有个字符向中间汇聚,这个实现也比较简单,准备两个数组,左下标和右下标,交换,打印。
页面在添加一些通讯录的操作。。

voidOpertion(){gotoxy(32,3);printf("Opertion:");gotoxy(42,4);printf("0.Exit     1.Add      2.Delete  3.Search");gotoxy(42,6);printf("4.Modify   5.Order    6.Show    7.Clear");}voidOpenContact(){char arr[]="Welcome back to  Contact";char arr1[]="                          ";int left =0;//左下标int right =strlen(arr)-1;//右下标while(left < right){
        arr1[left]= arr[left];
        arr1[right]= arr[right];gotoxy(44,2);printf("%s", arr1);Sleep(1);
        left++;
        right--;}Opertion();printf("\n");}
gotoxy(44, 2);

每次打印都将光标移到(44,2)这个位置。所以就不需要清屏的操作,每次都从(44,2)打印,原有数据会被覆盖。打印速度太快没看到效果的话,加就加个

Sleep(1);

停屏操作。

🍒通讯录属性的设计

enumOp{
    Exit,
    Add,
    Delete,
    Search,
    Modify,
    Order,
    Show,
    Clear,};typedefstructPeopleInfo{char name[20];int age;char tele[12];char address[30];structPeopleInfo* next;}People;typedefstructContact{int lenth;//通讯录的长度
    People* next;//当作头指针}Contact;extern Contact* Con;

项目中使用一些有特定含义的常量,并且这些常量有内在的联系,推荐使用枚举,不用宏。

对于上面两个结构体要理解,这和单链表操作中的不同,这两个是不同类型的结构体,一不小心很容易出错。

所以在写的时候,我会向熟悉的转换,也就是向单链表操作中那样使用,这样写起来会简单许多。可能这样说不太理解,看下面的图片。

在这里插入图片描述

这样设计,写来还是比较轻松的。浪费一个节点,浪费的节点充当头节点。

🍒主函数的设计

cHead可以设成全局变量,在调试的时候,可以观察到添加删除等操作之后,节点之间是否连接起来。

//Contact* cHead = (Contact*)malloc(sizeof(Contact));intmain(){int input =0;
    Contact* cHead =(Contact*)malloc(sizeof(Contact));if(NULL== cHead){perror("AollocPeopleInfo");return1;}memset(cHead,0,sizeof(Contact));

    cHead->next =AollocPeopleInfo();
    People* pEnd = cHead->next;if(NULL== cHead->next){perror("AollocPeopleInfo");return1;}OpenContact();do{Input();gotoxy(32,8);scanf("%d",&input);gotoxy(32,8);printf("  ");switch(input){case Exit :printf("通讯录关闭");break;case Add :AddContact(&pEnd);
            cHead->lenth++;break;case Delete :DelContact(cHead,&pEnd);
            cHead->lenth--;break;case Search:SerContact(cHead);break;case Modify:ModContact(cHead);break;case Order:OrderContact(cHead);break;case Show:ShowContact(cHead);break;case Clear:ClearContact(cHead);break;default:Error();break;}gotoxy(32,3);printf("   现有联系人:%d ", cHead->lenth);}while(input);free(cHead->data);free(cHead);
    cHead =NULL;gotoxy(32,12);return0;}

🍒AddContact

这里采用的是尾插法添加。

memset

这个函数是用来对数据进行初始化。在这篇博客内存函数、字符串函数中有介绍,也提到了其他的常用的函数。

People*AollocPeopleInfo(){
    People* p =(People*)malloc(sizeof(People));//初始化memset(p,0,sizeof(People));return p;}//添加信息voidAddInput(){gotoxy(32,7);printf("请分别输入姓名 年龄 电话 地址的信息\n");}//清理添加痕迹voidCleanAdd(){gotoxy(32,7);printf("                                           ");gotoxy(32,8);printf("                                           ");gotoxy(32,9);printf("                     ");}//添加voidAddContact(People** end){
    People* p =AollocPeopleInfo();AddInput();gotoxy(32,8);scanf("%s%d%s%s", p->name,&p->age,
        p->tele, p->address);gotoxy(32,9);printf("添加成功");Sleep(50);CleanAdd();(*end)->next = p;*end = p;}

🍒ShowContact

这里的

i

作用是用来清理所有打印出来的结果。

//清理展示的效果voidCleanShow(int i){while(i >0){gotoxy(32,7+ i);printf("                                                                    ");
        i--;}}//展示voidShowContact(Contact* head){
    People* p = head->next->next;//指向第一个节点staticint i =1;gotoxy(32,7);printf("%-20s\t%-3s\t%-12s\t%-30s\t","姓名","年龄","电话","地址");if(0== head->lenth){gotoxy(32,8);printf("通讯录为空");Sleep(500);CleanShow(i);return;}while(p){gotoxy(32,7+ i);printf("%-20s\t%-3d\t%-12s\t%-30s\t", p->name,
            p->age, p->tele, p->address);
        p = p->next;
        i++;}Sleep(3000);CleanShow(i);
    i =1;}

🍒DelContact

删除联系人,也可以根据人名进行删除,这里图了个简单。不管是哪种,都需要两个指针,一个去找位置,一个是记录前驱。

根据人名删除:字符串的比较需要用到

strcmp()

,删除人的名字需要用字符数组存起来
在循环中遍历,每一次都要与删除人的名字比较。找到了,前驱指向下个节点,在释放删除节点。

voidDelContact(Contact* head){gotoxy(32,7);printf("请输入删除哪一个联系人                     ");int x =0;gotoxy(32,8);scanf("%d",&x);gotoxy(32,8);printf("     ");if(x <1|| x > head->lenth){gotoxy(32,7);printf("位置错误,退回界面                ");Sleep(500);return;}
    People* p = head->next->next;//第一个节点开始 找删除位置
    People* p1 = head->next;//记录删除位置的前驱while(x -1){
        p = p->next;
        p1 = p1->next;
        x--;}
    p1->next = p->next;free(p);DelSet();}

真能这样写吗?这样写,会有bug,挺难发现的,要结合前后的操作去理解。
假设删除最后一个联系人,再去添加新联系人,会成功(链表能连接)吗?

不会,为什么呢?添加联系人我们使用的是尾插法。注意在删除最后一个联系人的时候,尾指针指向的是这个删除的节点,指向没有改变。也就是说下一次添加新联系人时,尾指针仍然指向的是这个删除的节点。

在这里插入图片描述

正确的是这样的

在这里插入图片描述

所以需要改进

DelContact(cHead,&pEnd);
voidDelContact(Contact* head,People** end){gotoxy(32,7);printf("请输入删除哪一个联系人                     ");int x =0;gotoxy(32,8);scanf("%d",&x);gotoxy(32,8);printf("     ");if(x <1|| x > head->lenth){gotoxy(32,7);printf("位置错误,退回界面                ");Sleep(500);return;}
    People* p = head->next->next;//第一个节点开始 找删除位置
    People* p1 = head->next;//记录删除位置的前驱int flag =0;if(x == head->lenth)
        flag =1;while(x -1){
        p = p->next;
        p1 = p1->next;
        x--;}
    p1->next = p->next;if(flag){*end = p1;}free(p);DelSet();}

🍒SerContact

查找联系人。
同样也可以根据人名查找。
这里不要删除,只用一个指针即可,找到了打印就完事了。

//查找展示和清理voidShowCleanSear(People* p){//先清除原来的痕迹gotoxy(32,7);printf("                                                                    ");gotoxy(32,7);printf("%-20s\t%-3s\t%-12s\t%-30s\t","姓名","年龄","电话","地址");gotoxy(32,8);printf("%-20s\t%-3d\t%-12s\t%-30s\t", p->name,
        p->age, p->tele, p->address);Sleep(2000);gotoxy(32,7);printf("                                                                    ");gotoxy(32,8);printf("                                                                    ");}voidSerContact(Contact* head){gotoxy(32,7);printf("请输入查找哪一个联系人                     ");int x =0;gotoxy(32,8);scanf("%d",&x);gotoxy(32,8);printf("     ");if(x <0|| x > head->lenth){gotoxy(32,7);printf("位置错误,退回界面                ");Sleep(500);return;}
    People* p = head->next;//从头节点开始 找 查找位置while(x){
        p = p->next;
        x--;}ShowCleanSear(p);//展示找的记录,并清理痕迹}

🍒ModContact

对联系人信息进行修改。
先得找才能修改,基于上面找的操作加以改进就可以了。

//清楚修改的痕迹voidCleanMod(){int i =0;while(i <3){gotoxy(32,7+ i);printf("                                                                ");
        i++;}}//修改voidModContact(Contact* head){gotoxy(32,7);printf("请输入要修改哪一个联系人                     ");int x =0;gotoxy(32,8);scanf("%d",&x);gotoxy(32,8);printf("      ");if(x <0|| x > head->lenth){gotoxy(32,7);printf("位置错误,退回操作界面                ");Sleep(500);return;}

    People* p = head->next;//找 查找位置while(x){
        p = p->next;
        x--;}gotoxy(32,7);printf("请输入修改信息                                  ");gotoxy(32,8);printf("%-20s\t%-3s\t%-12s\t%-30s\t","姓名","年龄","电话","地址");gotoxy(32,9);scanf("%s%d%s%s", p->name,&p->age,
        p->tele, p->address);Sleep(1000);CleanMod();//清楚痕迹gotoxy(32,7);printf("修改成功");Sleep(500);CleanMod();//清楚痕迹}

🍒OrderContact

这个应该是第二难的吧,第一难的我觉得是刚刚那个bug,那个一不注意真难发现。

这里的排序和单链表基本操作和一些复杂操作博客中的略有不同,但大同小异,那篇博客中的稍微复杂,这里稍微简单。

这里是对节点中的任意成员排序,那篇博客中是对节点中的结构体的任意成员排序

这里的排序是由qsort的介绍,以及模拟实现发散而来。

单链表与顺序表不同,地址的不连续,参数的设计也有所不同。

在单链表基本操作和一些复杂操作的博客中解释了,这里就不浪费笔墨了。

intint_cmp(constvoid* e1,constvoid* e2){return((People*)e1)->age -((People*)e2)->age;}intname_cmp(constvoid* e1,constvoid* e2){returnstrcmp(((People*)e1)->name,((People*)e2)->name);}//排序voidInsertSort(Contact* head,int(*cmp)(constvoid*,constvoid*)){
    People* t1 = head->next->next;//插入节点的前驱
    People* t = t1->next;//插入节点
    People* p =NULL;//p1的前驱
    People* p1 =NULL;//遍历插入节点前面所有的节点int flag =1;while(t){
        flag =1;
        People* p = head->next;//p1的前驱
        People* p1 = head->next->next;//遍历插入节点前面所有的节点while(p1 != t && flag){if(cmp(p1, t)>0){
                People* t3 = t;
                t = t->next;//指向下个节点
                t1->next = t;//连接

                p->next = t3;
                t3->next = p1;
                flag =0;}
            p = p->next;
            p1 = p1->next;}if(flag){
            t = t->next;
            t1 = t1->next;}}}voidOrderSet(){gotoxy(32,7);printf("排序成功                                                      ");gotoxy(32,8);printf("             ");gotoxy(32,9);printf("               ");Sleep(500);gotoxy(32,7);printf("                                                   ");}voidBeginSet(){gotoxy(32,7);printf("1.对年龄排序                                                  ");gotoxy(32,8);printf("2.对姓名排序");gotoxy(32,9);printf("请选择:");}voidOrderContact(Contact* head){BeginSet();int b =0;scanf("%d",&b);int i =1;int flag =1;while(flag){switch(b){case1:InsertSort(head, int_cmp);
            flag =0;break;case2:InsertSort(head, name_cmp);
            flag =0;break;default:gotoxy(32,9+ i++);printf("选择错误");break;}}OrderSet();}

🍒ClearContact

清空数据的时候不能忘记把属性置为0
清空数据很容易,只要把堆区开辟的空间释放掉即可,但是这样写了以后,我们得考虑到主函数中也存在

free(head->next)

,同一块空间不可释放2次否则会报错的。因此需要调整。

局部代码

int flag =1;//标记ClearContact中是否释放空间过。//switch中case Clear:ClearContact(cHead);
            flag =0;if(flag)free(cHead->next);
voidClearContact(Contact* head){free(head->next);
    head->lenth =0;gotoxy(32,7);printf("通讯录已清空               ");Sleep(1000);gotoxy(32,7);printf("              ");}

💥完成代码

因为用到了任意位置的输出(它头文件包含c++的内容),所以得用.cpp的后缀名文件。

💢Contact.h

#pragmaonce#include<stdio.h>#include<windows.h>#include<iostream>#include<conio.h>#include<string.h>#include<stdlib.h>#pragmawarning(disable:4996)enumOp{
    Exit,
    Add,
    Delete,
    Search,
    Modify,
    Order,
    Show,
    Clear,};typedefstructPeopleInfo{char name[20];int age;char tele[12];char address[30];structPeopleInfo* next;}People;typedefstructContact{int lenth;//通讯录的长度
    People* next;//当作头指针}Contact;//extern Contact* Con; 用全局变量的时候申请一下//函数声明externvoidgotoxy(int x,int y);extern People*AollocPeopleInfo();externvoidAddContact(People** end);externvoidDelContact(Contact* head, People** end);externvoidSerContact(Contact* head);externvoidModContact(Contact* head);externvoidOrderContact(Contact* head);externvoidShowContact(Contact* head);externvoidClearContact(Contact* head);

💢Contact.cpp

#include"Contact.h"//开辟联系人(节点)并初始化
People*AollocPeopleInfo(){
    People* p =(People*)malloc(sizeof(People));//初始化memset(p,0,sizeof(People));return p;}//添加信息voidAddInput(){gotoxy(32,7);printf("请分别输入姓名 年龄 电话 地址的信息\n");}//清理添加痕迹voidCleanAdd(){gotoxy(32,7);printf("                                           ");gotoxy(32,8);printf("                                           ");gotoxy(32,9);printf("                     ");}//清理展示的效果voidCleanShow(int i){while(i >0){gotoxy(32,7+ i);printf("                                                                                ");
        i--;}}//展示voidShowContact(Contact* head){
    People* p = head->next->next;//指向第一个节点staticint i =1;gotoxy(32,7);printf("%-20s\t%-3s\t%-12s\t%-30s\t","姓名","年龄","电话","地址");if(0== head->lenth){gotoxy(32,8);printf("通讯录为空");Sleep(500);CleanShow(i);return;}while(p){gotoxy(32,7+ i);printf("%-20s\t%-3d\t%-12s\t%-30s\t", p->name,
            p->age, p->tele, p->address);
        p = p->next;
        i++;}Sleep(3000);CleanShow(i);
    i =1;}//添加voidAddContact(People** end){
    People* p =AollocPeopleInfo();AddInput();gotoxy(32,8);scanf("%s%d%s%s", p->name,&p->age,
        p->tele, p->address);gotoxy(32,9);printf("添加成功");Sleep(50);CleanAdd();(*end)->next = p;*end = p;}//删除成功的设置voidDelSet(){gotoxy(32,9);printf("删除成功");Sleep(500);gotoxy(32,9);printf("        ");}//删除voidDelContact(Contact* head,People** end){gotoxy(32,7);printf("请输入删除哪一个联系人                     ");int x =0;gotoxy(32,8);scanf("%d",&x);gotoxy(32,8);printf("     ");if(x <1|| x > head->lenth){gotoxy(32,7);printf("位置错误,退回界面                ");Sleep(500);return;}
    People* p = head->next->next;//第一个节点开始 找删除位置
    People* p1 = head->next;//记录删除位置的前驱int flag =0;if(x == head->lenth)
        flag =1;while(x -1){
        p = p->next;
        p1 = p1->next;
        x--;}
    p1->next = p->next;if(flag){*end = p1;}free(p);DelSet();}//查找展示和清理voidShowCleanSear(People* p){//先清除原来的痕迹gotoxy(32,7);printf("                                                                    ");gotoxy(32,7);printf("%-20s\t%-3s\t%-12s\t%-30s\t","姓名","年龄","电话","地址");gotoxy(32,8);printf("%-20s\t%-3d\t%-12s\t%-30s\t", p->name,
        p->age, p->tele, p->address);Sleep(2000);gotoxy(32,7);printf("                                                                    ");gotoxy(32,8);printf("                                                                    ");}//查找voidSerContact(Contact* head){gotoxy(32,7);printf("请输入查找哪一个联系人                     ");int x =0;gotoxy(32,8);scanf("%d",&x);gotoxy(32,8);printf("     ");if(x <0|| x > head->lenth){gotoxy(32,7);printf("位置错误,退回界面                ");Sleep(500);return;}
    People* p = head->next;//从头节点开始 找 查找位置while(x){
        p = p->next;
        x--;}ShowCleanSear(p);//展示找的记录,并清理痕迹}//清楚修改的痕迹voidCleanMod(){int i =0;while(i <3){gotoxy(32,7+ i);printf("                                                                ");
        i++;}}//修改voidModContact(Contact* head){gotoxy(32,7);printf("请输入要修改哪一个联系人                     ");int x =0;gotoxy(32,8);scanf("%d",&x);gotoxy(32,8);printf("      ");if(x <0|| x > head->lenth){gotoxy(32,7);printf("位置错误,退回操作界面                ");Sleep(500);return;}

    People* p = head->next;//找 查找位置while(x){
        p = p->next;
        x--;}gotoxy(32,7);printf("请输入修改信息                                  ");gotoxy(32,8);printf("%-20s\t%-3s\t%-12s\t%-30s\t","姓名","年龄","电话","地址");gotoxy(32,9);scanf("%s%d%s%s", p->name,&p->age,
        p->tele, p->address);Sleep(1000);CleanMod();//清楚痕迹gotoxy(32,7);printf("修改成功");Sleep(500);CleanMod();//清楚痕迹}intint_cmp(constvoid* e1,constvoid* e2){return((People*)e1)->age -((People*)e2)->age;}intname_cmp(constvoid* e1,constvoid* e2){returnstrcmp(((People*)e1)->name,((People*)e2)->name);}//排序voidInsertSort(Contact* head,int(*cmp)(constvoid*,constvoid*)){
    People* t1 = head->next->next;//插入节点的前驱
    People* t = t1->next;//插入节点
    People* p =NULL;//p1的前驱
    People* p1 =NULL;//遍历插入节点前面所有的节点int flag =1;while(t){
        flag =1;
        People* p = head->next;//p1的前驱
        People* p1 = head->next->next;//遍历插入节点前面所有的节点while(p1 != t && flag){if(cmp(p1, t)>0){
                People* t3 = t;
                t = t->next;//指向下个节点
                t1->next = t;//连接

                p->next = t3;
                t3->next = p1;
                flag =0;}
            p = p->next;
            p1 = p1->next;}if(flag){
            t = t->next;
            t1 = t1->next;}}}voidOrderSet(){gotoxy(32,7);printf("排序成功                                                      ");gotoxy(32,8);printf("             ");gotoxy(32,9);printf("               ");Sleep(500);gotoxy(32,7);printf("                                                   ");}voidBeginSet(){gotoxy(32,7);printf("1.对年龄排序                                                  ");gotoxy(32,8);printf("2.对姓名排序");gotoxy(32,9);printf("请选择:");}voidOrderContact(Contact* head){BeginSet();int b =0;scanf("%d",&b);int i =1;int flag =1;while(flag){switch(b){case1:InsertSort(head, int_cmp);
            flag =0;break;case2:InsertSort(head, name_cmp);
            flag =0;break;default:gotoxy(32,9+ i++);printf("选择错误");break;}}OrderSet();}voidClearContact(Contact* head){free(head->next);
    head->lenth =0;gotoxy(32,7);printf("通讯录已清空               ");Sleep(1000);gotoxy(32,7);printf("              ");}

💢main.cpp

#include"Contact.h"voidgotoxy(int x,int y){
    COORD pos;
    HANDLE hOutput;
    pos.X = x;//水平方向
    pos.Y = y;//垂直方向
    hOutput =GetStdHandle(STD_OUTPUT_HANDLE);//获取缓冲区中的数据,地址赋值给句柄SetConsoleCursorPosition(hOutput, pos);/*定位光标位置的函数,坐标为GetStdHandle()返回标准的输出的句柄,
    也就是获得输出屏幕缓冲区的句柄,并赋值给对象 pos*//*隐藏光标操作 */
    CONSOLE_CURSOR_INFO cursor;
    cursor.bVisible = FALSE;
    cursor.dwSize =sizeof(cursor);SetConsoleCursorInfo(hOutput,&cursor);}voidOpertion(){gotoxy(32,3);printf("Opertion:");gotoxy(42,4);printf("0.Exit     1.Add      2.Delete  3.Search");gotoxy(42,6);printf("4.Modify   5.Order    6.Show    7.Clear");}voidOpenContact(){char arr[]="Welcome back to  Contact";char arr1[]="                          ";int left =0;//左下标int right =strlen(arr)-1;//右下标while(left < right){
        arr1[left]= arr[left];
        arr1[right]= arr[right];gotoxy(44,2);printf("%s", arr1);Sleep(1);/*if (right - left != 1)
            system("cls");*/
        left++;
        right--;}Opertion();printf("\n");}voidInput(){gotoxy(32,7);printf("请输入执行功能按键:                                  ");}voidCleanInput(){gotoxy(32,7);printf("                   ");}voidError(){gotoxy(32,7);printf("输入错误重新输入");gotoxy(32,7);printf("                ");}//Contact* cHead = (Contact*)malloc(sizeof(Contact));intmain(){int input =0;
    Contact* cHead =(Contact*)malloc(sizeof(Contact));if(NULL== cHead){perror("AollocPeopleInfo");return1;}memset(cHead,0,sizeof(Contact));

    cHead->next =AollocPeopleInfo();
    People* pEnd = cHead->next;if(NULL== cHead->next){perror("AollocPeopleInfo");return1;}int flag =1;OpenContact();do{Input();gotoxy(32,8);scanf("%d",&input);gotoxy(32,8);printf("  ");
        flag =1;switch(input){case Exit :printf("通讯录关闭");break;case Add :AddContact(&pEnd);
            cHead->lenth++;break;case Delete :DelContact(cHead,&pEnd);
            cHead->lenth--;break;case Search:SerContact(cHead);break;case Modify:ModContact(cHead);break;case Order:OrderContact(cHead);break;case Show:ShowContact(cHead);break;case Clear:ClearContact(cHead);
            flag =0;break;default:Error();break;}gotoxy(32,3);printf("   现有联系人:%d ", cHead->lenth);}while(input);if(flag)free(cHead->next);free(cHead);
    cHead =NULL;gotoxy(32,12);return0;}

🍑顺序表实现通讯录

柔性数组形式的和堆区开辟空间的略有不同,大部分一样,数组形式的比较简单就不介绍了。

🍓通讯录属性设计

🍉堆区开辟连续空间:

在设计结构体属性的时候,增加的当前联系人个数,是非常有必要的。

因为这个是顺序存储,又因为指针与数组访问方式是打通的,所以指针可以通过下标的形式去访问每一个联系人。
这个下标从何而来呢?不就是当前联系人的个数吗?
一开始设置成0,每增加一个联系人当前联系人的个数就加一,满了在扩容。

enumop{
    Exit,
    Add,
    Delete,
    Search,
    Modify,
    Order,
    Show,
    Clear,};typedefstructPeopleInfo{char name[20];int age;char tele[12];char address[30];}People;typedefstructContacts{int count;//当前int total;//总
    People* data;}Contacts;

🍉柔性数组

enumop{
    Exit,
    Add,
    Delete,
    Search,
    Modify,
    Order,
    Show,
    Clear,};typedefstructPeopleInfo{char name[20];int age;char tele[12];char address[30];}People;typedefstructContacts{int count;//当前int total;//总
    People data[0];}Contacts;

🍉数组形式

enumop{
    Exit,
    Add,
    Delete,
    Search,
    Modify,
    Order,
    Show,
    Clear,};typedefstructPeopleInfo{char name[20];int age;char tele[12];char address[30];}People;typedefstructContacts{int count;//当前
    People data[1000];}Contacts;

🍓主函数的设计

柔性数组的形式和堆区开辟连续空间的形式,部分地方略有差异。
这里的

X

是一个宏(标识符),大小为3

voidmenu(){printf("**************************************\n");printf("********0.Eixt     1.Add    **********\n");printf("********2.Delete   3.Search **********\n");printf("********4.Modify   5.Order  **********\n");printf("********6.Show     7.Clear  **********\n");printf("**************************************\n");}

堆区开辟连续空间

这里的

Con

不是指针,是一个结构体变量,在传参的时候需要传址调用。因为通过增删查改等操作,可能会改变其内部成员。
还有一个原因,形参是实参的一份临时拷贝,假设通讯录很大,如果传值调用,这个形参要开辟同样大的空间,太浪费空间了,而传址调用只需要开辟4个字节(32位平台下)。

intmain(){
    Contacts Con ={NULL,0,0};
    Con.data =(People*)malloc(sizeof(People)* X);memset(Con.data,0,sizeof(People)* X);
    Con.total =0;int x =0;do{menu();scanf("%d",&x);switch(x){case Exit:printf("退出通讯录");break;case Add:AddContact(&Con);break;case Delete:DelContact(&Con);break;case Search:SerContact(&Con);break;case Modify:ModContact(&Con);break;case Order:OrderContact(&Con);break;case Show:ShowContact(&Con);break;case Clear:ClearContact(&Con);break;default:printf("输入错误\n");break;}}while(x);return0;}

柔性数组

Con

是一个指针变量,在不需要改变指针变量空间中存放的地址时,传指针变量就可以操作指针指向的内容。这里

Add、Clear

操作略有不同呢,这和

realloc

有关,也和形参有关。

堆区开辟一块连续空间

在这里插入图片描述

柔性数组

在这里插入图片描述

intmain(){
    Contacts* Con =(Contacts*)malloc(sizeof(Contacts)+sizeof(People)* X);
    Con->total = X;
    Con->count =0;int x =0;do{menu();scanf("%d",&x);printf("Main-->%p\n", Con);switch(x){case Exit:printf("退出通讯录");break;case Add:
            Con =AddContact(Con);break;case Delete:DelContact(Con);break;case Search:SerContact(Con);break;case Modify:ModContact(Con);break;case Order:OrderContact(Con);break;case Show:ShowContact(Con);break;case Clear:
            Con =ClearContact(Con);break;default:printf("输入错误\n");break;}}while(x);free(Con);
    Con =NULL;return0;}

🍓AddContact

堆区开辟

intAddSpace(Contacts* Con){
    People* p =(People*)realloc(Con->data,sizeof(People)*(Con->count + X));if(NULL== p){return0;}
    Con->data = p;
    Con->total += X;memset(Con->data+Con->count,0,sizeof(People)* X);return1;}voidAddContact(Contacts* Con){//判断是否满if(Con->count == Con->total){if(AddSpace(Con))printf("增容成功\n");else{printf("开辟空间失败\n");}}printf("%-20s\t%-3s\t%-12s\t%-30s\t\n","姓名","年龄","电话","地址");scanf("%s",Con->data[Con->count].name );scanf("%d",&(Con->data[Con->count].age));scanf("%s", Con->data[Con->count].tele);scanf("%s", Con->data[Con->count].address);
    Con->count++;printf("添加成功\n");}

柔性数组

Contacts*AddSpace(Contacts* Con){
    Contacts* p =(Contacts*)realloc(Con,sizeof(Contacts)+sizeof(People)*(Con->count + X));if(NULL== p){perror("开辟失败AddSpace:");return0;}printf("Space-->%p\n", p);printf("增容成功\n");
    p->total += X;memset(Con->data + Con->count,0,sizeof(People)* X);return p;}
Contacts*AddContact(Contacts* Con){//判断是否满if(Con->count == Con->total){
        Con =AddSpace(Con);printf("Add-->%p\n", Con);}printf("%-20s\t%-3s\t%-12s\t%-30s\t\n","姓名","年龄","电话","地址");scanf("%s", Con->data[Con->count].name);scanf("%d",&(Con->data[Con->count].age));scanf("%s", Con->data[Con->count].tele);scanf("%s", Con->data[Con->count].address);
    Con->count++;printf("添加成功\n");return Con;}

为什么两者不一样呢?

realloc

有关,原来已经开辟空间的后面没有足够大的空间进行扩容时,会在堆区中重新找个位置,开辟一个增容后的空间,内部的数据不会改变。

需要深刻理解刚刚的两张图片

在这里插入图片描述

堆区开辟一块连续空间的形式:通过结构体中的 指针变量

data

来维护联系人这块空间的。而

Con

是指向这个结构体的,

data

Con

指向目标的内部成员,因为函数调用传的是指针形式的,

realloc

返回一个新的地址,

data

接收了,回到

AddContact

函数中,

data

还是指向这个新的空间。
对于

data

来说这是一个是传址调用。

柔性数组:通过指针

Con

来维护联系人这块空间的,在

AddSpace

函数中,申请的空间需要交给

Con

来维护,假设

realloc

返回的是一个新的地址,如果代码和上面写的一样,回到

AddContact

中会有效吗?无效,为啥呢?指针变量

Con

空间中存储的地址发生改变了,对于指针

Con

来说这是传值调用,回到

AddContact

Con

指向的并不会是新的地址,还是原来的地址。
两种解决方案,一个是返回地址(堆区),一个是传址调用。
这里改动了,不能忘记主函数那里,也是同样的道理。

🍓ClearContact

这里柔性数组形式要注意,堆区开辟形式比较简单

堆区开辟形式

voidClearContact(Contacts* Con){free(Con->data);
    Con->data =NULL;
    Con->count =0;
    Con->total =0;printf("已清空通讯录\n");}

柔性数组
也可以使用

realloc

减少空间

Contacts*ClearContact(Contacts* Con){free(Con);
    Con =(Contacts*)malloc(sizeof(Contacts)+sizeof(People)* X);printf("After:Clear-->%p\n", Con);
    Con->total = X;
    Con->count =0;printf("已清空通讯录\n");return Con;}

其它操作,两种形式都是一样的

🍓DelContact

输入的人可能不了解数组下标是从0开始。因此x的合法值从1开始。输入的数x,遍历时x要-1。
也可以根据姓名去删除。设计一个函数要找到删除人的名字返回它对应的下标。
不管哪种都不能忘记处理没有联系人的情况

voidDelContact(Contacts* Con){printf("请输入删除哪个联系人\n");if(0== Con->count){printf("通讯录--空\n");return;}int x =0;int flag =1;while(flag){scanf("%d",&x);if(x > Con->count || x <1)printf("输入错误,重新输入\n");else
            flag =0;}//输入的x从1开始,数组下标从0开始,所以-1for(int i = x -1; i < Con->count -1; i++){
        Con->data[i]= Con->data[i +1];}
    Con->count--;printf("删除成功\n");}

🍓SerContact

也可根据姓名查找,和删除操作中说法一样不再赘述。

voidSerContact(Contacts* Con){if(0== Con->count){printf("通讯录--空\n");return;}printf("请输入要查找哪个联系人\n");int x =0;int flag =1;while(flag){scanf("%d",&x);if(x > Con->count || x <1)printf("输入错误,重新输入\n");else
            flag =0;}printf("%-20s\t%-3s\t%-12s\t%-30s\t\n","姓名","年龄","电话","地址");printf("%-20s\t", Con->data[x -1].name);printf("%-3d\t",(Con->data[x -1].age));printf("%-12s\t", Con->data[x -1].tele);printf("%-30s\t\n", Con->data[x -1].address);}

🍓ModContact

也可根据姓名修改,和删除操作中说法一样不再赘述。

voidModContact(Contacts* Con){if(0== Con->count){printf("通讯录--空\n");return;}printf("请输入要修改的哪个联系人的信息\n");int x =0;int flag =1;while(flag){scanf("%d",&x);if(x > Con->count || x <1)printf("输入错误,重新输入\n");else
            flag =0;}printf("请输入:\n");printf("%-20s\t%-3s\t%-12s\t%-30s\t\n","姓名","年龄","电话","地址");scanf("%s", Con->data[x -1].name);scanf("%d",&(Con->data[x -1].age));scanf("%s", Con->data[x -1].tele);scanf("%s", Con->data[x -1].address);printf("修改成功\n");}

🍓OrderContact

这是对

qsort

函数的实际应用,可别傻傻的再写排序函数。之前写的博客—qsort函数的介绍以及模拟实现
挺详细的,写了冒泡排序和插入排序去模拟

qsort

的功能

intage_cmp(constvoid* e1,constvoid* e2){return((People*)e1)->age -((People*)e2)->age;}intname_cmp(constvoid* e1,constvoid* e2){returnstrcmp(((People*)e1)->name,((People*)e2)->name);}voidOrderContact(Contacts* Con){if(0== Con->count){printf("通讯录--空\n");return;}printf("1.姓名排序,2.年龄排序\n");printf("请选择哪种排序\n");int x =0;int flag =1;while(flag){scanf("%d",&x);switch(x){case1:qsort(Con->data, Con->count,sizeof(People), name_cmp);
            flag =0;break;case2:qsort(Con->data, Con->count,sizeof(People), age_cmp);
            flag =0;break;default:printf("重新输入\n");break;}}printf("排序成功\n");}

🍓ShowContactw

遍历的方法就可以。

voidShowContact(Contacts* Con){int i =0;if(0== Con->count){printf("通讯录--空\n");return;}printf("%-20s\t%-3s\t%-12s\t%-30s\t\n","姓名","年龄","电话","地址");for(i =0; i < Con->count; i++){printf("%-20s\t", Con->data[i ].name);printf("%-3d\t",(Con->data[i].age));printf("%-12s\t", Con->data[i].tele);printf("%-30s\t\n", Con->data[i].address);}}

💥完整代码

💢堆区开辟空间形式

🌊Contact.h
#pragmaonce#include<stdio.h>#include<stdlib.h>#include<string.h>enum{
    Exit,
    Add,
    Delete,
    Search,
    Modify,
    Order,
    Show,
    Clear,};typedefstructPeopleInfo{char name[20];int age;char tele[12];char address[30];}People;typedefstructContacts{int count;//当前int total;//总
    People* data;}Contacts;#defineX3externvoidAddContact(Contacts* Con);externvoidDelContact(Contacts* Con);externvoidSerContact(Contacts* Con);externvoidDelContact(Contacts* Con);externvoidModContact(Contacts* Con);externvoidOrderContact(Contacts* Con);externvoidShowContact(Contacts* Con);externvoidClearContact(Contacts* Con);
🌊main.c
#include"Contact.h"voidmenu(){printf("**************************************\n");printf("********0.Eixt     1.Add    **********\n");printf("********2.Delete   3.Search **********\n");printf("********4.Modify   5.Order  **********\n");printf("********6.Show     7.Clear  **********\n");printf("**************************************\n");}intmain(){
    Contacts Con ={NULL,0,0};
    Con.data =(People*)malloc(sizeof(People)* X);memset(Con.data,0,sizeof(People)* X);
    Con.total =0;int x =0;do{menu();scanf("%d",&x);switch(x){case Exit:printf("退出通讯录");break;case Add:AddContact(&Con);break;case Delete:DelContact(&Con);break;case Search:SerContact(&Con);break;case Modify:ModContact(&Con);break;case Order:OrderContact(&Con);break;case Show:ShowContact(&Con);break;case Clear:ClearContact(&Con);break;default:printf("输入错误\n");break;}}while(x);return0;}
🌊Contact.c
#include"Contact.h"intAddSpace(Contacts* Con){
    People* p =(People*)realloc(Con->data,sizeof(People)*(Con->count + X));if(NULL== p){return0;}
    Con->data = p;
    Con->total += X;memset(Con->data+Con->count,0,sizeof(People)* X);return1;}voidAddContact(Contacts* Con){//判断是否满if(Con->count == Con->total){if(AddSpace(Con))printf("增容成功\n");else{printf("开辟空间失败\n");}}printf("%-20s\t%-3s\t%-12s\t%-30s\t\n","姓名","年龄","电话","地址");scanf("%s",Con->data[Con->count].name );scanf("%d",&(Con->data[Con->count].age));scanf("%s", Con->data[Con->count].tele);scanf("%s", Con->data[Con->count].address);
    Con->count++;printf("添加成功\n");}voidDelContact(Contacts* Con){printf("请输入删除哪个联系人\n");if(0== Con->count){printf("通讯录--空\n");return;}int x =0;int flag =1;while(flag){scanf("%d",&x);if(x > Con->count || x <1)printf("输入错误,重新输入\n");else
            flag =0;}//1 1 2 3 4//输入的x从1开始,数组下标从0开始for(int i = x -1; i < Con->count -1; i++){
        Con->data[i]= Con->data[i +1];}
    Con->count--;printf("删除成功\n");}voidSerContact(Contacts* Con){if(0== Con->count){printf("通讯录--空\n");return;}printf("请输入要查找哪个联系人\n");int x =0;int flag =1;while(flag){scanf("%d",&x);if(x > Con->count || x <1)printf("输入错误,重新输入\n");else
            flag =0;}printf("%-20s\t%-3s\t%-12s\t%-30s\t\n","姓名","年龄","电话","地址");printf("%-20s\t", Con->data[x -1].name);printf("%-3d\t",(Con->data[x -1].age));printf("%-12s\t", Con->data[x -1].tele);printf("%-30s\t\n", Con->data[x -1].address);}voidModContact(Contacts* Con){if(0== Con->count){printf("通讯录--空\n");return;}printf("请输入要修改的哪个联系人的信息\n");int x =0;int flag =1;while(flag){scanf("%d",&x);if(x > Con->count || x <1)printf("输入错误,重新输入\n");else
            flag =0;}printf("请输入:\n");printf("%-20s\t%-3s\t%-12s\t%-30s\t\n","姓名","年龄","电话","地址");scanf("%s", Con->data[x -1].name);scanf("%d",&(Con->data[x -1].age));scanf("%s", Con->data[x -1].tele);scanf("%s", Con->data[x -1].address);printf("修改成功\n");}intage_cmp(constvoid* e1,constvoid* e2){return((People*)e1)->age -((People*)e2)->age;}intname_cmp(constvoid* e1,constvoid* e2){returnstrcmp(((People*)e1)->name,((People*)e2)->name);}voidOrderContact(Contacts* Con){if(0== Con->count){printf("通讯录--空\n");return;}printf("1.姓名排序,2.年龄排序\n");printf("请选择哪种排序\n");int x =0;int flag =1;while(flag){scanf("%d",&x);switch(x){case1:qsort(Con->data, Con->count,sizeof(People), name_cmp);
            flag =0;break;case2:qsort(Con->data, Con->count,sizeof(People), age_cmp);
            flag =0;break;default:printf("重新输入\n");break;}}printf("排序成功\n");}voidShowContact(Contacts* Con){int i =0;if(0== Con->count){printf("通讯录--空\n");return;}printf("%-20s\t%-3s\t%-12s\t%-30s\t\n","姓名","年龄","电话","地址");for(i =0; i < Con->count; i++){printf("%-20s\t", Con->data[i ].name);printf("%-3d\t",(Con->data[i].age));printf("%-12s\t", Con->data[i].tele);printf("%-30s\t\n", Con->data[i].address);}}voidClearContact(Contacts* Con){free(Con->data);
    Con->data =NULL;
    Con->count =0;
    Con->total =0;printf("已清空通讯录\n");}

💢柔性数组形式

🌊Contact.h
#pragmaonce#include<stdio.h>#include<stdlib.h>#include<windows.h>#include<string.h>enumop{
    Exit,
    Add,
    Delete,
    Search,
    Modify,
    Order,
    Show,
    Clear,};typedefstructPeopleInfo{char name[20];int age;char tele[12];char address[30];}People;typedefstructContacts{int count;//通讯中总个数int total;//通讯录中当前个数
    People data[];}Contacts;#defineX3//每次开辟三个空间extern Contacts*AddContact(Contacts* Con);externvoidDelContact(Contacts* Con);externvoidSerContact(Contacts* Con);externvoidDelContact(Contacts* Con);externvoidModContact(Contacts* Con);externvoidOrderContact(Contacts* Con);externvoidShowContact(Contacts* Con);extern Contacts*ClearContact(Contacts* Con);
🌊main.c
#include"Contact.h"voidmenu(){printf("**************************************\n");printf("********0.Eixt     1.Add    **********\n");printf("********2.Delete   3.Search **********\n");printf("********4.Modify   5.Order  **********\n");printf("********6.Show     7.Clear  **********\n");printf("**************************************\n");}intmain(){//柔性数组
    Contacts* Con =(Contacts*)malloc(sizeof(Contacts)+sizeof(People)* X);
    Con->total = X;
    Con->count =0;int x =0;do{menu();scanf("%d",&x);printf("Main-->%p\n", Con);switch(x){case Exit:printf("退出通讯录");break;case Add:
            Con =AddContact(Con);break;case Delete:DelContact(Con);break;case Search:SerContact(Con);break;case Modify:ModContact(Con);break;case Order:OrderContact(Con);break;case Show:ShowContact(Con);break;case Clear:
            Con =ClearContact(Con);break;default:printf("输入错误\n");break;}}while(x);free(Con);
    Con =NULL;return0;}
🌊Contact.c
#include"Contact.h"

Contacts*AddSpace(Contacts* Con){
    Contacts* p =(Contacts*)realloc(Con,sizeof(Contacts)+sizeof(People)*(Con->count + X));if(NULL== p){perror("开辟失败AddSpace:");return0;}printf("Space-->%p\n", p);printf("增容成功\n");
    p->total += X;memset(Con->data + Con->count,0,sizeof(People)* X);return p;}
Contacts*AddContact(Contacts* Con){//判断是否满if(Con->count == Con->total){
        Con =AddSpace(Con);printf("Add-->%p\n", Con);}printf("%-20s\t%-3s\t%-12s\t%-30s\t\n","姓名","年龄","电话","地址");scanf("%s", Con->data[Con->count].name);scanf("%d",&(Con->data[Con->count].age));scanf("%s", Con->data[Con->count].tele);scanf("%s", Con->data[Con->count].address);
    Con->count++;printf("添加成功\n");return Con;}voidDelContact(Contacts* Con){printf("请输入删除哪个联系人\n");if(0== Con->count){printf("通讯录--空\n");return;}int x =0;int flag =1;while(flag){scanf("%d",&x);if(x > Con->count || x <1)printf("输入错误,重新输入\n");else
            flag =0;}//1 1 2 3 4//输入的x从1开始,数组下标从0开始for(int i = x -1; i < Con->count -1; i++){
        Con->data[i]= Con->data[i +1];}
    Con->count--;printf("删除成功\n");}voidSerContact(Contacts* Con){if(0== Con->count){printf("通讯录--空\n");return;}printf("请输入要查找哪个联系人\n");int x =0;int flag =1;while(flag){scanf("%d",&x);if(x > Con->count || x <1)printf("输入错误,重新输入\n");else
            flag =0;}printf("%-20s\t%-3s\t%-12s\t%-30s\t\n","姓名","年龄","电话","地址");printf("%-20s\t", Con->data[x -1].name);printf("%-3d\t",(Con->data[x -1].age));printf("%-12s\t", Con->data[x -1].tele);printf("%-30s\t\n", Con->data[x -1].address);}voidModContact(Contacts* Con){if(0== Con->count){printf("通讯录--空\n");return;}printf("请输入要修改的哪个联系人的信息\n");int x =0;int flag =1;while(flag){scanf("%d",&x);if(x > Con->count || x <1)printf("输入错误,重新输入\n");else
            flag =0;}printf("请输入:\n");printf("%-20s\t%-3s\t%-12s\t%-30s\t\n","姓名","年龄","电话","地址");scanf("%s", Con->data[x -1].name);scanf("%d",&(Con->data[x -1].age));scanf("%s", Con->data[x -1].tele);scanf("%s", Con->data[x -1].address);printf("修改成功\n");}intage_cmp(constvoid* e1,constvoid* e2){return((People*)e1)->age -((People*)e2)->age;}intname_cmp(constvoid* e1,constvoid* e2){returnstrcmp(((People*)e1)->name,((People*)e2)->name);}voidOrderContact(Contacts* Con){if(0== Con->count){printf("通讯录--空\n");return;}printf("1.姓名排序,2.年龄排序\n");printf("请选择哪种排序\n");int x =0;int flag =1;while(flag){scanf("%d",&x);switch(x){case1:qsort(Con->data, Con->count,sizeof(People), name_cmp);
            flag =0;break;case2:qsort(Con->data, Con->count,sizeof(People), age_cmp);
            flag =0;break;default:printf("重新输入\n");break;}}printf("排序成功\n");}voidShowContact(Contacts* Con){int i =0;if(0== Con->count){printf("通讯录--空\n");return;}printf("%-20s\t%-3s\t%-12s\t%-30s\t\n","姓名","年龄","电话","地址");for(i =0; i < Con->count; i++){printf("%-20s\t", Con->data[i].name);printf("%-3d\t",(Con->data[i].age));printf("%-12s\t", Con->data[i].tele);printf("%-30s\t\n", Con->data[i].address);}}
Contacts*ClearContact(Contacts* Con){free(Con);
    Con =(Contacts*)malloc(sizeof(Contacts)+sizeof(People)* X);printf("After:Clear-->%p\n", Con);
    Con->total = X;
    Con->count =0;return Con;printf("已清空通讯录\n");}

🍑文件操作

文件操作也不是太复杂,当退出通讯录的时候就把内存中的数据存放到文件中就可以了。当运行程序的时候把文件中的数据加载到内存中就完成了这个文件操作。
这里就以单链表的操作为例

加载数据进内存

要注意的需要用尾插法和

AddContact

联系起来,同时每加载一个都要更新

lenth

所以

head

必须传过来

//加载进内存voidLoadingData(Contact* head,People** end){
    FILE* f =fopen("Contact.txt","r");if(NULL== f){perror("Loading::");exit(0);}

    People* p =AollocPeopleInfo();while(fscanf(f,"%s %d %s %s", p->name,&p->age, p->tele, p->address)!=EOF){(*end)->next = p;*end = p;
        head->lenth++;}gotoxy(32,7);printf("数据加载完成");Sleep(30);gotoxy(32,7);printf("             ");}

主函数中的修改

在这里插入图片描述
数据加载进文件中

从第一节点开始,一开始可以判断一下第一个节点是否为

NULL

,是的话就返回

//将数据存进文件中voidStoreData(Contact* head){
    FILE* f =fopen("Contact.txt","w");if(NULL== f){if(NULL== f){perror("Store::");exit(0);}}
    People* p = head->next->next;//指向第一个节点。if(NULL== p){return;}while(p){fprintf(f,"%s %d %s %s", p->name, p->age, p->tele, p->address);
        p = p->next;}gotoxy(32,8);printf("数据存储完成");Sleep(30);gotoxy(32,8);printf("             ");}

主函数中位置

在这里插入图片描述

这两个函数的声明要写在头文件中

在这里插入图片描述

写完的时候先把

LoadingData(cHead,&pEnd);

这个注释掉,先通过

StoreData(cHead);

生成文件。因为刚开始还没有文件,执行

LoadingData(cHead,&pEnd);

会报错,执行

StoreData(cHead);

没有文件会自动生成一个文件。当然你也可以在对应的路径下手动添加同名文件。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

呼呼,终于写完了,希望对你有所帮助🤗


本文转载自: https://blog.csdn.net/m0_64212811/article/details/127285822
版权归原作者 日向晚,声声慢 所有, 如有侵权,请联系我们删除。

“单链表、顺序表实操小项目---通讯录”的评论:

还没有评论