我们前面已经学习完 动态内存管理 和 C语言操作文件,相信你对内存的管理和操作有了更加深入的了解。那我们今天接着来优化一下前面我们写过的通讯录。
- 特别提醒:函数如果放在后面,在使用函数前面必须声明!!
动态内存管理
使用动态内存相关的知识优化通讯录。主要是从增加内存的方面去优化。我们设置为以下规则,注意这种设置仅仅为了方便讲解和测试仅此而已。后期大家可以设置为自己想要的规则。
- 通讯录刚刚开始可以存放3个人的信息。
- 通讯录放满,可以增加容量,每次增加2个人的信息的空间。
通讯录声明
静态版本
//通讯录---存放结构体的数组--每个结构体就是一个人的信息 typedef struct Contact { PeoInfo data[MAX];//存放数据 int sz;//记录当前通讯录中存放的人的信息个数 }Contact;
动态版本
//动态版本 typedef struct Contact { PeoInfo* data;//存放数据,可修改数组内存的空间了 //🆗PeoInfo结构体类型的指针 int sz;//记录当前通讯录中存放的人的信息个数 int capacity;//记录的是通讯录的当前容量 }Contact;
- 当通讯录中存放的人的信息个数 sz == 通讯录当前容量 capacity就可以考虑增加容量问题。
- 目前的data是没有空间的,需要在初始化的时候在堆区开辟3个人的信息空间。
- PeoInfo* data 存放数据,可修改数组内存的空间了,🆗PeoInfo结构体类型的指针
- PeoInfo* data 指针维护开辟的空间
初始化通讯录
静态版本
void InitContact(Contact* pc) { assert(pc); pc->sz = 0; memset(pc->data, 0, sizeof(pc->data)); }
动态版本
#define DEFAULT_SZ 3 //后期容易修改
void InitContact(Contact* pc) { assert(pc); pc->sz = 0; pc->capacity = DEFAULT_SZ; pc->data = calloc(pc->capacity ,sizeof(PeoInfo)); if (pc->data == NULL) { perror("InitContact->calloc"); return; } }
- PeoInfo* data 指针维护开辟的空间
Add增加通讯录
静态版本
void AddContact(Contact* pc) { assert(pc); printf("请输入名字\n"); scanf("%s", pc->data[pc->sz].name);//name是数组名,不用& printf("请输入年龄\n"); scanf("%d", &(pc->data[pc->sz].age));// printf("请输入性别\n"); scanf("%s", pc->data[pc->sz].sex); printf("请输入电话\n"); scanf("%s", pc->data[pc->sz].tele); printf("请输入地址\n"); scanf("%s", pc->data[pc->sz].addr); // pc->sz++; printf("增加成功\n"); }
动态版本
#define DEFAULT_INC 2//后期容易修改
//动态版本 void AddContact(Contact* pc) { assert(pc); //满了增加容量 if (pc->sz == pc->capacity) { PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + DEFAULT_INC) * sizeof(PeoInfo)); if (ptr != NULL) { pc->data = ptr; } else { perror("AddContact->realloc"); return; } } //不需要增容填写人信息 printf("请输入名字\n"); scanf("%s", pc->data[pc->sz].name);//name是数组名,不用& printf("请输入年龄\n"); scanf("%d", &(pc->data[pc->sz].age));// printf("请输入性别\n"); scanf("%s", pc->data[pc->sz].sex); printf("请输入电话\n"); scanf("%s", pc->data[pc->sz].tele); printf("请输入地址\n"); scanf("%s", pc->data[pc->sz].addr); // pc->sz++; printf("增加成功\n"); }
- 当通讯录中存放的人的信息个数 sz == 通讯录当前容量 capacity就可以考虑增加容量问题。
当然也可以封装成函数Checkcapacity
Checkcapacity增容
Checkcapacity(pc);
void Checkcapacity(Contact* pc) { if (pc->sz == pc->capacity) { PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + DEFAULT_INC) * sizeof(PeoInfo)); if (ptr != NULL) { pc->data = ptr; } else { perror("AddContact->realloc"); return; } } }
当然我们使用了动态内存的空间,我们必须手动释放和销毁,我们封装一个函数去销毁。
DestroyContact释放动态空间
void DestroyContact(Contact* pc);
case EXIT: DestroyContact(&con); printf("退出通讯录\n"); break;
void DestroyContact(Contact* pc) { free(pc->data); pc->data = NULL; pc->sz = 0; pc->capacity = 0; }
文件操作
程序退出之后,输入的信息都丢了。所以在通讯录之前最好把我们输入的信息全部保存到文件里。
SaveContact保存信息到文件中
//保存信息到文件 void SaveContact(Contact* pc);
case EXIT: //保存通讯录中的数据到文件中 SaveContact(&con); DestroyContact(&con); printf("退出通讯录\n"); break;
void SaveContact(Contact* pc) { //打开文件 FILE* pf = fopen("contact.txt", "wb");//wb以二进制的方式写数据到文件 if (pf == NULL) { perror("SaveContact"); return; } //写信息到文件 int i = 0;//一个数据一个数据写 for (i = 0; i < pc->sz; i++) { fwrite(&(pc->data[i]), sizeof(PeoInfo), 1, pf); fwrite(pc->data+i, sizeof(PeoInfo), 1, pf); } //关闭文件 fclose(pf); pf = NULL; }
- 字符以ASCII码和二进制的存储的方式相同
- 整型以ASCII码和二进制的存储的方式不同
初始化通讯录
但是当程序再次跑起来的时候,还是看不到信息??所以我们需要在执行通讯录等一系列的功能之前我们就要从文件里读取到数据信息。我们去修改初始化部分
旧版本
void InitContact(Contact* pc) { assert(pc); pc->sz = 0; pc->capacity = DEFAULT_SZ; pc->data = calloc(pc->capacity * sizeof(PeoInfo)); if (pc->data == NULL) { perror("InitContact->calloc"); return; } }
文件版本
//文件版本 void InitContact(Contact* pc) { assert(pc); pc->sz = 0; pc->capacity = DEFAULT_SZ; pc->data = calloc(pc->capacity * sizeof(PeoInfo)); if (pc->data == NULL) { perror("InitContact->calloc"); return; } LoadContact(pc); }
LoadContact加载文件信息到通讯录
//加载文件信息到通讯录 void LoadContact(Contact* pc);
void Checkcapacity(Contact* pc);//声明 void LoadContact(Contact* pc) { //打开文件 FILE* pf = fopen("contact.txt", "rb");//以二进制的形式读 if (pf == NULL) { perror("LoadContact"); return; } //读文件 PeoInfo tmp = { 0 };//创建临时变量,结构体PeoInfo类型 while (fread(&tmp, sizeof(PeoInfo), 1, pf))//读到就返回1,没有读到就返回0,一个一个读直到没有读到为0跳出循环 { Checkcapacity(pc);//若文件中有5个人,但是这里只有3个人的容量,首先判断需不需要增容 pc->data[pc->sz] = tmp; pc->sz++; } //关闭文件 fclose(pf); pf = NULL; }
test.c
#define _CRT_SECURE_NO_WARNINGS 1 #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("********0.exit ***********\n"); printf("************************************\n"); } enum Option { EXIT,//0 ADD,//1 DEL,//2 SEARCH,//3 MODIFY,//4 SHOW,//5 SORT,//6 }; int main() { int input = 0; //创建通讯录 Contact con; //初始化通讯录 InitContact(&con); do { menu(); printf("请输入你的选择:>\n"); scanf("%d", &input); switch (input)//这里为了代码更加清晰用枚举常量 { case ADD: AddContact(&con); break; case DEL: DelContact(&con); break; case SEARCH: SearchContact(&con); break; case MODIFY: ModifyContact(&con); break; case SHOW: ShowContact(&con); break; case SORT: //SortContact(&con); break; case EXIT: //保存通讯录中的数据到文件中 SaveContact(&con); DestroyContact(&con); printf("退出通讯录\n"); break; default: printf("选择错误,请重新选择:>\n"); break; } } while (input); return 0; }
contact.h
#pragma once #include<stdio.h> #include<string.h> #include<assert.h> #define NAME_MAX 20 #define SEX_MAX 20 #define TELE_MAX 20 #define ADDR_MAX 30 #define MAX 100 #define DEFAULT_SZ 3 #define DEFAULT_INC 2 typedef struct PeoInfo { char name[NAME_MAX]; int age; char sex[SEX_MAX]; char tele[TELE_MAX]; char addr[ADDR_MAX]; }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(Contact* pc); //删除个人信息 void DelContact(Contact *pc); //查找个人信息 void SearchContact(Contact* pc); //修改个人信息 void ModifyContact(Contact* pc); //排序 //void SortContact(Contact* pc); //释放空间 void DestroyContact(Contact* pc); //保存信息到文件 void SaveContact(Contact* pc); //加载文件信息到通讯录 void LoadContact(Contact* pc);
contact.c
#define _CRT_SECURE_NO_WARNINGS 1 #include"contact.h" //初始化静态版本 //void InitContact(Contact* pc) //{ // assert(pc); // pc->sz = 0; // memset(pc->data, 0, sizeof(pc->data)); //} //动态版本 //void InitContact(Contact* pc) //{ // assert(pc); // pc->sz = 0; // pc->capacity = DEFAULT_SZ; // pc->data = calloc(pc->capacity * sizeof(PeoInfo)); // if (pc->data == NULL) // { // perror("InitContact->calloc"); // return; // } //} //文件版本 void Checkcapacity(Contact* pc);//声明 void LoadContact(Contact* pc) { //打开文件 FILE* pf = fopen("contact.txt", "rb");//以二进制的形式读 if (pf == NULL) { perror("LoadContact"); return; } //读文件 PeoInfo tmp = { 0 }; while (fread(&tmp, sizeof(PeoInfo), 1, pf)) { Checkcapacity(pc); pc->data[pc->sz] = tmp; pc->sz++; } //关闭文件 fclose(pf); pf = NULL; } void InitContact(Contact* pc) { assert(pc); pc->sz = 0; pc->capacity = DEFAULT_SZ; pc->data = calloc(pc->capacity * sizeof(PeoInfo)); if (pc->data == NULL) { perror("InitContact->calloc"); return; } LoadContact(pc); } //增加静态版本 //void AddContact(Contact* pc) //{ // assert(pc); // printf("请输入名字\n"); // scanf("%s", pc->data[pc->sz].name);//name是数组名,不用& // printf("请输入年龄\n"); // scanf("%d", &(pc->data[pc->sz].age));// // printf("请输入性别\n"); // scanf("%s", pc->data[pc->sz].sex); // printf("请输入电话\n"); // scanf("%s", pc->data[pc->sz].tele); // printf("请输入地址\n"); // scanf("%s", pc->data[pc->sz].addr); // // // pc->sz++; // printf("增加成功\n"); //} //动态版本 void Checkcapacity(Contact* pc) { if (pc->sz == pc->capacity) { PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + 2) * sizeof(PeoInfo)); if (ptr != NULL) { pc->data = ptr; } else { perror("AddContact->realloc"); return; } } } void AddContact(Contact* pc) { assert(pc); //满了增加容量 Checkcapacity(pc); //不需要增容填写人信息 printf("请输入名字\n"); scanf("%s", pc->data[pc->sz].name);//name是数组名,不用& printf("请输入年龄\n"); scanf("%d", &(pc->data[pc->sz].age));// printf("请输入性别\n"); scanf("%s", pc->data[pc->sz].sex); printf("请输入电话\n"); scanf("%s", pc->data[pc->sz].tele); printf("请输入地址\n"); scanf("%s", pc->data[pc->sz].addr); // pc->sz++; printf("增加成功\n"); } //展示 void ShowContact(const Contact* pc) { assert(pc); if (pc->sz == 0) { printf("通讯录为空,无需打印\n"); return 0; } int i = 0; //名字 年龄 性别 电话 地址 //-左对齐 //20是需要20字符的位置来放名字 printf("%-20s%-5s%-5s%-12s%-30s\n", "姓名", "年龄", "性别", "电话", "地址"); for (i = 0; i < pc->sz; i++) { printf("%-20s%-5d%-5s%-12s%-30s\n", pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].tele, pc->data[i].addr); } } //删除,查找,修改都需要使用 int FindByName(const Contact* pc, char name[]) { assert(pc); int i = 0; for (i = 0; i < pc->sz; i++) { if (strcmp(pc->data[i].name, name) == 0) { return i; } } return -1; } //删除 void DelContact(Contact* pc) { char name[NAME_MAX];// assert(pc); if (pc->sz == 0) { printf("通讯录为空,无法删除\n"); return; } printf("输入要删除的名字:>"); scanf("%s", name); int ret = FindByName(pc, name); if (ret == -1) { printf("要删除的人不存在\n"); return; } //存在返回这个人的所在data的下标放入ret int i = 0; for (i = ret; i < pc -> sz - 1; i++) { pc->data[i] = pc->data[i + 1]; } pc->sz--; printf("删除成功\n"); } //查找 void SearchContact(Contact* pc) { char name[NAME_MAX]; assert(pc); if (pc->sz == 0) { printf("通讯录为空,无法查找\n"); return; } //查找 printf("输入要查找的名字:>"); scanf("%s", name); int ret = FindByName(pc, name); if (ret == -1) { printf("要查找的人不存在\n"); return; } //返回下标显示这个的信息🆗 printf("%-20s%-5s%-5s%-12s%-30s\n", "姓名", "年龄", "性别", "电话", "地址"); printf("%-20s%-5d%-5s%-12s%-30s\n", pc->data[ret].name, pc->data[ret].age, pc->data[ret].sex, pc->data[ret].tele, pc->data[ret].addr); } //修改 void ModifyContact(Contact* pc) { char name[NAME_MAX]; assert(pc); if (pc->sz == 0) { printf("通讯录为空,无法修改\n"); return; } //查找 printf("输入要修改的名字:>"); scanf("%s", name); int ret = FindByName(pc, name); if (ret == -1) { printf("要修改的人不存在\n"); return; } //修改 assert(pc); printf("请输入名字\n"); scanf("%s", pc->data[ret].name);//name是数组名,不用& printf("请输入年龄\n"); scanf("%d", &(pc->data[ret].age));// printf("请输入性别\n"); scanf("%s", pc->data[ret].sex); printf("请输入电话\n"); scanf("%s", pc->data[ret].tele); printf("请输入地址\n"); scanf("%s", pc->data[ret].addr); printf("修改成功\n"); } //释放动态空间 void DestroyContact(Contact* pc) { free(pc->data); pc->data = NULL; pc->sz = 0; pc->capacity = 0; } //写函数到文件中 void SaveContact(Contact* pc) { //打开文件 FILE* pf = fopen("contact.txt", "wb");//wb以二进制的方式写数据到文件 if (pf == NULL) { perror("SaveContact"); return; } //写信息到文件 int i = 0;//一个数据一个数据写 for (i = 0; i < pc->sz; i++) { fwrite(&(pc->data[i]), sizeof(PeoInfo), 1, pf); fwrite(pc->data+i, sizeof(PeoInfo), 1, pf); } //关闭文件 fclose(pf); pf = NULL; }
✔✔✔✔✔最后,感谢大家的阅读,若有错误和不足,欢迎指正!
接下来的博文会更新一些练习题,到实践中去加深对知识的理解。C语言的基本学习就快结束了,还是要多加练习。🆗
代码----------→【gitee:唐棣棣 (TSQXG) - Gitee.com】
联系----------→ 【邮箱:2784139418@qq.com】