在上篇文章,讲了一下用C语言实现通讯录的静态版本,那么这篇文章主要讲一下怎样在它的静态版本的基础上,改成动态版本。
目录
1. 更改
在静态版本的通讯录中,我们在表示通讯录的结构体中,直接定义了一个结构体数组,并直接写死了它的元素个数 — 1000 个,这样就使通讯录的联系人总数也给写死了。最大只能存放1000个联系人,如果不够1000个,那么其他的空间也就会浪费掉。
所以在动态版本中,我们使用动态内存开辟来给定通讯录的大小,在最初给三个联系人的空间,不够,就扩容2个,还不够就又扩容2个,以此重复,这样对于一个通讯录来说就会更加合理,浪费的空间也就非常少了。
同样,由于通讯录里存放联系人的空间是动态内存开辟的,所以在退出通讯录时,就需要把空间释放掉。从而避免空间泄露问题。以
上就是大致需要改变的地方,接下来,就在代码中具体实现:
首先就是通讯录模板的创建:
静态版本:
//通讯录
struct Contact
{
struct PeoInfo Data[1000]; //用来存放联系人的信息的数组
int sz; //记录当前通讯录里还有多少人
};
动态版本:
//通讯录
struct Contact
{
struct PeoInfo* Data; //用来指向动态开辟的那块内存空间
int sz; //记录当前通讯录里还有多少人
int ContactMax; //通讯录当前最大容量
};
可以看到,上面代码的改变之处,就是数组变成了个指针,然后加了一个变量用来存放通讯录当前最大容量。
然后,就是在初始化通讯录时,动态开辟一块空间,使通讯录里的Data指针指向这块空间的起始地址:
静态版本:
//初始化通讯录
void InitContact(struct Contact* pc)
{
pc->sz = 0; //归零,表示通讯录里没有人
//使用memset函数对存放联系人的数组初始化
//memset(pc->Data, 0, sizeof(struct PeoInfo) * PEOPLE_MAX); //方法1:便于理解
memset(pc->Data, 0, sizeof(pc->Data)); //方法2:sizeof(数组名) - 求整个数组的大小
}
动态版本:
//初始化通讯录
void InitContact(struct Contact* pc)
{
pc->sz = 0; //归零,表示通讯录里没有人
pc->Data = (struct PeoInfo*)malloc(CONTACT_MAX * sizeof(struct PeoInfo));
pc->ContactMax = CONTACT_MAX; //最开始的最大容量
}
注意上面的 CONTACT_MAX 是我们定义的一个符号常量,用来确定刚开始通讯录的最大容量。
#define CONTACT_MAX 3 //最开始通讯录的容量
//这里定义3,不够就再扩容
在这里,我们可以看到上面两段代码,有很大的不同,静态版本是直接把已有的通讯录数组进行初始化,然后将当前通讯录人数赋值为0。动态版本是先开辟一块空间,再将空间的起始地址放到Data里去,同时当前通讯录人数赋值为0,初始最大容量赋值为CONTACT_MAX ;
再然后,就是对添加联系人模块进行更改:
静态版本:
//添加联系人信息
void AddPeople(struct Contact* pc)
{
struct PeoInfo temp = { 0 }; //用来临时存放单个人的信息
if (pc->sz == PEOPLE_MAX) //查看当前通讯录是否已满
{
printf("通讯录已满!\n");
}
else
{
//添加联系人信息
printf("请输入姓名:");
scanf("%s", temp.name); //scanf("%s", pc->Data[pc->sz].name);
printf("请输入性别:");
scanf("%s", temp.sex); //scanf("%s", pc->Data[pc->sz].sex);
printf("请输入年龄:");
scanf("%d", &temp.age); //scanf("%d", &(pc->Data[pc->sz].age));
printf("请输入电话号码:");
scanf("%s", temp.number); //scanf("%s", pc->Data[pc->sz].number);
printf("请输入地址:");
scanf("%s", temp.address); //scanf("%s", pc->Data[pc->sz].address);
pc->Data[pc->sz] = temp; //把这个人的信息真正存放到通讯录中
pc->sz++; //总人数加1
printf("添加成功!\n");
}
}
动态版本:
//添加联系人信息
void AddPeople(struct Contact* pc)
{
struct PeoInfo temp = { 0 }; //用来临时存放单个人的信息
if (pc->sz == pc->ContactMax) //判断通讯录是否达到最大容量
{
//增容
struct PeoInfo* ret = realloc(pc->Data, //内存增容函数
(pc->ContactMax + 2) * sizeof(struct PeoInfo));
if (ret == NULL)
{
printf("增容失败!\n");
return;
}
else
{
pc->Data = ret; //将开辟的内存的地址真正存放到Data指针中去
pc->ContactMax += 2; //更改下容量
printf("增容成功!\n");
}
}
//添加联系人信息
printf("请输入姓名:");
scanf("%s", temp.name);
printf("请输入性别:");
scanf("%s", temp.sex);
printf("请输入年龄:");
scanf("%d", &temp.age);
printf("请输入电话号码:");
scanf("%s", temp.number);
printf("请输入地址:");
scanf("%s", temp.address);
pc->Data[pc->sz] = temp; //把这个人的信息真正存放到通讯录中
pc->sz++; //总人数加1
printf("添加成功!\n");
}
对于添加联系人模块,在静态版本中,我们先要判断当前通讯录满没满,然后再添加联系人。在动态版本中没有通讯录是否满了这个判断,而是判断当前容量是否达到最大,达到,则进行增容处理,然后再添加联系人信息。
最后,就是在退出通讯录时,进行通讯录的销毁(内存释放):
函数声明:
//销毁通讯录
void FreeContact(struct Contact* pc);
函数定义:
//销毁通讯录
void FreeContact(struct Contact* pc)
{
free(pc->Data); //释放空间
pc->Data = NULL; //释放之后,要置为空指针
pc->ContactMax = 0; //最大容量置零
pc->sz = 0; //当前联系人个数置零
}
总结:其实动态版本和静态版本的不同之处并不大,就是在初始化模块和添加联系人模块发生一些改变,然后加一个销毁通讯录模块就大功告成了。总的来说,动态版本对于内存的使用更加高效了。
2. 源代码
contact.h文件中的内容:
#pragma once
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define CONTACT_MAX 3 //最开始通讯录的容量
#define NAME_MAX 20 //最大名字的长度
#define SEX_MAX 5 //性别最大长度
#define NUMBER_MAX 20 //电话最大长度
#define ADDRESS_MAX 20 //地址最大长度
//存放每一个人的具体信息
struct PeoInfo
{
char name[NAME_MAX];
char sex[SEX_MAX];
int age;
char number[NUMBER_MAX];
char address[ADDRESS_MAX];
};
//通讯录
struct Contact
{
struct PeoInfo* Data; //用来指向动态开辟的那块内存空间
int sz; //记录当前通讯录里还有多少人
int ContactMax; //通讯录当前最大容量
};
//初始化通讯录
void InitContact(struct Contact* pc);
//添加联系人信息
void AddPeople(struct Contact* pc);
//删除联系人信息
void DelPeople(struct Contact* pc);
//显示通讯录
void Display(struct Contact* pc);
//查找联系人
int FindPeople(struct Contact* pc, char* name);
//查找联系人并打印其信息
void SearchPeople(struct Contact* pc);
//修改指定联系人信息
void ModPeople(struct Contact* pc);
//以名字排序通讯录 - 升序
void NameSort(struct Contact* pc);
//清空所有联系人
void ClsContact(struct Contact* pc);
//销毁通讯录
void FreeContact(struct Contact* pc);
contact.c文件中的内容:
#include"contact.h"
//初始化通讯录
void InitContact(struct Contact* pc)
{
pc->sz = 0; //归零,表示通讯录里没有人
pc->Data = (struct PeoInfo*)malloc(CONTACT_MAX * sizeof(struct PeoInfo));
pc->ContactMax = CONTACT_MAX; //最开始的最大容量
}
//添加联系人信息
void AddPeople(struct Contact* pc)
{
struct PeoInfo temp = { 0 }; //用来临时存放单个人的信息
if (pc->sz == pc->ContactMax)
{
//增容
struct PeoInfo* ret = realloc(pc->Data, //内存增容函数
(pc->ContactMax + 2) * sizeof(struct PeoInfo));
if (ret == NULL)
{
printf("增容失败!\n");
return;
}
else
{
pc->Data = ret; //将开辟的内存的地址真正存放到Data指针中去
pc->ContactMax += 2; //更改下容量
printf("增容成功!\n");
}
}
printf("请输入姓名:");
scanf("%s", temp.name);
printf("请输入性别:");
scanf("%s", temp.sex);
printf("请输入年龄:");
scanf("%d", &temp.age);
printf("请输入电话号码:");
scanf("%s", temp.number);
printf("请输入地址:");
scanf("%s", temp.address);
pc->Data[pc->sz] = temp; //把这个人的信息真正存放到通讯录中
pc->sz++; //总人数加1
printf("添加成功!\n");
}
//删除联系人信息
void DelPeople(struct Contact* pc)
{
if (pc->sz == 0) //判断通讯录是否为空
{
printf("通讯录为空,删除失败!\n");
}
else
{
char name[NAME_MAX] = { 0 }; //临时存放要删除的联系人名字
printf("请输入要删除联系人的名字:");
scanf("%s", name);
//查找该联系人
int ret = FindPeople(pc, name);
//自定义查找联系人函数,找到返回下标,否则返回 -1
if (ret == -1)
{
printf("该联系人不存在!\n");
}
else
{
int j = 0;
for (j = ret; j < pc->sz - 1; j++)
//从查找的联系人开始,把后面的联系人整体向前移动一位
{
pc->Data[j] = pc->Data[j + 1]; //把该联系人的信息给覆盖掉
}
pc->sz--; //当前联系人数量减一
printf("删除成功!\n");
}
}
}
//查找联系人
int FindPeople(struct Contact* pc, char* name)
{
int i = 0;
for (i = 0; i < pc->sz; i++)
//当前通讯录有多少联系人就遍历多少次,直到找到所需要的那个
{
if (strcmp(pc->Data[i].name, name) == 0)
//用字符串比较函数,在判断是不是我想查找的那个联系人
{
return i; //找到了,返回下标
}
}
return -1; //没找到,返回 -1
}
//显示通信录里的联系人
void Display(struct Contact* pc)
{
printf("%10s\t%2s\t%4s\t%8s\t%10s\n\n",
"name", "sex", "age", "number", "address");
int i = 0;
for (i = 0; i < pc->sz; i++) //当前有多少联系人,就循环多少次
{
printf("%10s\t%2s\t%4d\t%8s\t%10s\n",
pc->Data[i].name, pc->Data[i].sex, pc->Data[i].age,
pc->Data[i].number, pc->Data[i].address);
}
}
//查找联系人并打印其信息
void SearchPeople(struct Contact* pc)
{
char name[NAME_MAX] = { 0 }; //临时存放需要查询的联系人的名字
if (pc->sz == 0) //判断通讯录是否为空
{
printf("通讯录为空,查询失败!\n");
}
else
{
printf("请输入联系人的名字:");
scanf("%s", name);
int ret = FindPeople(pc, name); //查找该联系人
if (ret == -1)
{
printf("该联系人不存在!\n");
}
else
{
//打印该联系人的具体信息
printf("%10s\t%2s\t%4s\t%8s\t%10s\n\n",
"name", "sex", "age", "number", "address");
printf("%10s\t%2s\t%4d\t%8s\t%10s\n",
pc->Data[ret].name, pc->Data[ret].sex, pc->Data[ret].age,
pc->Data[ret].number, pc->Data[ret].address);
}
}
}
//修改指定联系人信息
void ModPeople(struct Contact* pc)
{
char name[NAME_MAX] = { 0 };
if (pc->sz == 0) //先判断是不是空的通讯录
{
printf("通讯录为空,修改失败!\n");
}
else
{
printf("请输入需要修改的联系人名字:");
scanf("%s", name);
int ret = FindPeople(pc, name); //查找该联系人,并返回其下标
if (ret == -1)
{
printf("该联系人不存在!\n");
}
else
{
//和添加联系人信息一样,创建一个临时变量,用来存放我要输入的信息
struct PeoInfo temp = { 0 };
//逐条输入
printf("请输入新的名字:");
scanf("%s", temp.name);
printf("请输入新的性别:");
scanf("%s", temp.sex);
printf("请输入新的年龄:");
scanf("%d", &temp.age);
printf("请输入新的电话:");
scanf("%s", temp.number);
printf("请输入新的地址:");
scanf("%s", temp.address);
//覆盖掉原来联系人的信息
pc->Data[ret] = temp;
//提示一下
printf("修改成功!\n");
}
}
}
//按名字升序排通讯录
int name_sort(const void* e1, const void* e2)
{
return strcmp(((struct PeoInfo*)e1)->name, ((struct PeoInfo*)e2)->name);
}
//以名字排序通讯录 - 升序
void NameSort(struct Contact* pc)
{
if (pc->sz == 0)
{
printf("通讯录为空,排序失败!\n");
}
else
{
//使用qsort快速排序
qsort(pc->Data, pc->sz, sizeof(struct PeoInfo), name_sort);
printf("排序成功!(升序)!\n");
}
}
//清空所有联系人
void ClsContact(struct Contact* pc)
{
if (pc->sz == 0) //判断通讯录里有没有联系人
{
printf("没有联系人,清除失败!\n");
}
else
{
//直接初始化通讯录
InitContact(pc);
printf("清除成功!\n");
}
}
//销毁通讯录
void FreeContact(struct Contact* pc)
{
free(pc->Data);
pc->Data = NULL; //释放之后,要置为空指针
pc->ContactMax = 0;
pc->sz = 0;
}
test.c文件中的内容:
#include"contact.h"
//通讯录面板
void menu()
{
printf("************************************\n");
printf("****** 0.exit 1.add ******\n");
printf("****** 2.del 3.search ******\n");
printf("****** 4.modify 5.dis ******\n");
printf("****** 6.cls 7.sort ******\n");
printf("************************************\n");
}
//用来存放对通讯录的操作
enum board
{
EXIT,
ADD,
DEL,
SEARCH,
MODIFY,
DIS,
CLS,
SORT
};
int main()
{
int input = 0; //接收选项
//创建一个通讯录
struct Contact con = { 0 };
//初始化通讯录
InitContact(&con);
do
{
//打印个面板
menu();
printf("请输入选项》");
scanf("%d", &input);
switch (input)
{
case EXIT:
FreeContact(&con); //释放空间
printf("销毁通讯录!\n");
break;
case ADD: //添加联系人
AddPeople(&con);
break;
case DEL: //删除联系人
DelPeople(&con);
break;
case SEARCH: //查询联系人
SearchPeople(&con);
break;
case MODIFY: //修改联系人
ModPeople(&con);
break;
case DIS: //显示通讯录
Display(&con);
break;
case CLS: //清空联系人
ClsContact(&con);
break;
case SORT: //给联系人按名字排升序
NameSort(&con);
break;
default:
printf("选项错误,重新选择!\n");
break;
}
} while (input);
return 0;
}