(使用柔性数组)动态版通讯录

编程入门 行业动态 更新时间:2024-10-08 08:25:25

(使用<a href=https://www.elefans.com/category/jswz/34/1753562.html style=柔性数组)动态版通讯录"/>

(使用柔性数组)动态版通讯录

文章目录

  • 前言:
  • 思维导图
  • 思路分析
    • 分析一:
    • 分析二
    • 分析三:
    • 分析四:
  • 函数分析
    • 初始化函数
    • 打印函数
    • 排序函数
    • 添加元素函数
    • 查找元素函数
    • 删除元素函数
    • 修改元素函数
  • 全部代码:
    • text.c
    • contact.h
    • contact.c
  • 效果展示
  • 总结

前言:

  • 博主目前实力有限,博文有什么错误,请你斧正!

  • 后面学会EasyX会重新更新本篇博客。0.0.

  • 上一篇博客更新了动态内存管理与柔性数组,因此趁热打铁,本通讯录采用动态函数与柔性数组。

  • 希望本篇博客对你有帮助。

思维导图

思路分析

分析一:

我的思路是来一个元素,增加一次空间(realloc),这样不需要预留空间 了。顺便在添加元素的时侯,排序元素。0.0

分析二

通讯录中包含 很多人,而每个人又有很多类信息。因此我们创建2个结构体(一个也行,建立结构数组):一个是人信息的集合,一个记录有效的人数与人结构柔性数组的通讯录

enum whole//存放全局作用的常量
{Name_max=20,Age_max=10,Sex_max=20,Tel_max=20,Exit=0,Add,Del,Modify,Show,Search
};struct peo
{char name[Name_max];char age[Age_max];char sex[Sex_max];char tel[Tel_max];
};struct cont
{size_t sz;struct peo date[0];//柔性数组,这里用realloc
};

分析三:

在删除,修改,都需要对结构体进行元素查找(我用的二分查找法,需要数组有一定顺序),因此需要排序数组,但是什么时候排序呢?我的思路是在添加元素的时候就排序数组。而排序我用的是 模拟qsort的方法。需要2个种子。

分析四:

我们在函数中动态内存开辟时,实参可能不产生任何影响**(形参只是实参的拷贝,我只是给形式变量传一个数据)**。为了避免这种情况,在某些函数我们通过使用二级指针。可以改变实参的指向。另外

在这个函数中:我们可以先用中间变量去 realloc,产生我们想要作用之后,再用实参指向新的申请空间。

函数分析

初始化函数

  • 通过二级指针的方法,改变实参的指向。
void In_Bein(struct cont** pc)
{assert(pc);struct cont *p = (struct cont*)malloc(sizeof(struct cont));//这里只需要给sz申请空间就行了//后面增加元素的时候再未peo开辟空间if (NULL == p){printf("%s\n", strerror(errno));exit(1);}p->sz = 0;*pc = p;//p是局部变量,因此函数栈帧结束后,就成为野指针,但是那快动态申请的空间没有free,// 但是我们通过 *pc=p,这步,在程序结束,最后free就行了。
}

打印函数

void My_Show(struct cont* pc)
{assert(pc);//断言防止传入NULL指针for (size_t i = 0; i < pc->sz; i++){printf("姓名:%s\n", ((pc -> date)+i)->name);printf("年龄:%s\n", ((pc-> date)+i)->age);printf("性别:%s\n", ((pc->date)+i)->sex);printf("电话:%s\n", ((pc->date)+i)->tel);}}

排序函数

  • 复写qsort的使用见我另外一篇博客:

  • 在排序的时候,可能会碰到同名的情况,因此我们比较电话号码就行了,毕竟电话号码是唯一的。因此需要2个比较函数(我称谓种子)。(排序是从小到大,想从大到小,改变种子就行)

int compare1(const void* e1, const void* e2)//种子1 比较名字//比较结构体2元素中成员 名字
{assert(e1 && e2);//防止传入NULL指针return strcmp(((struct peo*)e1)->name, ((struct peo*)e2)->name);}
int compare2(const void* e1, const void* e2)//种子2 比较电话
{assert(e1 && e2);return strcmp(((struct peo*)e1)->tel, ((struct peo*)e2)->tel);}
void swap(void* e1, void* e2, size_t width)//交换2元素所占内存字节中的内容
{assert(e1 && e2);for (size_t i = 0; i < width; i++)//依次交换e1,e2中的字节内容{unsigned char tmp = *((unsigned char*)e1 + i);*((unsigned char*)e1 + i) = *((unsigned char*)e2 + i);*((unsigned char*)e2 + i) = tmp;}}
void my_qsort(void* base,size_t num,size_t width	)
{assert(base);//防止传入NULL指针for (size_t i = 0; i < num-1; i++){size_t min = i;for (size_t j = i + 1; j < num; j++){ //传入的是date,因此强转(char*)后利用width就行了if (0 < compare1( (char *)base + min * width, (char*)base + j * width)){min = j;}else if (0 == compare1((char*)base + min * width, (char*)base + j * width))//一旦重名就检测电话{if (0 < compare2((char*)base + min * width, (char*)base + j * width)){min = j;}}}if (min != i){swap((char *)base + i * width, (char *)base + min * width,width);}}
}

添加元素函数

  • 添加元素,必然需要重新开辟空间,需要realloc。
  • 我们要使实参改变指向,必然二级指针。
void My_Add(struct cont** pc)
{assert(pc);//断言防止传入NULL指针struct  cont* p = (struct cont*)realloc(*pc, sizeof(struct cont) + ((*pc)->sz+1) * sizeof(struct peo));if (NULL == p){printf("%s\n", strerror(errno));//打印错误信息exit(1);}printf("请输入姓名:");scanf("%s", (p->date + p->sz)->name);printf("\n");printf("请输入性别:");scanf("%s", (p->date + p->sz)->sex);printf("\n");printf("请输入年龄:");scanf("%s", (p->date + p->sz)->age);printf("\n");printf("请输入电话:");scanf("%s", (p->date + p->sz)->tel);p->sz++;*pc = p;my_qsort(p->date, p->sz, sizeof(struct peo));//排序新增后的元素
}
void My_Add(struct cont** pc)
{assert(pc);//断言防止传入NULL指针struct  cont* p = (struct cont*)realloc(*pc, sizeof(struct cont) + ((*pc)->sz+1) * sizeof(struct peo));if (NULL == p){printf("%s\n", strerror(errno));//打印错误信息exit(1);}printf("请输入姓名:");scanf("%s", (p->date + p->sz)->name);printf("\n");printf("请输入性别:");scanf("%s", (p->date + p->sz)->sex);printf("\n");printf("请输入年龄:");scanf("%s", (p->date + p->sz)->age);printf("\n");printf("请输入电话:");scanf("%s", (p->date + p->sz)->tel);p->sz++;*pc = p;my_qsort(p->date, p->sz, sizeof(struct peo));
}

查找元素函数

  • 二分查找必然需要数组一定顺序.

  • 返回一个值的原因是为了其它函数的实现。

int  My_Search(struct cont* pc,const char *str1,const char *str2)//二分查找
{assert(pc && str1 && str2);//断言防止传入NULL指针size_t left = 0;size_t right = pc->sz - 1;while (left <= right){size_t mid = (left + right) / 2;if ((strcmp((pc->date + mid)->name, str1)==0)&& (strcmp((pc->date + mid)->tel, str2) == 0)){printf("找到了\n");printf("%d\n", mid);return mid;}else if((strcmp((pc->date + mid)->name, str1)<0)||((strcmp((pc->date + mid)->name, str1)==0)  && (strcmp((pc->date + mid)->tel, str2)<0))){left = mid + 1;}else if ((strcmp((pc->date + mid)->name, str1) > 0) || ((strcmp((pc->date + mid)->name, str1) == 0 && (strcmp((pc->date + mid)->tel, str2) > 0)))){right = mid - 1;}}printf("查无此人\n");return -1;
}

删除元素函数

  • 删除必然需要查找是否存在。而查找又需要排序。哈哈~~~0.0.
void My_Del(struct cont** pc)
{assert(pc);//断言防止传入NULL指针char You_name[Name_max] = {0};char You_tel[Tel_max] = {0};printf("请输入要删除的姓名与电话\n");scanf("%s", You_name);scanf("%s", You_tel);int ret = My_Search(*pc,You_name,You_tel);if (ret!=-1){for (size_t i = ret; i < (*pc)->sz-1; i++){*((*pc)->date + i) = *((*pc) -> date+i + 1);}struct cont* p = (struct cont*)realloc(*pc, ((*pc)->sz - 1) * sizeof(struct peo));if (NULL == p){printf("%s\n", strerror(errno));exit(1);}p->sz--;*pc = p;}else{printf("请重新输入\n");}
}

修改元素函数

  • 修改必然查找…(套娃–哈哈 ~ ~~)
void My_Modify(struct cont* pc)
{assert(pc);//断言防止传入NULL指针char You_name[Name_max] = {0};//要初始化下,strcpy的原因char You_tel[Tel_max] = {0};char You_sex[Sex_max] = {0};char You_age[Age_max] = {0};while (1){printf("请输入要修改的姓名与电话\n");scanf("%s", You_name);scanf("%s", You_tel);int ret = My_Search(pc, You_name, You_tel);if (ret != -1){printf("请输入要修改的姓名:");scanf("%s", You_name);strcpy((pc->date + ret)->name, You_name);printf("请输入要修改的年龄:");scanf("%s", You_age);strcpy((pc->date + ret)->age , You_age);printf("请输入要修改的性别:");scanf("%s", You_sex);strcpy((pc->date + ret)->sex, You_sex);printf("请输入要修改的电话:");scanf("%s", You_tel);strcpy((pc->date + ret)->tel, You_tel);printf("修改成功\n");return;}else{printf("%s\t%s\n", You_name, You_tel);}}
}

全部代码:

text.c

#define _CRT_SECURE_NO_WARNINGS 1#include "contact.h"void menu()//提示菜单
{printf("****************************************************\n");printf("*************动态通讯录*****************************\n");printf("****************************************************\n");printf("*******1.Add(添加)               2. Del(删除)*******\n");printf("****** 3.Modify(纠正)            4.Show(打印)*******\n");printf("*******5.Search(寻找)              *******\n");printf("*******0.Exit(退出)                     ************\n");printf("****************************************************\n");
}int main ()
{int Input = 0;struct cont* pc = NULL;//悬挂pcIn_Bein(&pc);//因为pc NULL的原因,我们必须让它指向一段动态的内存,并初始化。do//先打印提示菜单,供玩家判断。{menu();printf("请输入你的选择:\n");scanf("%d", &Input);switch (Input){case Add:My_Add(&pc);break;case Del:My_Del(&pc);break;case Modify:My_Modify(pc);break;case Show:My_Show(pc);break;case Search:{char You_name[Name_max] = {0};char You_tel[Tel_max] = {0};printf("请输入要找的的姓名与电话\n");scanf("%s", You_name);scanf("%s", You_tel);My_Search(pc, You_name, You_tel);};break;case Exit://exit是C语言内置的关键字,因此Exit,而不是 exit.{system("cls");//清空屏幕printf("\t\t~~~~~~~感谢使用博主的动态通许录!!!!~~~~~\n");};break;default:printf("输入错误。请重新输入\n");break;}} while (Input);free(pc);pc = NULL;return 0;
}

contact.h

#pragma once#include <stdio.h>
#include <stdlib.h> 
#include <assert.h>
#include <string.h>
enum whole//存放全局作用的常量
{Name_max=20,Age_max=10,Sex_max=20,Tel_max=20,Exit=0,Add,Del,Modify,Show,Search
};struct peo
{char name[Name_max];char age[Age_max];char sex[Sex_max];char tel[Tel_max];
};struct cont
{size_t sz;struct peo date[0];//柔性数组,这里用realloc
};void In_Bein(struct cont** pc);
void My_Show(struct cont* pc);
void My_Add(struct cont** pc);
void My_Del(struct cont** pc);
int  My_Search(struct cont* pc,const char*str1,const char *str2);
void My_Modify(struct cont* pc);
int compare1(const void* e1, const void* e2);//种子1 比较名字//比较结构体2元素中成员 名字 
int compare2(const void* e1, const void* e2);//种子2 比较学号
void swap(void* e1, void* e2, size_t width);
void my_qsort(void* base,size_t num,size_t width);//有2个种子就不需要 再来个参数了

contact.c

#define _CRT_SECURE_NO_WARNINGS 1#include "contact.h"
void In_Bein(struct cont** pc)
{assert(pc);//断言防止传入NULL指针struct cont* p = (struct cont*)malloc(sizeof(struct cont));//这里只需要给sz申请空间就行了//后面增加元素的时候再未peo开辟空间if (NULL == p){printf("%s\n", strerror(errno));exit(1);}p->sz = 0;*pc = p;//p是局部变量,因此函数栈帧结束后,就成为野指针,但是那快动态申请的空间没有free,// 但是我们通过 *pc=p,这步,在程序结束,最后free就行了。
}
void My_Show(struct cont* pc)
{assert(pc);//断言防止传入NULL指针for (size_t i = 0; i < pc->sz; i++){printf("姓名:%s\n", ((pc -> date)+i)->name);printf("年龄:%s\n", ((pc-> date)+i)->age);printf("性别:%s\n", ((pc->date)+i)->sex);printf("电话:%s\n", ((pc->date)+i)->tel);}}
void My_Add(struct cont** pc)
{assert(pc);//断言防止传入NULL指针struct  cont* p = (struct cont*)realloc(*pc, sizeof(struct cont) + ((*pc)->sz+1) * sizeof(struct peo));if (NULL == p){printf("%s\n", strerror(errno));//打印错误信息exit(1);}printf("请输入姓名:");scanf("%s", (p->date + p->sz)->name);printf("\n");printf("请输入性别:");scanf("%s", (p->date + p->sz)->sex);printf("\n");printf("请输入年龄:");scanf("%s", (p->date + p->sz)->age);printf("\n");printf("请输入电话:");scanf("%s", (p->date + p->sz)->tel);p->sz++;*pc = p;my_qsort(p->date, p->sz, sizeof(struct peo));//排序新增后的元素
}int  My_Search(struct cont* pc,const char *str1,const char *str2)//二分查找
{assert(pc && str1 && str2);//断言防止传入NULL指针size_t left = 0;size_t right = pc->sz - 1;while (left <= right){size_t mid = (left + right) / 2;if ((strcmp((pc->date + mid)->name, str1)==0)&& (strcmp((pc->date + mid)->tel, str2) == 0)){printf("找到了\n");printf("%d\n", mid);return mid;}else if((strcmp((pc->date + mid)->name, str1)<0)||((strcmp((pc->date + mid)->name, str1)==0)  && (strcmp((pc->date + mid)->tel, str2)<0))){left = mid + 1;}else if ((strcmp((pc->date + mid)->name, str1) > 0) || ((strcmp((pc->date + mid)->name, str1) == 0 && (strcmp((pc->date + mid)->tel, str2) > 0)))){right = mid - 1;}}printf("查无此人\n");return -1;
}void My_Del(struct cont** pc)
{assert(pc);//断言防止传入NULL指针char You_name[Name_max] = {0};char You_tel[Tel_max] = {0};printf("请输入要删除的姓名与电话\n");scanf("%s", You_name);scanf("%s", You_tel);int ret = My_Search(*pc,You_name,You_tel);if (ret!=-1){for (size_t i = ret; i < (*pc)->sz-1; i++){*((*pc)->date + i) = *((*pc) -> date+i + 1);}struct cont* p = (struct cont*)realloc(*pc, ((*pc)->sz - 1) * sizeof(struct peo));if (NULL == p){printf("%s\n", strerror(errno));exit(1);}p->sz--;*pc = p;}else{printf("请重新输入\n");}
}void My_Modify(struct cont* pc)
{assert(pc);//断言防止传入NULL指针char You_name[Name_max] = {0};//要初始化下,strcpy的原因char You_tel[Tel_max] = {0};char You_sex[Sex_max] = {0};char You_age[Age_max] = {0};while (1){printf("请输入要修改的姓名与电话\n");scanf("%s", You_name);scanf("%s", You_tel);int ret = My_Search(pc, You_name, You_tel);if (ret != -1){printf("请输入要修改的姓名:");scanf("%s", You_name);strcpy((pc->date + ret)->name, You_name);printf("请输入要修改的年龄:");scanf("%s", You_age);strcpy((pc->date + ret)->age , You_age);printf("请输入要修改的性别:");scanf("%s", You_sex);strcpy((pc->date + ret)->sex, You_sex);printf("请输入要修改的电话:");scanf("%s", You_tel);strcpy((pc->date + ret)->tel, You_tel);printf("修改成功\n");return;}else{printf("%s\t%s\n", You_name, You_tel);}}
}int compare1(const void* e1, const void* e2)//种子1 比较名字//比较结构体2元素中成员 名字
{assert(e1 && e2);//防止传入NULL指针return strcmp(((struct peo*)e1)->name, ((struct peo*)e2)->name);}
int compare2(const void* e1, const void* e2)//种子2 比较电话
{assert(e1 && e2);return strcmp(((struct peo*)e1)->tel, ((struct peo*)e2)->tel);}
void swap(void* e1, void* e2, size_t width)//交换2元素所占内存字节中的内容
{assert(e1 && e2);for (size_t i = 0; i < width; i++)//依次交换e1,e2中的字节内容{unsigned char tmp = *((unsigned char*)e1 + i);*((unsigned char*)e1 + i) = *((unsigned char*)e2 + i);*((unsigned char*)e2 + i) = tmp;}}
void my_qsort(void* base,size_t num,size_t width	)
{assert(base);//防止传入NULL指针for (size_t i = 0; i < num-1; i++){size_t min = i;for (size_t j = i + 1; j < num; j++){ //传入的是date,因此强转(char*)后利用width就行了if (0 < compare1( (char *)base + min * width, (char*)base + j * width)){min = j;}else if (0 == compare1((char*)base + min * width, (char*)base + j * width))//一旦重名就检测电话{if (0 < compare2((char*)base + min * width, (char*)base + j * width)){min = j;}}}if (min != i){swap((char *)base + i * width, (char *)base + min * width,width);}}
}

效果展示

总结

弄清思路,代码的实现只是时间与思考的问题。
动态函数的语法容易理解,但是实操却会有很多问题坚持,调试,思考,测试。反复这个400行的小程序,你也可以

更多推荐

(使用柔性数组)动态版通讯录

本文发布于:2024-02-07 05:19:32,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1753335.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:柔性   数组   版通   动态

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!