一.动态增容版简介
上篇博客我们一起用C语言实现了一个固定大小的通讯录程序,这篇博客里我们将借助动态内存相关知识把他改造成可以动态增加大小的版本.
二.动态增容版逐步实现详解
1.创建通讯录
创建部分与静态版不同的是,因为我们在通讯录成员个数扩容的时候需要有一个变量来记录当前通讯录的最大容量,因此我们在结构体中多创建一个变量capacity用来记录当前通讯录的最大容量.
该部分代码如下:
//动态版本 #define DEFAULT_SZ 3 #define INC_SZ 2 //人的信息-结构体 typedef struct PeoInfo { char name[NAME_MAX]; int age; char sex[SEX_MAX]; char addr[ADDR_MAX]; char tele[TELE_MAX];//电话11位,留一位给'\0'; }PeoInfo; //动态版本 typedef struct Contact { PeoInfo* data;//存放人的信息 int sz;//用来记录当前已经存放的信息的个数 int capacity;//记录当前通讯录的最大容量 }Contact;
2. 初始化通讯录
初始化的部分我们可以将原来的初始化方法替换成使用calloc函数动态开辟通讯录的初始大小的同时进行初始化.
//初始化通讯录成员(动态版) void InitContact(Contact* pc) { assert(pc); pc->sz = 0; PeoInfo * ptr = (PeoInfo*)calloc(DEFAULT_SZ , sizeof(PeoInfo)); if (ptr == NULL) { perror("InitContact::calloc"); return; } pc->data = ptr; pc->capacity = DEFAULT_SZ; }
3.增加联系人
增加联系人的部分则需要先判断通讯录当前是否满了,如果是,则转入扩容函数,如果不是则正常进行增加联系人操作.
//增加联系人(动态版) 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); //每次录入完,sz自增 pc->sz++; printf("联系人添加成功:>\n"); }
4.通讯录增容
通讯录的增容部分使用realloc函数来实现,当然要记得检查realloc返回的指针是否为空,如果为空则要输出内容提醒程序员出错的原因.
void check_capacity(Contact* pc) { if (pc->sz == pc->capacity) { //增容 PeoInfo*ptr=(PeoInfo*)realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(PeoInfo)); if (ptr == NULL) { perror("check_capacity::realloc"); return; } pc->data = ptr; pc->capacity += INC_SZ; } }
5.销毁通讯录
销毁通讯录是动态内存开辟非常重要的一步,如果使用动态内存开辟后忘记销毁,就会造成内存泄漏.因此在程序结束退出时我们一定要记得使用free将前面动态开辟的空间释放掉.
//销毁通讯录 void DestroyContact(Contact* pc) { free(pc->data); pc->data = NULL; pc->capacity = 0; pc->sz = 0; pc = NULL; }
三.动态增容版代码整合
我们将程序运行的代码分为三个模块分开书写,完整代码如下:
contact.c
//contact.c通讯录的实现 #include"contact.h" //初始化通讯录成员(动态版) void InitContact(Contact* pc) { assert(pc); pc->sz = 0; PeoInfo * ptr = (PeoInfo*)calloc(DEFAULT_SZ , sizeof(PeoInfo)); if (ptr == NULL) { perror("InitContact::calloc"); return; } pc->data = ptr; pc->capacity = DEFAULT_SZ; } //销毁通讯录 void DestroyContact(Contact* pc) { free(pc->data); pc->data = NULL; pc->capacity = 0; pc->sz = 0; pc = NULL; } void check_capacity(Contact* pc) { if (pc->sz == pc->capacity) { //增容 PeoInfo*ptr=(PeoInfo*)realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(PeoInfo)); if (ptr == NULL) { perror("check_capacity::realloc"); return; } pc->data = ptr; pc->capacity += INC_SZ; } } //增加联系人(动态版) 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); //每次录入完,sz自增 pc->sz++; printf("联系人添加成功:>\n"); } //按人名查找联系人 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) { assert(pc); char name[NAME_MAX] = { 0 }; //防止通讯录删完了 if (pc->sz == 0) { printf("通讯录为空,无法删除\n"); return; } //找到要删除的对象 printf("请输入要删除的人的名字:>"); scanf("%s", name); int del = FindByName(pc, name); if (-1 == del) { printf("抱歉,没有找到您要删除的对象;<\n"); return; } int i = 0; //删除 for (i = del; 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[NAME_MAX] = { 0 }; //找到要查找的对象 printf("请输入要查找的人的名字:>"); scanf("%s", name); int sea = FindByName(pc, name); if (-1 == sea) { printf("抱歉,没有找到您要查找的对象;<\n"); return; } //打印信息 printf("________________________________________________________________________________________\n"); printf("%-4s\t%-20s\t%-4s\t%-6s\t%-20s\t%-12s\n", "|序号", "|姓名", "|年龄", "|性别", "|地址", "|电话"); printf("________________________________________________________________________________________\n"); printf("|%-4d\t|%-20s\t|%-4d\t|%-6s\t|%-20s\t|%-12s\n", sea + 1, pc->data[sea].name, pc->data[sea].age, pc->data[sea].sex, pc->data[sea].addr, pc->data[sea].tele); printf("________________________________________________________________________________________\n"); } //修改联系人信息 void modifyContact(Contact* pc) { assert(pc); char name[NAME_MAX] = { 0 }; //找到要修改的对象 printf("请输入要修改的人的名字:>"); scanf("%s", name); int mod = FindByName(pc, name); if (-1 == mod) { printf("抱歉,没有找到您要修改的对象;<\n"); return; } printf("请输入修改后的联系人信息\n"); printf("请输入名字;>"); scanf("%s", pc->data[mod].name); printf("请输入年龄;>"); scanf("%d", &(pc->data[mod].age)); printf("请输入性别;>"); scanf("%s", pc->data[mod].sex); printf("请输入地址;>"); scanf("%s", pc->data[mod].addr); printf("请输入电话;>"); scanf("%s", pc->data[mod].tele); printf("修改成功;>\n"); printf("________________________________________________________________________________________\n"); printf("%-4s\t%-20s\t%-4s\t%-6s\t%-20s\t%-12s\n", "|序号", "|姓名", "|年龄", "|性别", "|地址", "|电话"); printf("________________________________________________________________________________________\n"); printf("|%-4d\t|%-20s\t|%-4d\t|%-6s\t|%-20s\t|%-12s\n", mod + 1, pc->data[mod].name, pc->data[mod].age, pc->data[mod].sex, pc->data[mod].addr, pc->data[mod].tele); printf("________________________________________________________________________________________\n"); } //显示通讯录中的信息 void ShowContact(const Contact* pc) { assert(pc); if (pc->sz == 0) { printf("通讯录为空,无法显示\n"); return; } int i = 0; printf("________________________________________________________________________________________\n"); printf("%-4s\t%-20s\t%-4s\t%-6s\t%-20s\t%-12s\n", "|序号", "|姓名", "|年龄", "|性别", "|地址", "|电话"); for (i = 0; i < pc->sz; i++) { printf("________________________________________________________________________________________\n"); printf("|%-4d\t|%-20s\t|%-4d\t|%-6s\t|%-20s\t|%-12s\n", i + 1, pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].addr, pc->data[i].tele); } printf("________________________________________________________________________________________\n"); } //清空联系人信息 void EmptyContact(Contact* pc) { assert(pc); if (pc->sz == 0) { printf("通讯录为空,无需清空\n"); return; } pc->sz = 0; memset(pc->data, 0, sizeof(pc->data)); printf("清空成功:>\n"); } //comper按姓名排序 int comper_name(const void* a, const void* b) { const PeoInfo* personA = (const PeoInfo*)a; const PeoInfo* personB = (const PeoInfo*)b; return strcmp(personA->name, personB->name); } //comper按年龄排序 int comper_age(const void* a, const void* b) { const PeoInfo* personA = (const PeoInfo*)a; const PeoInfo* personB = (const PeoInfo*)b; return (personA->age - personB->age); } //comper按性别排序 int comper_sex(const void* a, const void* b) { const PeoInfo* personA = (const PeoInfo*)a; const PeoInfo* personB = (const PeoInfo*)b; return strcmp(personA->sex, personB->sex); } //comper按住处排序 int comper_addr(const void* a, const void* b) { const PeoInfo* personA = (const PeoInfo*)a; const PeoInfo* personB = (const PeoInfo*)b; return strcmp(personA->addr, personB->addr); } //给联系人排序 void SortContact(Contact* pc) { int cho = 0; if (pc->sz == 0) { printf("通讯录为空,请先加入联系人\n"); return; } printf("******************************\n"); printf("******** 1.按姓名 *********\n"); printf("******** 2.按年龄 *********\n"); printf("******** 3.按性别 *********\n"); printf("******** 4.按地址 *********\n"); printf("******************************\n"); printf("请选择您想使用的排序方式:>"); scanf("%d", &cho); int (*compare_func)(const void*, const void*) = NULL; switch (cho) { case 1: compare_func = comper_name; break; case 2: compare_func = comper_age; break; case 3: compare_func = comper_sex; break; case 4: compare_func = comper_addr; break; default: printf("排序选项无效:<\n"); return; } // 使用qsort函数进行排序 qsort(pc->data, pc->sz, sizeof(PeoInfo), compare_func); printf("排序成功:>\n"); ShowContact(pc); }
test.c
//test.c测试通讯录 #include"contact.h" void menu() { printf("******************************************\n"); printf("******************************************\n"); printf("******* 1.add 2.del *******\n"); printf("******* 3.search 4.modify *******\n"); printf("******* 5.show 6.empty *******\n"); printf("******* 7.sort 0.exit *******\n"); printf("******************************************\n"); printf("******************************************\n"); } int main() { int input = 0; //创建通讯录 Contact con; //初始化通讯录 InitContact(&con); do { menu(); printf("请选择:>"); scanf("%d", &input); switch (input) { case 1: AddContact(&con); break; case 2: DelContact(&con); break; case 3: searchContact(&con); break; case 4: modifyContact(&con); break; case 5: ShowContact(&con); break; case 6: EmptyContact(&con); break; case 7: SortContact(&con); break; case 0: DestroyContact(&con); printf("退出程序:>\n"); break; default: printf("输入非法,请重新输入!\n"); break; } } while (input); return 0; }
contact.h
#pragma once //contact.h函数的声明 #define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<string.h> #include<assert.h> #include<stdlib.h> #define NAME_MAX 20 #define SEX_MAX 6 #define ADDR_MAX 30 #define TELE_MAX 12 //动态版本 #define DEFAULT_SZ 3 #define INC_SZ 2 //人的信息-结构体 typedef struct PeoInfo { char name[NAME_MAX]; int age; char sex[SEX_MAX]; char addr[ADDR_MAX]; char tele[TELE_MAX];//电话11位,留一位给'\0'; }PeoInfo; //动态版本 typedef struct Contact { PeoInfo* data;//存放1000个人的信息 int sz;//用来记录当前已经存放的信息的个数 int capacity;//记录当前通讯录的最大容量 }Contact; //初始化通讯录 void InitContact(Contact* pc); //销毁通讯录 void DestroyContact(Contact * pc); //增加联系人 void AddContact(Contact* pc); //删除通讯录中的信息 void DelContact(Contact* pc); //显示通讯录中的信息 void ShowContact(const Contact* pc); //查找联系人并打印其信息 void searchContact(const Contact* pc); //修改联系人信息 void modifyContact(Contact* pc); //清空联系人信息(和初始化逻辑一模一样) void EmptyContact(Contact* pc); //给联系人排序 void SortContact(Contact* pc);
结语
希望这篇通讯录的动态增容实现详解能对大家有所帮助,欢迎大佬们留言或私信与我交流.
学海漫浩浩,我亦苦作舟!关注我,大家一起学习,一起进步!