之前的博客里我们实现了静态的通讯录。|ू・ω・` )
静态通讯录,适合初学者的手把手一条龙讲解_陈大大陈的博客-CSDN博客
这个版本的通讯录无法实现容量的动态增加,也无法保存我们输入的信息。(•́へ•́╬)
静态通讯录,是直接开辟100块空间内存来供使用,但是这样就会导致问题:如果人太少,要存入的联系人只有10个,造成空间浪费,又或者人太多,需要存进200个人,内存中可存储的空间又会不够。ᕙ༼ ͝°益° ༽ᕗ
每当我们关闭通讯录时,信息随之消失,我们需要再次输入信息,这是非常麻烦且不合理的。
我们今天就来实现可以实现保存信息功能的动态通讯录。
首先是将静态通讯录改写成动态通讯录。
目录
静态通讯录源码
动态通讯录
初始化
动态扩容
信息的输入
数据的删除
动态通讯录源码
可保存信息的动态通讯录
可保存的动态通讯录源码
静态通讯录源码
//contact.h部分#pragma once#define MAX 100#define MAX_NAME 20#define MAX_SEX 5#define MAX_TELE 12#define MAX_ADDR 30#include<stdio.h>#include<string.h>#include<stdlib.h>typedef struct PeoInfo{char name[MAX_NAME];int age;char sex[MAX_SEX];char tele[MAX_TELE];char addr[MAX_ADDR];}PeoInfo;typedef struct Contact{PeoInfo data[MAX];int sz;}Contact;void InitContact(Contact* pc);void AddContact(Contact* pc);void DelContact(Contact* pc);void SortContact(Contact* pc);int SearchContact(Contact* pc);void ModifyContact(Contact* pc); void ShowContact(Contact* pc);int FindByName(Contact* pc, char name[]);//contact.c#include"contact.h"void InitContact(Contact* pc){pc->sz = 0;memset(pc->data, 0, sizeof(pc->data));}void AddContact(Contact* pc){if (pc->sz == 100){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].tele);printf("请输入地址:>");scanf("%s", pc->data[pc->sz].addr);pc->sz++;printf("添加成功\n");}void DelContact(Contact* pc){char name[MAX_NAME];if (pc->sz == 0){printf("通讯录已满\n");return;}printf("输入要删除的人的名字:>");scanf("%s", name);int pos = FindByName(pc, name);if (pos == -1){printf("要删除的人不存在\n");return;}for (int i = pos; i < pc->sz - 1; i++){pc->data[i] = pc->data[i + 1]; }pc->sz--;printf("删除成功\n"); }int FindByName(Contact* pc, char name[]){int i = 0;for (i = 0; i < pc->sz; i++){if (strcmp(pc->data[i].name, name) == 0){return i;break;}}return -1;}int cmp_by_name(const void* e1, const void* e2){return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);}int SearchContact(Contact* pc){char name[12] = { 0 };printf("请输入要查找的人的名字:>");scanf("%s", name);int pos = FindByName(pc, name);if (pos == -1){printf("此人信息不存在\n");} printf("此人信息如下:\n");printf("%-10s %-4s %-5s %-12s %-30s\n", "姓名", "年龄", "性别", "电话", "地址");printf("%-10s %-4d %-5s %-12s %-30s\n", pc->data[pos].name, pc->data[pos].age, pc->data[pos].sex, pc->data[pos].tele, pc->data[pos].addr);return pos;}void ShowContact(Contact* pc){int i = 0;printf("%-10s %-4s %-5s %-12s %-30s\n", "姓名", "年龄", "性别", "电话", "地址");for (i = 0; i < pc->sz; i++){printf("%-10s %-4d %-5s %-12s %-30s\n", pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].tele, pc->data[i].addr);}}void ModifyContact(Contact* pc){printf("输入要修改的人的名字:>");char name[12] = { 0 };scanf("%s", name);int pos = FindByName(pc, name);if (pos == -1){printf("此人不存在\n");return; }printf("请输入名字:>");scanf("%s", pc->data[pos].name);printf("请输入年龄:>");scanf("%d", &pc->data[pos].age);printf("请输入性别:>");scanf("%s", pc->data[pos].sex);printf("请输入电话:>");scanf("%s", pc->data[pos].tele);printf("请输入地址:>");scanf("%s", pc->data[pos].addr);printf("修改成功\n");}void SortContact(Contact* pc){qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp_by_name);}//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("'********** 0.exit ********\n");printf("'****************************************\n");}enum Option{EXIT,ADD,DEL,SEARCH,MODIFY,SHOW,SORT};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);break;case SEARCH:SearchContact(&con);break;case MODIFY:ModifyContact(&con);break;case SHOW:ShowContact(&con);break;case SORT:SortContact(&con);break;case EXIT:printf("退出");break;default:printf("输入错误\n");break;}} while (input);return 0;}
动态通讯录
实现上述的动态增容功能。
要写动态的通讯录,我们可以在结构体中再定义一个表示最大容量的变量。
当通讯录中有效信息的个数和容量相等时,我们就将其扩容,增大分配的内存空间来保存更多信息。并让指针data指向这一空间。
typedef struct Contact{PeoInfo *data;int sz;int capacity;}Contact;
初始化
不再是简单的将开辟的空间置为0。
我们定义两个数字DEFAULT_SZ和INC_SZ,分别是通讯录一开始的空间和每次扩容时增长的空间。
#define DEFAULT_SZ 3#define INC_SZ 2
初始化时,首先开辟大小为 sizeof(PeoInfo) * DEFAULT_SZ的空间。
如果开辟失败就报错,反之则将sz置为0,将容量变为 上面说的DEFAULT_SZ。
void InitContact(Contact* pc){pc->data = (PeoInfo*)malloc(sizeof(PeoInfo) * DEFAULT_SZ);if (pc->data == NULL){printf("通讯录初始化失败:%s\n",strerror(errno));return;}pc->sz = 0;pc->capacity = DEFAULT_SZ;}
动态扩容
当有效数字个数和容量相等时,我们就将其扩容,每次增加INC_SZ*sizeof(PeoInfo)的空间。
扩容成功之后不要忘记将新空间的地址赋给data,这是我踩坑的地方,大家不要也掉进去啊哈哈哈。
扩容成功,容量加2,返回1,失败则返回0,这两个返回值一会我们会用到。
int CheckCapacity(Contact* pc){if (pc->sz == pc->capacity){PeoInfo *ptr = (PeoInfo*)realloc(pc->data,sizeof(PeoInfo) * (pc->capacity + INC_SZ));if (ptr == NULL){printf("CheckCapacity:%s\n",strerror(errno));return 0;}pc->data = ptr;pc->capacity += 2;printf("扩容成功,当前容量%d\n",pc->capacity);return 1;}}
信息的输入
void AddContact(Contact* pc){if (CheckCapacity(pc) == 0){printf("空间不够,扩容失败\n");return;}else{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].tele);printf("请输入地址:>");scanf("%s", pc->data[pc->sz].addr);pc->sz++;printf("添加成功\n");}}
我们用到刚才的返回值,为1时则输入新数据,否则就报错之后返回。
数据的删除
void DestoryContact(Contact* pc){free(pc->data);pc->data = NULL;pc->sz = 0;pc->capacity = 0;printf("释放内存...\n");}
这一块没什么好说的,大家记得free之后将data置为空指针就好。
动态通讯录源码
//contact.h#define MAX 100#define MAX_NAME 20#define MAX_SEX 5#define MAX_TELE 12#define MAX_ADDR 30#define DEFAULT_SZ 3#define INC_SZ 2#include<stdio.h>#include<string.h>#include<stdlib.h>#include<errno.h>typedef struct PeoInfo{char name[MAX_NAME];int age;char sex[MAX_SEX];char tele[MAX_TELE];char addr[MAX_ADDR];}PeoInfo;typedef struct Contact{PeoInfo *data;int sz;int capacity;}Contact;void InitContact(Contact* pc);void AddContact(Contact* pc);void DelContact(Contact* pc);void SortContact(Contact* pc);int SearchContact(Contact* pc);void ModifyContact(Contact* pc);void ShowContact(Contact* pc);int FindByName(Contact* pc, char name[]);void DestoryContact(Contact* pc);//contact.c#include"contact.h"void InitContact(Contact* pc){pc->data = (PeoInfo*)malloc(sizeof(PeoInfo) * DEFAULT_SZ);if (pc->data == NULL){printf("通讯录初始化失败:%s\n",strerror(errno));return;}pc->sz = 0;pc->capacity = DEFAULT_SZ;}int CheckCapacity(Contact* pc){if (pc->sz >= pc->capacity){PeoInfo *ptr = (PeoInfo*)realloc(pc->data,sizeof(PeoInfo) * (pc->capacity + INC_SZ));if (ptr == NULL){printf("CheckCapacity:%s\n",strerror(errno));return 0;}pc->data = ptr;pc->capacity += 2;printf("扩容成功,当前容量%d\n",pc->capacity);return 1;}}void DestoryContact(Contact* pc){free(pc->data);pc->data = NULL;pc->sz = 0;pc->capacity = 0;printf("释放内存...\n");}void AddContact(Contact* pc){if (CheckCapacity(pc) == 0){printf("空间不够,扩容失败\n");return;}else{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].tele);printf("请输入地址:>");scanf("%s", pc->data[pc->sz].addr);pc->sz++;printf("添加成功\n");}}void DelContact(Contact* pc){char name[MAX_NAME];if (pc->sz == 0){printf("通讯录已满\n");return;}printf("输入要删除的人的名字:>");scanf("%s", name);int pos = FindByName(pc, name);if (pos == -1){printf("要删除的人不存在\n");return;}for (int i = pos; i < pc->sz - 1; i++){pc->data[i] = pc->data[i + 1];}pc->sz--;printf("删除成功\n");}int FindByName(Contact* pc, char name[]){int i = 0;for (i = 0; i < pc->sz; i++){if (strcmp(pc->data[i].name, name) == 0){return i;break;}}return -1;}int cmp_by_name(const void* e1, const void* e2){return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);}int SearchContact(Contact* pc){char name[12] = { 0 };printf("请输入要查找的人的名字:>");scanf("%s", name);int pos = FindByName(pc, name);if (pos == -1){printf("此人信息不存在\n");}printf("此人信息如下:\n");printf("%-10s %-4s %-5s %-12s %-30s\n", "姓名", "年龄", "性别", "电话", "地址");printf("%-10s %-4d %-5s %-12s %-30s\n", pc->data[pos].name, pc->data[pos].age, pc->data[pos].sex, pc->data[pos].tele, pc->data[pos].addr);return pos;}void ShowContact(Contact* pc){int i = 0;printf("%-10s %-4s %-5s %-12s %-30s\n", "姓名", "年龄", "性别", "电话", "地址");for (i = 0; i < pc->sz; i++){printf("%-10s %-4d %-5s %-12s %-30s\n", pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].tele, pc->data[i].addr);}}void ModifyContact(Contact* pc){printf("输入要修改的人的名字:>");char name[12] = { 0 };scanf("%s", name);int pos = FindByName(pc, name);if (pos == -1){printf("此人不存在\n");return;}printf("请输入名字:>");scanf("%s", pc->data[pos].name);printf("请输入年龄:>");scanf("%d", &pc->data[pos].age);printf("请输入性别:>");scanf("%s", pc->data[pos].sex);printf("请输入电话:>");scanf("%s", pc->data[pos].tele);printf("请输入地址:>");scanf("%s", pc->data[pos].addr);printf("修改成功\n");}void SortContact(Contact* pc){qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp_by_name);}#pragma once//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("'********** 0.exit ********\n");printf("'****************************************\n");}enum Option{EXIT,ADD,DEL,SEARCH,MODIFY,SHOW,SORT};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);break;case SEARCH:SearchContact(&con);break;case MODIFY:ModifyContact(&con);break;case SHOW:ShowContact(&con);break;case SORT:SortContact(&con);break;case EXIT:DestoryContact(&con);printf("通讯录销毁成功\n");break;default:printf("输入错误\n");break;}} while (input);return 0;}
可保存信息的动态通讯录
这一块的功能涉及到了文件的操作,不了解的朋友们可以看看这篇博客。
写程序必会的C语言文件操作(上)附手绘图详解_陈大大陈的博客-CSDN博客
保存功能的实现
我们用二进制只写的方式来创建一个叫Contact.dat的文件来存放信息。
用二进制输出 函数fwrite将pc->data里的 信息写到文件流里,每次写一个大小为sizeof(struct PeoInfo)的数据。
void SaveContact(Contact* pc){FILE* pf = fopen("Contact.dat", "wb");if (pf == NULL){perror("Condata:fopen:");return;}for (int i = 0; i < pc->sz; i++){fwrite(pc->data+i, sizeof(struct PeoInfo), 1, pf);}fclose(pf);pf = NULL;printf("保存成功...\n");}
运行一次,发现 Contact.dat文件里面出现很多乱码,不过不用担心,这是二进制的表示形式,计算机能读懂就行了!(*^▽^*)
读取功能的实现
我们成功将信息写到了文件里,那怎么确保下次打开文件时能读取信息呢?
当然是反其道而行之,用fread函数来将文件中的信息读取到内存里。
我们定义一个tmp来记录被读取的信息,一次读一个,然后将每一次读取到的值赋给data对应的位置。让sz++。
void LoadContact(Contact* pc){//打开文件FILE*pf=fopen("Contact.dat", "rb");if (pf == NULL){perror("LoadContact:fopen");return;}//读文件PeoInfo tmp;while (fread(&tmp, sizeof(struct PeoInfo), 1, pf)){CheckCapacity(pc);pc->data[pc->sz] = tmp;pc->sz++;}//关闭文件fclose(pf);pf = NULL;}
如图,我们之前输入了9个数据成功保存,再次运行代码发现扩容成功。
展示信息,数据成功保存,保存功能完美实现!(✪ω✪)
可保存的动态通讯录源码
//contact.h#define MAX 100#define MAX_NAME 20#define MAX_SEX 5#define MAX_TELE 12#define MAX_ADDR 30#define DEFAULT_SZ 3#define INC_SZ 2#include<stdio.h>#include<string.h>#include<stdlib.h>#include<errno.h>typedef struct PeoInfo{char name[MAX_NAME];int age;char sex[MAX_SEX];char tele[MAX_TELE];char addr[MAX_ADDR];}PeoInfo;typedef struct Contact{PeoInfo *data;int sz;int capacity;}Contact;void InitContact(Contact* pc);void AddContact(Contact* pc);void DelContact(Contact* pc);void SortContact(Contact* pc);int SearchContact(Contact* pc);void ModifyContact(Contact* pc);void ShowContact(Contact* pc);int FindByName(Contact* pc, char name[]);void DestoryContact(Contact* pc);void SaveContact(Contact* pc);void LoadContact(Contact* pc);//contact.c#include"contact.h"void InitContact(Contact* pc){pc->data = (PeoInfo*)malloc(sizeof(PeoInfo) * DEFAULT_SZ);if (pc->data == NULL){printf("通讯录初始化失败:%s\n",strerror(errno));return;}pc->sz = 0;pc->capacity = DEFAULT_SZ;}int CheckCapacity(Contact* pc){if (pc->sz >= pc->capacity){PeoInfo *ptr = (PeoInfo*)realloc(pc->data,sizeof(PeoInfo) * (pc->capacity + INC_SZ));if (ptr == NULL){printf("CheckCapacity:%s\n",strerror(errno));return 0;}pc->data = ptr;pc->capacity += 2;printf("扩容成功,当前容量%d\n",pc->capacity);return 1;}}void DestoryContact(Contact* pc){free(pc->data);pc->data = NULL;pc->sz = 0;pc->capacity = 0;printf("释放内存...\n");}void AddContact(Contact* pc){if (CheckCapacity(pc) == 0){printf("空间不够,扩容失败\n");return;}else{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].tele);printf("请输入地址:>");scanf("%s", pc->data[pc->sz].addr);pc->sz++;printf("添加成功\n");}}void DelContact(Contact* pc){char name[MAX_NAME];if (pc->sz == 0){printf("通讯录已满\n");return;}printf("输入要删除的人的名字:>");scanf("%s", name);int pos = FindByName(pc, name);if (pos == -1){printf("要删除的人不存在\n");return;}for (int i = pos; i < pc->sz - 1; i++){pc->data[i] = pc->data[i + 1];}pc->sz--;printf("删除成功\n");}int FindByName(Contact* pc, char name[]){int i = 0;for (i = 0; i < pc->sz; i++){if (strcmp(pc->data[i].name, name) == 0){return i;break;}}return -1;}int cmp_by_name(const void* e1, const void* e2){return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);}int SearchContact(Contact* pc){char name[12] = { 0 };printf("请输入要查找的人的名字:>");scanf("%s", name);int pos = FindByName(pc, name);if (pos == -1){printf("此人信息不存在\n");}printf("此人信息如下:\n");printf("%-10s %-4s %-5s %-12s %-30s\n", "姓名", "年龄", "性别", "电话", "地址");printf("%-10s %-4d %-5s %-12s %-30s\n", pc->data[pos].name, pc->data[pos].age, pc->data[pos].sex, pc->data[pos].tele, pc->data[pos].addr);return pos;}void ShowContact(Contact* pc){int i = 0;printf("%-10s %-4s %-5s %-12s %-30s\n", "姓名", "年龄", "性别", "电话", "地址");for (i = 0; i < pc->sz; i++){printf("%-10s %-4d %-5s %-12s %-30s\n", pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].tele, pc->data[i].addr);}}void ModifyContact(Contact* pc){printf("输入要修改的人的名字:>");char name[12] = { 0 };scanf("%s", name);int pos = FindByName(pc, name);if (pos == -1){printf("此人不存在\n");return;}printf("请输入名字:>");scanf("%s", pc->data[pos].name);printf("请输入年龄:>");scanf("%d", &pc->data[pos].age);printf("请输入性别:>");scanf("%s", pc->data[pos].sex);printf("请输入电话:>");scanf("%s", pc->data[pos].tele);printf("请输入地址:>");scanf("%s", pc->data[pos].addr);printf("修改成功\n");}void SortContact(Contact* pc){qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp_by_name);}void SaveContact(Contact* pc){FILE* pf = fopen("Contact.dat", "wb");if (pf == NULL){perror("Condata:fopen:");return;}for (int i = 0; i < pc->sz; i++){fwrite(pc->data+i, sizeof(struct PeoInfo), 1, pf);}fclose(pf);pf = NULL;printf("保存成功...\n");}void LoadContact(Contact* pc){//打开文件FILE*pf=fopen("Contact.dat", "rb");if (pf == NULL){perror("LoadContact:fopen");return;}//读文件PeoInfo tmp;while (fread(&tmp, sizeof(struct PeoInfo), 1, pf)){CheckCapacity(pc);pc->data[pc->sz] = tmp;pc->sz++;}//关闭文件fclose(pf);pf = NULL;}#pragma once//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("'********** 0.exit ********\n");printf("'****************************************\n");}enum Option{EXIT,ADD,DEL,SEARCH,MODIFY,SHOW,SORT};int main(){int input = 0;Contact con;InitContact(&con); LoadContact(&con);do{menu();printf("请选择:>");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);DestoryContact(&con);printf("通讯录销毁成功\n");break;default:printf("输入错误\n");break;}} while (input);return 0;}
这篇博客旨在总结我自己阶段性的学习,要是能帮助到大家,那可真是三生有幸!?如果觉得我写的不错的话还请点个赞和关注哦~我会持续输出编程的知识的!???