大体思路
今天我们将来实现一下基于我之前博客里的通讯录的进阶,将静态版本升级为动态版本
(建议阅读本篇博客的读者阅读我的之前博客:动态内存管理—知识点小集结和通讯录——静态版本)
现在我们就开始来实现我们通讯录的升级吧 !
首先来想一下大体思路,要如何实现动态内存呢?
1.在程序运行的开始我们要用malloc 或者calloc开辟一片内存空间。
2.在通讯录结构体中,我们除了sz这个已存人数外,还应该再加一个表示容量的值
2.在AddNumber函数(增加联系人的函数)中要使用recalloc函数进行扩容,确保造成尽可能少的内存浪费。
4.在退出通讯录的时候,我们要free我们开辟的空间
在开始之前,先附上main.c文件里的代码,以便对照下文去看
#define _CRT_SECURE_NO_WARNINGS
#include "Contact.h"
void menu()
{
printf("*****************************\n");
printf("**** 1.Add 2.Sub *****\n");
printf("**** 3.Mod 4.Show *****\n");
printf("**** 5.Init 0.Exit *****\n");
printf("*****************************\n");
}
enum input
{
Exit,//对应的值是0
Add,//对应的值是1,依次往下类推
Sub,
Mod,
Show,
Init
};
int main()
{
int input = 1;
struct Contact CON;
InitNUmber1(&CON);//初始化通讯录
while(input)//只有当input为0时才能推出循环
{
menu();//写一个菜单函数
scanf("%d", &input);
switch (input)
{
case Add:
AddNumber(&CON);
break;
case Sub:
SubNumber(&CON);
break;
case Mod:
ModNumber(&CON);
break;
case Show:
ShowNumber(&CON);
break;
case Init:
InitNumber2(&CON);
break;
case Exit:
ExitNumber(&CON);
break;
default:
printf("输入错误,请重新输入\n");
break;
}
}
}
改编通讯录的结构体
就很简单的增加一个结构体变量
struct Contact
{
struct Peo *Con;//定义一个结构体指针来指向开辟的空间
int sz;//用来储存通讯录中的人数
size_t cap;//用来代表通讯录储存人数的能力
};
初始化通讯录的修改
在进入主循环之前,先开辟一块空间,同时初始化通讯录结构体里的参数
void InitNumber1(struct Contact *CON)
{
CON->Con = (struct Peo*)malloc(MAX*sizeof(struct Peo));//这里就动态开辟了一块空间,MAX的大小定义为3
CON->sz = 0;
CON->cap = MAX;
}
增加函数的修改
void AddNumber(struct Contact *CON)
{
if (CON->sz == CON->cap)//当储存的人数等于存储的容量时,就开始扩容
{
CON->Con = (struct Peo*)realloc(CON->Con,(CON->cap+2)*sizeof(struct Peo));//满了之后我就扩容两个
if (CON->Con == NULL)//这里对开辟后的空间进行检查,如果为空,则直接结束该函数
{
printf("增加失败\n");
return;
}
else
{
CON->cap += 2;
printf("扩容成功\n");
}
}
//之后就直接输入信息
printf("请输入联系人的名字\n");
scanf("%s",(CON->Con+CON->sz)->name);
printf("请输入联系人的性别\n");
scanf("%s", (CON->Con + CON->sz)->sex);
printf("请输入联系人的年龄\n");
scanf("%d", &((CON->Con + CON->sz)->age));//要注意,这里要加&符号,因为其他成员变量都是字符串,相当于数组名就是地址,所以其他几个不用加。
printf("请输入联系人的地址\n");
scanf("%s", (CON->Con + CON->sz)->addr);
printf("请输入联系人的电话\n");
scanf("%s", (CON->Con + CON->sz)->tele);
++(CON->sz);//添加了之后别忘了自增 sz。
printf("添加联系人成功");
}
退出通讯录函数的实现
写这个函数的目的主要是在之前的代码中我们并未对我们开辟的空间进行释放,所以也必须要释放一下才行。
void ExitNumber(struct Contact *CON)
{
free(CON->Con);
CON->Con = NULL;
printf("退出通讯录\n");
}
到这,我们对通讯录的升级也就完成了。虽然使用起来和原来没什么差别,但是内部的使用却上了一层台阶,对内存的浪费变少了。
希望这篇文章对读者有所帮助,读者可以通过对通讯录的升级来加强自己对动态内存的知识点的运用。感谢观看!
代码一览图
Contact.c
#define _CRT_SECURE_NO_WARNINGS
#include"Contact.h"
void AddNumber(struct Contact *CON)
{
if (CON->sz == CON->cap)//当储存的人数等于存储的容量时,就开始扩容
{
CON->Con = (struct Peo*)realloc(CON->Con,(CON->cap+2)*sizeof(struct Peo));//满了之后我就扩容两个
if (CON->Con == NULL)//这里对开辟后的空间进行检查,如果为空,则直接结束该函数
{
printf("增加失败\n");
return;
}
else
{
CON->cap += 2;
printf("扩容成功\n");
}
}
//之后就直接输入信息
printf("请输入联系人的名字\n");
scanf("%s",(CON->Con+CON->sz)->name);
printf("请输入联系人的性别\n");
scanf("%s", (CON->Con + CON->sz)->sex);
printf("请输入联系人的年龄\n");
scanf("%d", &((CON->Con + CON->sz)->age));//要注意,这里要加&符号,因为其他成员变量都是字符串,相当于数组名就是地址,所以其他几个不用加。
printf("请输入联系人的地址\n");
scanf("%s", (CON->Con + CON->sz)->addr);
printf("请输入联系人的电话\n");
scanf("%s", (CON->Con + CON->sz)->tele);
++(CON->sz);//添加了之后别忘了自增 sz。
printf("添加联系人成功");
}
void SubNumber(struct Contact *CON)
{
if (CON->sz == 0)
{
printf("通讯录为空,无法删除\n");
return;
}
else
{
char name[NAME_MAX];
printf("请输入你想删除的联系人的名字\n");
scanf("%s",name);
int i = 0;
if (CON->sz == 1)
{
if (strcmp(name, (CON->Con->name)) == 0)
{
memset(CON->Con, 0, sizeof(struct Peo));
printf("删除成功\n");
--(CON->sz);
return;
}
else
{
printf("通讯录中无此人\n");
return;
}
}//因为只有一个联系人的时候,对于后面循环遍历覆盖有点特殊,我们可以选择特殊处理
for (i = 0 ; i < CON->sz; i++)
{
if (strcmp(name, (CON->Con + i)->name) == 0)
{
i += 1;
for (; i < CON->sz;i++)
{
(CON->Con)[i-1] = (CON->Con)[i];//可以直接将相同类型的结构体进行赋值操作
}
printf("删除成功\n");
--(CON->sz);
return;
}
}
printf("通讯录中无此人,无法删除\n");
return;
}
}
void ShowNumber(struct Contact *CON)
{
if (CON->sz == 0)
{
printf("通讯录为空\n");//判断一下啊,如果通讯录为空,那么将无法显示
return;
}
else
{
int i = 0;
printf("%s\t%s\t%s\t%s\t%s\n","姓名","年龄","性别","地址","电话");
for (; i < CON->sz; i++)
{
printf("%s\t%-d\t%s\t%s\t%s\n",
(CON->Con + i)->name,
(CON->Con + i)->age,
(CON->Con + i)->sex,
(CON->Con + i)->addr,
(CON->Con + i)->tele);
}
}
}
void ModNumber(struct Contact *CON)
{
if (CON->sz == 0)
{
printf("通讯录为空,无法修改\n");
return;
}
else
{
char name[NAME_MAX];
printf("请输入你想修改的联系人的名字\n");
scanf("%s", name);
int i = 0;
for (; i < CON->sz; i++)
{
if (strcmp(name, (CON->Con + i)->name) == 0)
{
printf("请输入联系人的名字\n");
scanf("%s", (CON->Con + i)->name);
printf("请输入联系人的性别\n");
scanf("%s", (CON->Con + i)->sex);
printf("请输入联系人的年龄\n");
scanf("%d", &((CON->Con + i)->age));
printf("请输入联系人的地址\n");
scanf("%s", (CON->Con + i)->addr);
printf("请输入联系人的电话\n");
scanf("%s", (CON->Con + i)->tele);
printf("修改成功\n");
return;
}
}
printf("通讯录中无此人\n");
return;
}
}
void InitNumber1(struct Contact *CON)
{
CON->Con = (struct Peo*)malloc(MAX*sizeof(struct Peo));//这里就动态开辟了一块空间,MAX的大小定义为3
CON->sz = 0;
CON->cap = MAX;
}
void InitNumber2(struct Contact *CON)
{
if (CON->sz == 0)
{
printf("通讯录为空,无需初始化\n");
return;
}
else
{
memset(CON->Con, 0, MAX*sizeof(struct Peo));
CON->sz = 0;
printf("初始化成功\n");
}
}
void ExitNumber(struct Contact *CON)
{
free(CON->Con);
CON->Con = NULL;
printf("退出通讯录\n");
}
main.c
#define _CRT_SECURE_NO_WARNINGS
#include "Contact.h"
void menu()
{
printf("*****************************\n");
printf("**** 1.Add 2.Sub *****\n");
printf("**** 3.Mod 4.Show *****\n");
printf("**** 5.Init 0.Exit *****\n");
printf("*****************************\n");
}
enum input
{
Exit,//对应的值是0
Add,//对应的值是1,依次往下类推
Sub,
Mod,
Show,
Init
};
int main()
{
int input = 1;
struct Contact CON;
InitNumber1(&CON);//初始化通讯录
while(input)//只有当input为0时才能推出循环
{
menu();//写一个菜单函数
scanf("%d", &input);
switch (input)
{
case Add:
AddNumber(&CON);
break;
case Sub:
SubNumber(&CON);
break;
case Mod:
ModNumber(&CON);
break;
case Show:
ShowNumber(&CON);
break;
case Init:
InitNumber2(&CON);
break;
case Exit:
ExitNumber(&CON);
break;
default:
printf("输入错误,请重新输入\n");
break;
}
}
}
Contact.h
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define NAME_MAX 10
#define SEX_MAX 4
#define ADDR_MAX 20
#define TELE_MAX 11
#define MAX 3
struct Peo
{
char name[NAME_MAX];//定义一个字符串来储存名字
char sex[SEX_MAX];//定义一个字符串来存储性别
int age;//定义一个整形用来存储年龄
char addr[ADDR_MAX];//定义一个字符串来存储联系人的地址
char tele[TELE_MAX];//定义一个字符串来存储联系人的地址
};
struct Contact
{
struct Peo *Con;//定义一个结构体指针来指向开辟的空间
int sz;//用来储存通讯录中的人数
size_t cap;//用来代表通讯录储存人数的能力
};
void AddNumber(struct Contact *CON);
void SubNumber(struct Contact *CON);
void ShowNumber(struct Contact *CON);
void ModNumber(struct Contact *CON);
void InitNumber1(struct Contact *CON);
void InitNumber2(struct Contact *CON);
void ExitNumber(struct Contact *CON);