前言:
前面我们学过了结构体、枚举等自定义类型的学习,写了一个静态版本的通讯录【传送门】点击进入静态版通讯录界面,我们知道,我们写的通讯录其实本质用的是一个结构体数组,数组的大小是固定的,这时我们就可以使用动态内存管理相关知识,对内存空间进行一个合理的分配,而不至于一下开辟一个较大的数组空间,用的时候却很少,造成了内存浪费的问题等。如果在内存不够时,也可以进行申请一定的空间来存储数据。
一、静态版本代码实现:
这里我就直接将完整代码放过来,具体分析,可以点击传送门链接,看实现思路哦~~
test.c文件代码(测试通讯录的相关功能)
#include "contact.h" void Menu() { printf("********************************\n"); printf("****** 0.exit 1.add ******\n"); printf("****** 2.delete 3.modify******\n"); printf("****** 4.search 5.show *****\n"); printf("****** 6.sort *****\n"); printf("********************************\n"); } enum Option { EXIT, ADD, DELETE, MODIFY, SEARCH, SHOW, SORT }; int main() { Contact con;//创建一个名为con的结构体变量 InitContact(&con); int input = 0; do { Menu(); printf("请选择:>"); scanf("%d", &input); switch (input) { case ADD: AddContact(&con); break; case DELETE: DeleteContact(&con); break; case MODIFY: ModifyContact(&con); break; case SEARCH: SearchContact(&con); break; case SHOW: ShowContact(&con); break; case SORT: SortContact(&con); break; case EXIT: printf("退出通讯录\n"); break; default: printf("输入有误,请重新输入\n"); break; } } while (input); }
contact.h文件(用来声明函数、结构体变量)
#include<stdio.h> #include<string.h> #include<stdlib.h> #include<errno.h> #define MAX_NAME 20 #define MAX_SEX 5 #define MAX_TELE 15 #define MAX_ADDR 20 #define MAX 20 //创建个人信息 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 ShowContact(const Contact* pc); //删除指定联系人 void DeleteContact(Contact* pc); //查找指定联系人 void SearchContact(const Contact* pc); //修改指定联系人 void ModifyContact(Contact* pc); //排序通讯录 void SortContact(Contact* pc);
contact.c文件(用来具体实现函数内部的功能)
#define _CRT_SECURE_NO_WARNINGS #include"contact.h" //静态版本 //初始化通讯录 void InitContact(Contact* pc) { pc->sz = 0; memset(pc->data, 0, sizeof(pc->data)); } //添加联系人 void AddContact(Contact* pc) { //CheckContact(pc); if (pc->data == pc->sz) { 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); printf("添加成功\n"); pc->sz++; } //显示通讯录 void ShowContact(const Contact* pc) { printf("%-15s %-5s %-5s %-15s %-20s\n","姓名", "年龄", "性别", "电话", "地址"); int i = 0; for (i = 0; i < pc->sz; i++) { printf("%-15s %-5d %-5s %-15s %-20s\n", pc->data[i].name, pc->data[i].age, pc->data[i].sex,pc->data[i].tele, pc->data[i].addr); } } //查找姓名 static int Findname(Contact* pc, char name[]) { int i = 0; for (i = 0; i < pc->sz; i++) { if (0 == strcmp(pc->data[i].name, name)) { return i;//找到返回i } } return -1;//没找到返回-1 } //删除指定联系人 void DeleteContact(Contact* pc) { if (0 == pc->sz) { printf("通讯录为空,无法删除联系人\n"); return; } printf("请输入姓名:>"); char name[MAX_NAME] = { 0 }; scanf("%s",name); //查找姓名 int pos = Findname(pc,name); //删除 if (-1 == pos) { printf("联系人不存在\n"); return; } int i = 0; for (i = pos; i < pc->sz; i++) { pc->data[pos] = pc->data[pos + 1]; } printf("删除成功\n"); pc->sz--; } //查找指定联系人 void SearchContact(const Contact* pc) { char name[MAX_NAME] = { 0 }; printf("请输入姓名:>"); scanf("%s", name); int pos = Findname(pc, name); if (-1 == pos) { printf("联系人不存在\n"); return; } printf("%-15s %-5s %-5s %-15s %-20s\n", "姓名", "年龄", "性别", "电话", "地址"); printf("%-15s %-5d %-5s %-15s %-20s\n", pc->data[pos].name, pc->data[pos].age, pc->data[pos].sex, pc->data[pos].tele, pc->data[pos].addr); } //修改指定联系人 void ModifyContact(Contact* pc) { printf("请输入要修改人的姓名:>"); char name[MAX_NAME] = { 0 }; scanf("%s", name); int pos = Findname(pc, name); if (-1 == pos) { 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"); } //通过名字来排序 int cmp_by_name(const void* e1,const void* e2) { return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name); } //排序通讯录 void SortContact(Contact* pc) { qsort(pc->data, pc->sz,sizeof(PeoInfo),cmp_by_name); printf("排序成功\n"); }
二、动态通讯录
我们将动态的版本的通讯录修改为:默认能够存放3个人的信息,不够的话,每次增加2个人的信息。
在contact.h文件中,我们宏定义 DEFAULT_SZ 表示 默认3个人的信息
INC_SZ 表示 容量不够时,每次增加2个人的信息
#define DEFAULT_SZ 3 #define INC_SZ 2
在contact.h文件中。声明结构体部分:我们要让动态申请的内存空间可大可小,一定需要malloc函数来申请空间,所以我们应该将结构体数组修改为一个结构体指针data,指针类型是PeoInfo*,data指向了存放数据的空间,定义sz变量用来记录当前通讯录中的信息个数,capacity变量用来记录通讯录的容量。
//动态版本的通讯录 typedef struct Contact { PeoInfo* data;//data指向了存放数据的空间 int sz;//记录通讯录中的有效信息个数 int capacity;//记录通讯录的容量 }Contact;
在contact.c文件中,初始化通讯录函数里面的部分就需要大改了,我们需要首先开辟默认3个元素大小的空间,元素类型是PeoInfo* ,即malloc的参数为DEFAULT_SZ*sizeof(PeoInfo),
我们拿一个ptr的结构体指针接收,如果ptr为NULL,就打印报错的信息(strerror需要引用<string.h>头文件,errno需要引用<errno.h>),然后返回空,不为NULL的话,就将ptr赋给data,元素的起始地址就为data,sz初始化为0,capacity容量初始化为默认值。
//静态版本 初始化通讯录 //void InitContact(Contact* pc) //{ // pc->sz = 0; // memset(pc->data, 0, sizeof(pc->data)); // //} //动态版本 //初始化通讯录 void InitContact(Contact* pc) { PeoInfo* ptr= (PeoInfo* )malloc(DEFAULT_SZ * sizeof(PeoInfo)); if (ptr == NULL) { printf("通讯录初始化失败::%s", strerror(errno)); return; } else { pc->data = ptr; pc->sz = 0; pc->capacity = DEFAULT_SZ; } }
在contact.c文件中,添加联系人函数里面我们也需要进行大幅度改动。pc->data这里就不是静态版本的数组了,我们直接将这个if语句给屏蔽掉,直接写一个CheckContact函数用来判断通讯录是否满了?是否需要扩容?
//添加联系人 void AddContact(Contact* pc) { CheckContact(pc); /*if (pc->data == pc->sz) { 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); printf("添加成功\n"); pc->sz++; }
CheckContact函数
如果pc->sz 等于 pc->capacity ,即通讯录当前有效个人信息等于目前的容量时,说明通讯录已经满了,就需要扩容,此时就可以用realloc函数来扩容,pc->data表示通讯录的起始地址,新的大小为新的元素个数*每个元素的大小。返回值暂存在ptr指针之中,如果ptr为NULL,则打印报错的信息,不为NULL,则将ptr赋值给pc->data,容量+2,为了显示扩容效果,我们顺便打印当前容量值。
//检测通讯录是否满了 void CheckContact(Contact* pc) { //如果通讯录满了 就增容 if (pc->sz == pc->capacity) { PeoInfo* ptr= (PeoInfo*)realloc(pc->data,(pc->capacity+ INC_SZ)*sizeof(PeoInfo)); if (ptr == NULL) { printf("CheckContact::%s", strerror(errno)); return; } else { pc->data = ptr; pc->capacity += INC_SZ; printf("增容成功,当前容量为:%d\n", pc->capacity); } } //没满直接跳过以上语句 }
最后,我们退出通讯录的时候,也可以将自己申请的内存空间给释放掉。我们在contact.h文件中声明一个DestroyContact函数表示销毁通讯录。
在contact.c文件下面接着编写代码:
//销毁通讯录 void DestroyContact(Contact* pc) { free(pc->data); pc->data = NULL; pc->sz = 0; pc->capacity = 0; printf("释放成功...\n"); }
代码测试:
为了测试方便,作者直接将信息用数字测试,当我们输入三个有效信息之后,此时有效空间等于当前的通讯录容量了,当我们再次选择1,继续添加联系人,就“显示增容成功,当前容量为5”。
三、代码整理
将代码整理在下面,需要的自取呐~
test.c文件
#include "contact.h" void Menu() { printf("********************************\n"); printf("****** 0.exit 1.add ******\n"); printf("****** 2.delete 3.modify******\n"); printf("****** 4.search 5.show *****\n"); printf("****** 6.sort *****\n"); printf("********************************\n"); } enum Option { EXIT, ADD, DELETE, MODIFY, SEARCH, SHOW, SORT }; int main() { Contact con;//创建一个名为con的结构体变量 InitContact(&con); int input = 0; do { Menu(); printf("请选择:>"); scanf("%d", &input); switch (input) { case ADD: AddContact(&con); break; case DELETE: DeleteContact(&con); break; case MODIFY: ModifyContact(&con); break; case SEARCH: SearchContact(&con); break; case SHOW: ShowContact(&con); break; case SORT: SortContact(&con); break; case EXIT: DestroyContact(&con); printf("退出通讯录\n"); break; default: printf("输入有误,请重新输入\n"); break; } } while (input); }
contact.h文件
#include<stdio.h> #include<string.h> #include<stdlib.h> #include<errno.h> #define MAX_NAME 20 #define MAX_SEX 5 #define MAX_TELE 15 #define MAX_ADDR 20 //#define MAX 20 #define DEFAULT_SZ 3 #define INC_SZ 2 //创建个人信息 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; //动态版本的通讯录 typedef struct Contact { PeoInfo* data;//data指向了存放数据的空间 int sz;//记录通讯录中的有效信息个数 int capacity;//记录通讯录的容量 }Contact; //初始化结构体 void InitContact(Contact* pc); //添加联系人 void AddContact(Contact* pc); //显示通讯录 void ShowContact(const Contact* pc); //删除指定联系人 void DeleteContact(Contact* pc); //查找指定联系人 void SearchContact(const Contact* pc); //修改指定联系人 void ModifyContact(Contact* pc); //排序通讯录 void SortContact(Contact* pc); //销毁通讯录 void DestroyContact(Contact* pc);
contact.c文件
#include"contact.h" //静态版本 初始化通讯录 //void InitContact(Contact* pc) //{ // pc->sz = 0; // memset(pc->data, 0, sizeof(pc->data)); // //} //动态版本 //初始化通讯录 void InitContact(Contact* pc) { PeoInfo* ptr= (PeoInfo* )malloc(DEFAULT_SZ * sizeof(PeoInfo)); if (ptr == NULL) { printf("通讯录初始化失败::%s", strerror(errno)); return; } else { pc->data = ptr; pc->sz = 0; pc->capacity = DEFAULT_SZ; } } //检测通讯录是否满了 void CheckContact(Contact* pc) { //如果通讯录满了 就增容 if (pc->sz == pc->capacity) { PeoInfo* ptr= (PeoInfo*)realloc(pc->data,(pc->capacity+ INC_SZ)*sizeof(PeoInfo)); if (ptr == NULL) { printf("CheckContact::%s", strerror(errno)); return; } else { pc->data = ptr; pc->capacity += INC_SZ; printf("增容成功,当前容量为:%d\n", pc->capacity); } } //没满直接跳过以上语句 } //添加联系人 void AddContact(Contact* pc) { CheckContact(pc); /*if (pc->data == pc->sz) { 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); printf("添加成功\n"); pc->sz++; } //显示通讯录 void ShowContact(const Contact* pc) { printf("%-15s %-5s %-5s %-15s %-20s\n","姓名", "年龄", "性别", "电话", "地址"); int i = 0; for (i = 0; i < pc->sz; i++) { printf("%-15s %-5d %-5s %-15s %-20s\n", pc->data[i].name, pc->data[i].age, pc->data[i].sex,pc->data[i].tele, pc->data[i].addr); } } //查找姓名= static int Findname(Contact* pc, char name[]) { int i = 0; for (i = 0; i < pc->sz; i++) { if (0 == strcmp(pc->data[i].name, name)) { return i;//找到返回i } } return -1;//没找到返回-1 } //删除指定联系人 void DeleteContact(Contact* pc) { if (0 == pc->sz) { printf("通讯录为空,无法删除联系人\n"); return; } printf("请输入姓名:>"); char name[MAX_NAME] = { 0 }; scanf("%s",name); //查找姓名 int pos = Findname(pc,name); //删除 if (-1 == pos) { printf("联系人不存在\n"); return; } int i = 0; for (i = pos; i < pc->sz; i++) { pc->data[pos] = pc->data[pos + 1]; } printf("删除成功\n"); pc->sz--; } //查找指定联系人 void SearchContact(const Contact* pc) { char name[MAX_NAME] = { 0 }; printf("请输入姓名:>"); scanf("%s", name); int pos = Findname(pc, name); if (-1 == pos) { printf("联系人不存在\n"); return; } printf("%-15s %-5s %-5s %-15s %-20s\n", "姓名", "年龄", "性别", "电话", "地址"); printf("%-15s %-5d %-5s %-15s %-20s\n", pc->data[pos].name, pc->data[pos].age, pc->data[pos].sex, pc->data[pos].tele, pc->data[pos].addr); } //修改指定联系人 void ModifyContact(Contact* pc) { printf("请输入要修改人的姓名:>"); char name[MAX_NAME] = { 0 }; scanf("%s", name); int pos = Findname(pc, name); if (-1 == pos) { 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"); } //通过名字来排序 int cmp_by_name(const void* e1,const void* e2) { return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name); } //排序通讯录 void SortContact(Contact* pc) { qsort(pc->data, pc->sz,sizeof(PeoInfo),cmp_by_name); printf("排序成功\n"); } //销毁通讯录 void DestroyContact(Contact* pc) { free(pc->data); pc->data = NULL; printf("释放成功...\n"); }