⭐博客主页:️CS semi主页
⭐欢迎关注:点赞收藏+留言
⭐系列专栏:C语言进阶
⭐代码仓库:C Advanced
家人们更新不易,你们的点赞和关注对我而言十分重要,友友们麻烦多多点赞+关注,你们的支持是我创作最大的动力,欢迎友友们私信提问,家人们不要忘记点赞收藏+关注哦!!!
通讯录2.0
- 前言
- 一、枚举
- 二、动态内存
- (一)更改初始化
- (二)更改ADD
- (三)销毁内存空间
- (四)修改qsort函数
- 三、文件操作(用fread和fwrite改造)
- (一)EXIT退出函数
- (二)InitContact初始化函数
- 四、结果
- 五、原码
- 总结
前言
前面我们写了简单的通讯录,接下来我们将写进阶的通讯录,里面包含枚举,动态内存和文件的操作,是包含了所有进阶的知识,所以,跟着我的脚步,出发!!!
一、枚举
枚举有点很多,比较大的优点是可读性好,当我们进行选择1,2,……后,我们对于这串代码的含义并不明确,而如果直接用枚举显示出来这些名称直接用,可读性非常强,另一个优点是它是有自己本身的类型的,是有类型检查的。更加严谨。
所以,只需要改test.c中的代码即可:
二、动态内存
(一)更改初始化
我们在进行存放联系人信息的时候,我现在有15个朋友,那我设立那么长的空间长时间不用是不是浪费了这么大的空间,那我们现在想的是设立一个动态数组,默认第一次的空间是3个,然后每当不够的时候加2个,空间不够就往上加2个……那我们改造一下:
我们创建动态内存是需要进行结构体声明的,是需要在contact.h里面替换掉原本的Contact的结构体而变成如下图所示(contact.h):
是data所指向的那段另开辟的动态内存空间,为了方便我们使用capcity的作用,我们进行宏定义,将初始化定义为3和将每次自增二定义为2,如下图(在contact.h中):
进行定义完这个数组以后,我们需要来对初始化通讯录进行改造,这里就用到了动态数组开辟的方法,为data开辟一块空间(在contact.c中),如下图:
挨个解释解释:
制作一个新的ptr指针,用来存放动态开辟的数组,类型肯定要和pc->data是一样的,因为这样才能实现将ptr首元素指针传给pc,因为ptr是用来维护pc指针的,是要看是不是空指针,至于calloc函数,那就直接上链接,大家去直接看下面博客关于calloc的详细讲解:动态内存 那当然了,为了不影响速度,那就简单介绍一下calloc,它是能够将它所维护的空间初始化为0,然后我们将capcity初始化为3即可。
(二)更改ADD
这是在contact.c中的,上完代码就一步步解释。
我们将这个动态开辟内存封装成一个函数,在函数内部,我们需要判断的是已经满容量了以后,我们需要做的操作,要增容怎么办,realloc解决一切烦恼,大家也可以看博客,也可以听我先简单陈述:内存函数 realloc函数的解释是pc->data是所需增加的空间,后面的是总的数(增加的空间加上原本的空间)。和前面一样的道理,和王者荣耀里面的元歌一样,分身先往前走,前面安全了再让真身走,前面有危险,分身消失,真身保持不变。
(三)销毁内存空间
我们在学习动态数组开辟的时候,是有free这个函数的,是释放开辟的空间的函数,而在制作这套通讯录的动态数组中,我们同样也需要来一个销毁这个动态内存的空间的操作,那我们就定义一个Destory函数吧!
test.c
contact.h
contact.c
这串代码总体来讲就是将这些值全部变为空或者0。
(四)修改qsort函数
我发现如果qsort函数一直是保持这样不变的话,是肯定不正确的,我通过调试和各项参考发现如果还按照老版本的qsort函数发现数据都给我吞了,和Empty函数怎么一样了,当即我就慌了神,但是经过一段时间的思考,&con本来就是在结构体内开辟的一个数组,已经是被维护的数组,而con.data是指向某一个数组,因为我们创立的的data是指向某一个数组的,所以就不能是&con了,需要找到data的数组。
到这里先给个整体的代码,下面将进行文件的操作,有很多改编不需要文件的操作那就这里先放一下动态数组的版本吧!
test.c
#include"contact.h"
void menu()
{
printf("************************************\n");
printf("******* 1.add 2.del *******\n");
printf("******* 3.search 4.modify *******\n");
printf("******* 5.show 6.sort *******\n");
printf("******* 7.empty 0.exit *******\n");
printf("************************************\n");
}
enum Option {
EXIT,//0
ADD, //1
DEL, //2
SEARCH,
MODIFY,
SHOW,
SORT,
EMPTY
};
int main()
{
int input = 0;
//创建通讯录
Contact con;
//初始化通讯录
InitContact(&con);
do {
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case ADD:
AddContact(&con);
break;
case DEL:
DelContact(&con);
Sleep(1000);
break;
case SEARCH:
SearchContact(&con);
break;
case MODIFY:
ModifyContact(&con);
Sleep(500);
break;
case SHOW:
ShowContact(&con);
break;
case SORT://&con
qsort(con.data, con.sz, sizeof(con.data[0]), ContactSortName);
printf("排序成功\n");
Sleep(1000);
break;
case EMPTY:
EmptyContact(&con);
printf("已全部清空\n");
Sleep(1000);
break;
case EXIT:
Destory(&con);
printf("退出通讯录\n");
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
return 0;
}
contact.h
#define MAX 1000
#define NAMEMAX 20
#define SEWXMAX 5
#define ADDRMAX 30
#define TELEMAX 12
#define DEFAULTSZ 3
#define INCSZ 2
#include<stdio.h>
#include<string.h>
#include<assert.h>
#include<stdlib.h>
#include<windows.h>
//人的信息
typedef struct PeoInfo {
char name[NAMEMAX];
int age;
char sex[SEWXMAX];
char addr[ADDRMAX];
char tele[TELEMAX];
}PeoInfo;
静态数组版本
//typedef struct Contact {
// //创建通讯录
// PeoInfo data[MAX];//存放人的信息
// int sz;//当前已经放的信息的个数
//}Contact;
//动态数组版本
typedef struct Contact {
//创建通讯录
PeoInfo* data;//指向存放人的信息的空间
int sz;//当前已经放的信息的个数
int capacity;//当前通讯录的最大容量
}Contact;
//初始化通讯录
void InitContact(Contact* pc);
//增加联系人
void AddContact(Contact* pc);
//显示通讯录里的联系人
void ShowContact(const Contact* pc);
//删除联系人
void DelContact(Contact* pc);
//查找联系人
void SearchContact(const Contact* pc);
//修改指定联系人信息
void ModifyContact(Contact* pc);
//排序
int ContactSortName(void* e1, void* e2);
//清空
void EmptyContact(Contact* pc);
//销毁通讯录内存
void Destory(Contact* pc);
contact.c
//负责实现声明
#include"contact.h"
静态版本
实现初始化通讯录
//void InitContact(Contact* pc) {
// assert(pc);
// pc->sz = 0;
// memset(pc->data, 0, sizeof(pc->data));//传过来的是地址,根据地址找到了data数组,算的是data数组的总大小
//}
//动态版本
//实现初始化通讯录
void InitContact(Contact* pc) {
assert(pc);
pc->sz = 0;
PeoInfo* ptr = (PeoInfo*)calloc(DEFAULTSZ,sizeof(PeoInfo) );
if (ptr == NULL) {
perror("calloc::InitContact");
return;
}
pc->data = ptr;
pc->capacity = DEFAULTSZ;
}
静态版本
增加联系人
//void AddContact(Contact* pc) {
// assert(pc);
// if (pc->sz == MAX) {
// printf("通讯录已满,无法添加联系人\n");
// return;
// }
// //增加一个人信息
// printf("请输入名字>");
// scanf("%s", pc->data[pc->sz].name);
// printf("请输入年龄>");
// scanf("%d", &(pc->data[pc->sz].age));
// printf("请输入性别>");
// scanf("%s", pc->data[pc->sz].sex);
// printf("请输入地址>");
// scanf("%s", pc->data[pc->sz].addr);
// printf("请输入电话>");
// scanf("%s", pc->data[pc->sz].tele);
// pc->sz++;
//}
//动态版本
//增加联系人
void check_capacity(Contact* pc) {
if (pc->sz == pc->capacity) {
//增容
PeoInfo* ptr = (PeoInfo*)realloc(pc->data, sizeof(PeoInfo) * (pc->capacity + INCSZ));
if (ptr == NULL) {
perror("realloc::check_capacity");
return;
}
pc->data = ptr;
pc->capacity += INCSZ;
}
}
void AddContact(Contact* pc) {
assert(pc);
check_capacity(pc);
//增加一个人信息
printf("请输入名字>");
scanf("%s", pc->data[pc->sz].name);
printf("请输入年龄>");
scanf("%d", &(pc->data[pc->sz].age));
printf("请输入性别>");
scanf("%s", pc->data[pc->sz].sex);
printf("请输入地址>");
scanf("%s", pc->data[pc->sz].addr);
printf("请输入电话>");
scanf("%s", pc->data[pc->sz].tele);
pc->sz++;
}
//显示通讯录里的信息
void ShowContact(const Contact* pc) {
assert(pc);
int i = 0;
printf("%-20s\t%-4s\t%-5s\t%-20s\t%-12s\n", "name", "age", "sex", "address", "telephone");
for (i = 0; i < pc->sz; i++) {
printf("%-20s\t%-4d\t%-5s\t%-20s\t%-12s\n", pc->data[i].name,
pc->data[i].age,
pc->data[i].sex,
pc->data[i].addr,
pc->data[i].tele);
}
}
int FindByName(const Contact* pc, char name[]) {
assert(pc);
int i = 0;
int pos = 0;
for (i = 0; i < pc->sz; i++) {
if (strcmp(pc->data[i].name, name) == 0) {
pos = i;
return i;
}
}
return -1;
}
//删除联系人
void DelContact(Contact* pc) {
assert(pc);
char name[NAMEMAX] = { 0 };
//没有联系人不删除
if (pc->sz == 0) {
printf("通讯录为空,无法删除\n");
return;
}
//删除联系人
//找到要删除的人
printf("请输入要删除的人的名字:>");
scanf("%s", name);
//查找
int ret = FindByName(pc, name);
if (-1 == ret) {
printf("查无此人\n");
return;
}
else {
//删除
int i = 0;
for (i = ret; i < pc->sz - 1; i++) {
pc->data[i] = pc->data[i + 1];
}
pc->sz--;
printf("删除成功\n");
}
}
//查找联系人
void SearchContact(const Contact* pc) {
assert(pc);
char name[NAMEMAX] = { 0 };
printf("请输入要查找人的名字>");
scanf("%s", name);
int ret = FindByName(pc, name);
if (-1 == ret) {
printf("查无此人\n");
return;
}
else {
//打印信息
printf("%-20s\t%-4s\t%-5s\t%-20s\t%-12s\n", "name", "age", "sex", "address", "telephone");
printf("%-20s\t%-4d\t%-5s\t%-20s\t%-12s\n", pc->data[ret].name,
pc->data[ret].age,
pc->data[ret].sex,
pc->data[ret].addr,
pc->data[ret].tele);
}
}
//修改指定联系人
void ModifyContact(Contact* pc) {
assert(pc);
//先找这个联系人
char name[NAMEMAX] = { 0 };
printf("请输入要修改人的名字>");
scanf("%s", name);
int ret = FindByName(pc, name);
if (-1 == ret) {
printf("查无此人\n");
return;
}
else {
//信息重新录入
printf("请输入名字>");
scanf("%s", pc->data[ret].name);
printf("请输入年龄>");
scanf("%d", &(pc->data[ret].age));
printf("请输入性别>");
scanf("%s", pc->data[ret].sex);
printf("请输入地址>");
scanf("%s", pc->data[ret].addr);
printf("请输入电话>");
scanf("%s", pc->data[ret].tele);
printf("修改完成\n");
}
}
//排序
int ContactSortName(void* e1, void* e2) {
assert(e1 && e2);
return strcmp((Contact*)e1, (Contact*)e2);
}
//清空
void EmptyContact(Contact* pc) {
assert(pc);
pc->sz = 0;
memset(pc->data, 0, sizeof(pc->data));
}
//销毁通讯录内存
void Destory(Contact* pc) {
free(pc->data);
pc->data = NULL;
pc->capacity = 0;
pc->sz = 0;
pc = NULL;
}
三、文件操作(用fread和fwrite改造)
(一)EXIT退出函数
既然我们要大刀阔斧地朝向代码进行改编,势必要把它放到文件里面,那就我文件执行完我再放吧,这毕竟我立马存到文件里面万一之后要删删改改呢?所以我们在EXIT函数进行增加添加文件项:
test.c:
contact.c:
首先我们需要在相对应的文件夹中创立一个好取名字的文件名并在contact.c文件中进行引用,这里选择的是fwrite,是因为在计算机中写的是二进制的“流”,更加的好用。
这里是需要认清楚fwrite函数的用法,大家可以看下面这篇博客:
文件操作简单而言就是找到数组地址,字节长度,元素个数,文件流。
(二)InitContact初始化函数
contact.c:
我们读到文件里面需要加载文件信息到通讯录,我们直接封装一个函数进行操作(还在contact.c):
这里形式有些许复杂,我们需要了解清楚fread函数的具体作用,再根据增容的变化值进行读数据。
四、结果
这是添加完的操作:
这是退出通讯录,保存完文件的操作:
这是再次打开调试台显示数据被保存的操作:
五、原码
test.c
#include"contact.h"
void menu()
{
printf("************************************\n");
printf("******* 1.add 2.del *******\n");
printf("******* 3.search 4.modify *******\n");
printf("******* 5.show 6.sort *******\n");
printf("******* 7.empty 0.exit *******\n");
printf("************************************\n");
}
enum Option {
EXIT,//0
ADD, //1
DEL, //2
SEARCH,
MODIFY,
SHOW,
SORT,
EMPTY
};
int main()
{
int input = 0;
//创建通讯录
Contact con;
//初始化通讯录
InitContact(&con);
do {
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case ADD:
AddContact(&con);
break;
case DEL:
DelContact(&con);
Sleep(1000);
break;
case SEARCH:
SearchContact(&con);
break;
case MODIFY:
ModifyContact(&con);
Sleep(500);
break;
case SHOW:
ShowContact(&con);
break;
case SORT://&con
qsort(con.data, con.sz, sizeof(con.data[0]), ContactSortName);
printf("排序成功\n");
Sleep(1000);
break;
case EMPTY:
EmptyContact(&con);
printf("已全部清空\n");
Sleep(1000);
break;
case EXIT:
//保存通讯录信息到文件中
SaveContact(&con);
Destory(&con);
printf("退出通讯录\n");
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
return 0;
}
contact.h
#define MAX 1000
#define NAMEMAX 20
#define SEWXMAX 5
#define ADDRMAX 30
#define TELEMAX 12
#define DEFAULTSZ 3
#define INCSZ 2
#include<stdio.h>
#include<string.h>
#include<assert.h>
#include<stdlib.h>
#include<windows.h>
//人的信息
typedef struct PeoInfo {
char name[NAMEMAX];
int age;
char sex[SEWXMAX];
char addr[ADDRMAX];
char tele[TELEMAX];
}PeoInfo;
静态数组版本
//typedef struct Contact {
// //创建通讯录
// PeoInfo data[MAX];//存放人的信息
// int sz;//当前已经放的信息的个数
//}Contact;
//动态数组版本
typedef struct Contact {
//创建通讯录
PeoInfo* data;//指向存放人的信息的空间
int sz;//当前已经放的信息的个数
int capacity;//当前通讯录的最大容量
}Contact;
//初始化通讯录
void InitContact(Contact* pc);
//增加联系人
void AddContact(Contact* pc);
//显示通讯录里的联系人
void ShowContact(const Contact* pc);
//删除联系人
void DelContact(Contact* pc);
//查找联系人
void SearchContact(const Contact* pc);
//修改指定联系人信息
void ModifyContact(Contact* pc);
//排序
int ContactSortName(void* e1, void* e2);
//清空
void EmptyContact(Contact* pc);
//销毁通讯录内存
void Destory(Contact* pc);
//保存通讯录中的信息到文件中
void SaveContact(Contact* pc);
//加载文件信息到通讯录
void LoadContact(Contact* pc);
contact.c
//负责实现声明
#include"contact.h"
静态版本
实现初始化通讯录
//void InitContact(Contact* pc) {
// assert(pc);
// pc->sz = 0;
// memset(pc->data, 0, sizeof(pc->data));//传过来的是地址,根据地址找到了data数组,算的是data数组的总大小
//}
//动态版本
//实现初始化通讯录
void InitContact(Contact* pc) {
assert(pc);
pc->sz = 0;
PeoInfo* ptr = (PeoInfo*)calloc(DEFAULTSZ, sizeof(PeoInfo));
if (ptr == NULL) {
perror("calloc::InitContact");
return;
}
pc->data = ptr;
pc->capacity = DEFAULTSZ;
//加载文件信息到通讯录
LoadContact(pc);
}
静态版本
增加联系人
//void AddContact(Contact* pc) {
// assert(pc);
// if (pc->sz == MAX) {
// printf("通讯录已满,无法添加联系人\n");
// return;
// }
// //增加一个人信息
// printf("请输入名字>");
// scanf("%s", pc->data[pc->sz].name);
// printf("请输入年龄>");
// scanf("%d", &(pc->data[pc->sz].age));
// printf("请输入性别>");
// scanf("%s", pc->data[pc->sz].sex);
// printf("请输入地址>");
// scanf("%s", pc->data[pc->sz].addr);
// printf("请输入电话>");
// scanf("%s", pc->data[pc->sz].tele);
// pc->sz++;
//}
//动态版本
//增加联系人
void check_capacity(Contact* pc) {
if (pc->sz == pc->capacity) {
//增容
PeoInfo* ptr = (PeoInfo*)realloc(pc->data, sizeof(PeoInfo) * (pc->capacity + INCSZ));
if (ptr == NULL) {
perror("realloc::check_capacity");
return;
}
pc->data = ptr;
pc->capacity += INCSZ;
}
}
void AddContact(Contact* pc) {
assert(pc);
check_capacity(pc);
//增加一个人信息
printf("请输入名字>");
scanf("%s", pc->data[pc->sz].name);
printf("请输入年龄>");
scanf("%d", &(pc->data[pc->sz].age));
printf("请输入性别>");
scanf("%s", pc->data[pc->sz].sex);
printf("请输入地址>");
scanf("%s", pc->data[pc->sz].addr);
printf("请输入电话>");
scanf("%s", pc->data[pc->sz].tele);
pc->sz++;
}
//显示通讯录里的信息
void ShowContact(const Contact* pc) {
assert(pc);
int i = 0;
printf("%-20s\t%-4s\t%-5s\t%-20s\t%-12s\n", "name", "age", "sex", "address", "telephone");
for (i = 0; i < pc->sz; i++) {
printf("%-20s\t%-4d\t%-5s\t%-20s\t%-12s\n", pc->data[i].name,
pc->data[i].age,
pc->data[i].sex,
pc->data[i].addr,
pc->data[i].tele);
}
}
int FindByName(const Contact* pc, char name[]) {
assert(pc);
int i = 0;
int pos = 0;
for (i = 0; i < pc->sz; i++) {
if (strcmp(pc->data[i].name, name) == 0) {
pos = i;
return i;
}
}
return -1;
}
//删除联系人
void DelContact(Contact* pc) {
assert(pc);
char name[NAMEMAX] = { 0 };
//没有联系人不删除
if (pc->sz == 0) {
printf("通讯录为空,无法删除\n");
return;
}
//删除联系人
//找到要删除的人
printf("请输入要删除的人的名字:>");
scanf("%s", name);
//查找
int ret = FindByName(pc, name);
if (-1 == ret) {
printf("查无此人\n");
return;
}
else {
//删除
int i = 0;
for (i = ret; i < pc->sz - 1; i++) {
pc->data[i] = pc->data[i + 1];
}
pc->sz--;
printf("删除成功\n");
}
}
//查找联系人
void SearchContact(const Contact* pc) {
assert(pc);
char name[NAMEMAX] = { 0 };
printf("请输入要查找人的名字>");
scanf("%s", name);
int ret = FindByName(pc, name);
if (-1 == ret) {
printf("查无此人\n");
return;
}
else {
//打印信息
printf("%-20s\t%-4s\t%-5s\t%-20s\t%-12s\n", "name", "age", "sex", "address", "telephone");
printf("%-20s\t%-4d\t%-5s\t%-20s\t%-12s\n", pc->data[ret].name,
pc->data[ret].age,
pc->data[ret].sex,
pc->data[ret].addr,
pc->data[ret].tele);
}
}
//修改指定联系人
void ModifyContact(Contact* pc) {
assert(pc);
//先找这个联系人
char name[NAMEMAX] = { 0 };
printf("请输入要修改人的名字>");
scanf("%s", name);
int ret = FindByName(pc, name);
if (-1 == ret) {
printf("查无此人\n");
return;
}
else {
//信息重新录入
printf("请输入名字>");
scanf("%s", pc->data[ret].name);
printf("请输入年龄>");
scanf("%d", &(pc->data[ret].age));
printf("请输入性别>");
scanf("%s", pc->data[ret].sex);
printf("请输入地址>");
scanf("%s", pc->data[ret].addr);
printf("请输入电话>");
scanf("%s", pc->data[ret].tele);
printf("修改完成\n");
}
}
//排序
int ContactSortName(void* e1, void* e2) {
assert(e1 && e2);
return strcmp((Contact*)e1, (Contact*)e2);
}
//清空
void EmptyContact(Contact* pc) {
assert(pc);
pc->sz = 0;
memset(pc->data, 0, sizeof(pc->data));
}
//销毁通讯录内存
void Destory(Contact* pc) {
free(pc->data);
pc->data = NULL;
pc->capacity = 0;
pc->sz = 0;
pc = NULL;
}
//保存通讯录中的信息到文件中
void SaveContact(Contact* pc) {
assert(pc);
//写数据
//打开文件
FILE* pf = fopen("D:\\GITTE chuantimu\\test_1_24(【C进阶】文件操作3)\\contact.txt", "wb");
if (NULL == pf) {
perror("SaveContact::fopen");
return;
}
else {
//写数据
int i = 0;
for (i = 0; i < pc->sz; i++) {
fwrite(&(pc->data[i]), sizeof(PeoInfo), 1, pf);
}
//关闭文件
fclose(pf);
pf = NULL;
printf("保存数据成功\n");
}
}
//加载文件信息到通讯录
void LoadContact(Contact* pc) {
assert(pc);
//读数据
//1.打开文件
FILE* pf = fopen("D:\\GITTE chuantimu\\test_1_24(【C进阶】文件操作3)\\contact.txt", "rb");
if (pf == NULL) {
perror("LoadContact::fopen");
return;
}
else {
//2.读数据
PeoInfo tmp = { 0 };
int i = 0;
while (fread(&tmp, sizeof(PeoInfo), 1, pf)) {
//增容
check_capacity(pc);
pc->data[i] = tmp;
pc->sz++;
i++;
}
fclose(pf);
pf = NULL;
}
}
总结
这次的改编算是比较完美了,但肯定还有美中不足的地方,就是排列可以更加多元化一些,如果仅仅是qsort排序是很方便但也需要体验不同的排序方式,总而言之,这就是最终的通讯录版本了~~
客官,来个三连支持一下吧!!!