之前写过一个版本的通讯录,当时还没有学习动态内存管理,所以只是简单实现了一版。传送门在这:
在之前一段时间把动态内存管理学完之后,今天把这个小项目,重新捡起来,改一改,供大家参考。
之前写的时候是给了1000个人的信息,一旦生成.exe文件后,那么这个数量信息就没法改了。不太方便。今天主要是利用动态内存开辟,优化一下之前写的代码。改成动态增长的版本。
动态增长版本通讯录
设定:
默认可以存放3个人的信息,当发现当前通讯录满的时候,我们进行扩容,每次增加两个信息长度的空间。
(这里空间个数少,是方便我调试,大家可以直接多来点空间。)
contact.h
首先针对头文件中,我们的宏定义1000 肯定就没用了要注释掉,下面是原有版本:
#ifndef C_CONTACT_H
#define C_CONTACT_H
#include "stdio.h"
#include "string.h"
#define MAX 1000
#define NAME 20
#define SEX 5
#define TELE 12
#define ADDR 30
struct PeoInfo
{
char name[NAME];
int age;
char sex[SEX];
char tele[TELE];
char addr[ADDR];
};
struct Contact{
struct PeoInfo data[MAX];//存放一个信息
int size;//记录当前个数
};
enum Option{
EXIT,
ADD,
DEL,
SEARCH,
MODIFY,
SHOW,
SORT
};
//声明函数
void InitContact(struct Contact* ps);
void AddContact(struct Contact* ps);
void ShowContact(const struct Contact* ps);
void DelContact(struct Contact* ps);
void ModifyContact(const struct Contact* ps);
void SortContact(struct Contact* ps);
#endif //C_CONTACT_H
我们把第6行注释掉:
//#define MAX 1000
然后要修改结构体Contact
struct Contact{
struct PeoInfo * data;//存放一个信息
int size;//记录当前个数
int capacity;
};
当然这个地方也可以采取我之前文章中的柔性数组的方案,这里我就用指针来实现了,除了修改指针以外,还需要一个变量来记录当前空间的容量,size只是记录有几个,还需要有个变量记录总共空间能容纳几个。
之所以需要这个主要是方便我们判断空间进行扩容。
那么这就是第一个文件需要改的地方。
test.c
原代码如下:
//
// Created by 提灯前行 on 2023/11/30.
//
#include "contact.c"
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");
}
int main(){
int input=0;
//创建通讯录
struct Contact con;//通讯录,包含1000个元素和size
//初始化通讯录
InitContact(&con);
do {
menu();
printf("please select:>");
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("exit\n");
break;
default:
printf("input error\n");
break;
}
} while (input);
}
我们首先聚焦于这里,
//创建通讯录
struct Contact con;//通讯录,包含1000个元素和size
既然使用动态开辟空间的方法, 那么这里在创建通讯录的时候,就不能是这样简单的像原来一样创建个对象了。里面包含的也不是1000个元素了和单独一个size了。
//创建通讯录
struct Contact con;//通讯录,包含data指针和size、capacity
所以这里初始化函数要改
//初始化通讯录
InitContact(&con);
我们进入到这个初始化函数。
contact.c
初始化函数InitContact的实现是在contact.c文件中的,我们要对其作修改。
原本初始化函数如下:
void InitContact(struct Contact* ps){
//置0
memset(ps->data,0, sizeof (ps->data));
ps->size=0;
}
修改为:
void InitContact(struct Contact* ps){
ps->data= (struct PeoInfo*)malloc(3*sizeof (struct PeoInfo));
//空间开辟判断
if (ps->data){
ps->size=0;
ps->capacity=DEFAULT_SIZE;
}else{
printf("空间开辟失败");
return;
}
}
这里就要使用malloc函数进行空间开辟了,当然我上面设定里说要开辟三个结构体大小的空间,咱们就3*结构体的大小,然后开辟完一定要记得判断是否开辟成功,还要在.h文件中引入该引得头文件。
#include "stdlib.h"
#define DEFAULT_SIZE 3
紧接着我们讨论实现的操作,有哪些需要改。
通讯录增加
增加也需要修改,原代码如下:
void AddContact(struct Contact* ps){
//判断通讯录是否满
if(ps->size==MAX){
printf("error , full of contact");
} else{
printf("please input name");
scanf("%s",ps->data[ps->size].name);
printf("please input ege");
scanf("%d",&(ps->data[ps->size].age));
printf("please input sex");
scanf("%s",ps->data[ps->size].sex);
printf("please input tele");
scanf("%s",ps->data[ps->size].tele);
printf("please input addr");
scanf("%s",ps->data[ps->size].addr);
ps->size++;
printf("add sucessfully\n");
ShowContact(ps);
}
}
修改为:
void AddContact(struct Contact* ps){
//判断通讯录是否满
//1.如果满了,就增加空间
//2.未满,不执行操作
CheckCapacity(ps);
printf("please input name");
scanf("%s",ps->data[ps->size].name);
printf("please input ege");
scanf("%d",&(ps->data[ps->size].age));
printf("please input sex");
scanf("%s",ps->data[ps->size].sex);
printf("please input tele");
scanf("%s",ps->data[ps->size].tele);
printf("please input addr");
scanf("%s",ps->data[ps->size].addr);
ps->size++;
printf("add sucessfully\n");
ShowContact(ps);
}
这里引入了一个新的函数,检查空间函数
void CheckCapacity(struct Contact *ps){
if (ps->size==ps->capacity){
//满了,增容
struct PeoInfo *ptr =realloc(ps->data,(ps->capacity+2)*sizeof (struct PeoInfo));
if (ptr){
ps->data=ptr;
ps->capacity+=2;
printf("增加了两个空间的容量\n");
} else{
printf("空间开辟失败");
}
}
}
这个函数负责判断空间容量,如果满了就增加,如果不满就什么也不干。
到这里我们先测试一下:
看到现在我已经添加了三个信息了。
然后我们继续添加:
会发现输出了一句:“增加了两个空间的容量”,这就说明我们之前改的思路是没问题的。
销毁空间
既然我们空间时动态开辟的,那么在最后使用完毕之后我们是不是还需要对空间进行释放啊,所以还需要增加上空间的释放。
我们去修改test.c中的菜单那块代码:
case EXIT:
//释放动态开辟的空间
DestroyContact(&con);
printf("exit\n");
break;
然后去.h中声明:
void DestroyContact( Contact* ps);
再去.c中去实现:
void DestroyContact( Contact* ps){
free(ps->data);
ps->data=NULL;
printf("空间已回收\n");
}
最后测试一下:
那么到此未知,这个小项目的改进已经改进完毕了。
全部代码
最后还是附赠上全套的代码:
contac.h
//
// Created by 提灯前行 on 2023/11/30.
//
#ifndef C_CONTACT_H
#define C_CONTACT_H
#include "stdio.h"
#include "string.h"
#include "stdlib.h"
//#define MAX 1000
#define DEFAULT_SIZE 3
#define NAME 20
#define SEX 5
#define TELE 12
#define ADDR 30
typedef struct PeoInfo
{
char name[NAME];
int age;
char sex[SEX];
char tele[TELE];
char addr[ADDR];
}PeoInfo;
typedef struct Contact{
struct PeoInfo * data;//存放一个信息
int size;//记录当前个数
int capacity;
}Contact;
enum Option{
EXIT,
ADD,
DEL,
SEARCH,
MODIFY,
SHOW,
SORT
};
//声明函数
void InitContact(struct Contact* ps);
void AddContact(struct Contact* ps);
void ShowContact(const struct Contact* ps);
void DelContact(struct Contact* ps);
void ModifyContact(const struct Contact* ps);
void SortContact(struct Contact* ps);
void DestroyContact( Contact* ps);
#endif //C_CONTACT_H
contact.c
//
// Created by 提灯前行 on 2023/11/30.
//
#include "contact.h"
void InitContact(struct Contact* ps){
ps->data= (struct PeoInfo*)malloc(3*sizeof (struct PeoInfo));
//空间开辟判断
if (ps->data){
ps->size=0;
ps->capacity=DEFAULT_SIZE;
}else{
printf("空间开辟失败");
return;
}
}
void CheckCapacity(struct Contact *ps){
if (ps->size==ps->capacity){
//满了,增容
struct PeoInfo *ptr =realloc(ps->data,(ps->capacity+2)*sizeof (struct PeoInfo));
if (ptr){
ps->data=ptr;
ps->capacity+=2;
printf("增加了两个空间的容量\n");
ptr=NULL;
} else{
printf("空间开辟失败");
}
}
}
void AddContact(struct Contact* ps){
//判断通讯录是否满
//1.如果满了,就增加空间
//2.未满,不执行操作
CheckCapacity(ps);
printf("please input name");
scanf("%s",ps->data[ps->size].name);
printf("please input ege");
scanf("%d",&(ps->data[ps->size].age));
printf("please input sex");
scanf("%s",ps->data[ps->size].sex);
printf("please input tele");
scanf("%s",ps->data[ps->size].tele);
printf("please input addr");
scanf("%s",ps->data[ps->size].addr);
ps->size++;
printf("add sucessfully\n");
ShowContact(ps);
}
void ShowContact(const struct Contact* ps){
if(ps->size==0){
printf("contact null\n");
} else{
// int i=0;
printf("%-20s\t%-4s\t%-5s\t%-12s\t%-20s\n","name","age","sex","tele","addr");
for (int j = 0; j < ps->size; ++j) {
printf("%-20s\t%-4d\t%-5s\t%-12s\t%-20s\n",
ps->data[j].name,
ps->data[j].age,
ps->data[j].sex,
ps->data[j].tele,
ps->data[j].addr);
}
}
}
static int FindByNme(const struct Contact* ps,char name[NAME]){
int i;
for (i=0; i < ps->size; ++i) {
if(0==strcmp(ps->data[i].name,name)){
return i;
}
}
//找不到的情况
return -1;
}
void DelContact(struct Contact* ps){
char name[NAME];
printf("please input del name");
scanf("%s",name);
//查找要删除的成员的位置
//找到了返回名字所在元素下标
//找不到返回-1
int pos= FindByNme(ps,name);
// int i;
// for (i=0; i < ps->size; ++i) {
// if(0==strcmp(ps->data[i].name,name)){
// break;
// }
// }
if(pos==-1){
printf("member is null");
}else{
int j;
for ( j = pos; j < ps->size-1; ++j) {
ps->data[j]=ps->data[j+1];
}
ps->size--;
printf("delete successfully\n");
ShowContact(ps);
}
//删除
}
void SearchContact(struct Contact* ps){
char name[NAME];
printf("please input name for select:>");
scanf("%s",name);
int pos= FindByNme(ps,name);
if(pos==-1) {
printf("member is null");
} else{
printf("%-20s\t%-4s\t%-5s\t%-12s\t%-20s\n","name","age","sex","tele","addr");
//打印出来查找到的结果
printf("%-20s\t%-4d\t%-5s\t%-12s\t%-20s\n",
ps->data[pos].name,
ps->data[pos].age,
ps->data[pos].sex,
ps->data[pos].tele,
ps->data[pos].addr);
}
}
void ModifyContact(const struct Contact* ps){
char name[NAME];
printf("please input name for modify:>");
scanf("%s",name);
int pos= FindByNme(ps,name);
if(pos==-1) {
printf("member is null");
} else{
printf("please input modify name");
scanf("%s",ps->data[pos].name);
printf("please input modify ege");
scanf("%d",&(ps->data[pos].age));
printf("please input modify sex");
scanf("%s",ps->data[pos].sex);
printf("please input modify tele");
scanf("%s",ps->data[pos].tele);
printf("please input modify addr");
scanf("%s",ps->data[pos].addr);
printf("modify sucessfully\n");
printf("%-20s\t%-4s\t%-5s\t%-12s\t%-20s\n","name","age","sex","tele","addr");
//打印出来查找到的结果
printf("%-20s\t%-4d\t%-5s\t%-12s\t%-20s\n",
ps->data[pos].name,
ps->data[pos].age,
ps->data[pos].sex,
ps->data[pos].tele,
ps->data[pos].addr);
}
}
void SortContact(struct Contact* ps){
for (int i = 0; i < ps->size; ++i) {
for (int j = i+1; j < ps->size; ++j) {
if(ps->data[i].age>ps->data[j].age){
//交换位置
struct PeoInfo tmp;
tmp=ps->data[i];
ps->data[i]=ps->data[j];
ps->data[j]=tmp;
}
}
}
printf("sort successfully");
ShowContact(ps);
}
void DestroyContact( Contact* ps){
free(ps->data);
ps->data=NULL;
printf("空间已回收\n");
}
test.c
//
// Created by 提灯前行 on 2023/11/30.
//
#include "contact.c"
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");
}
int main(){
int input=0;
//创建通讯录
struct Contact con;//通讯录,包含data指针和size、capacity
//初始化通讯录
InitContact(&con);
do {
menu();
printf("please select:>");
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:
//释放动态开辟的空间
DestroyContact(&con);
printf("exit\n");
break;
default:
printf("input error\n");
break;
}
} while (input);
}