通讯录动态内存开辟,c语言知识覆盖

一.内容概况

本文章介绍通讯录的动态内存开辟的版本,同时基本覆盖了c语言学习的基本知识,可以将所写的内容保存到一个test.c的文件中。、

二.代码实现

此部分介绍整个代码的内容和代码的实现


#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<errno.h>
void menu()
{
	printf("********************\n");
	printf("*****1.添加       2.删除\n");
	printf("*****3.寻找    4.修改\n");
	printf("*****5.显示      6.排序\n");
	printf("*****0.退出      7.存储      \n");
	printf("***********************\n");
}
enum opition//枚举 
{

	add=1,
	del,
	search,
	modify,
	show,
	sort,
	save 
};
struct info
{
	char name[20];
	char tele[100];
	int age;
	char sex[5];
	char add[30];
};
void loadcontact(contast*ps)
{
	FILE*pf=fopen("test.txt","rb");
	if(pf==NULL)
	printf("%s\n",strerror(errno));
	struct info tmp={0
	};
	while(fread(&tmp,sizeof(struct info),1,pf))
	{
		ps->date[ps->num]=tmp;
		ps->num++;
	}
	fclose(pf);
	pf=NULL;
}
void initializationcontact(contast *ps)
{
	ps->date=(struct info*)malloc(3*sizeof(struct info));
	if(ps->date==NULL)
	return ;
	ps->num=0;
	ps->capacity=3;
	loadcontact(ps); 
}
void check_capacity(contast*ps)
{
	if(ps->num==ps->capacity)
	{
		
	    	struct info *ptr=(struct info*)realloc(ps->date,(ps->capacity+2)*sizeof(struct info)); 
	    	if(ptr!=NULL)
	    	{
	    	ps->date=ptr;
	    	ps->capacity+=2;
	    	puts("增容成功"); 
	    	}
	    	else
			puts("增容失败"); 
	    	
	}
 } 
void addcontact(contast*ps)
{
	check_capacity(ps); 
	if(ps->num==ps->capacity) 
	printf("通讯录已满\n"); 
	else
	{
		printf("请输入名字>\n"); 
		scanf("%s",ps->date[ps->num].name);//ps->date指向数组ps->num使用结构体中的数据num作为数组的下标 .表示访问结构体数组中的内容 
		printf("请输入年龄>\n");
		scanf("%d",&(ps->date[ps->num].age));
		printf("请输入性别\n");
		scanf("%s",ps->date[ps->num].sex);
		printf("请输入地址\n");
		scanf("%s",ps->date[ps->num].add);
		printf("请输入电话号码\n");
		scanf("%s",ps->date[ps->num].tele); 
		ps->num++;
		printf("添加完成\n");  
		printf("%d\n",ps->num);
	}
}
void delcontact(contast*ps)//删除指定的联系人 
{
	char name[20];
	printf("请输入要删除人的名字\n");
	scanf("%s",name); 
	int i;
	for(i=0;i<ps->num;i++)
	{
		if(strcmp(name,ps->date[i].name)==0)
		{
			break;
		}
	}
	if(i==ps->num){
	printf("找不到\n"); }
	else
	{  
		for(int j=i;j<ps->num-1;j++)
		{
			ps->date[j]=ps->date[j+1];
		}
		ps->num--;
		printf("删除成功\n"); 
   } 
}

void showcontact( const contast*ps)
{
	
	if(ps->num==0)
	{
		printf("通讯录为空\n");
	}
	else
	{
	printf("%20s\t%4s\t%5s\t%12s\t%20s\n","名字","年龄","性别","电话","地址"); 
		for(int i=0;i<ps->num;i++)
		{
			printf("%20s\t%4d\t%5s\t%12s\t%20s\n",
			ps->date[i].name,
			ps->date[i].age,
			ps->date[i].sex,
			ps->date[i].tele,
			ps->date[i].add); //输出的时候前面加-为左对齐,不加符号或者加正号表示右对齐 
		}
	}
}
void searchcontact(contast*ps)//寻找指定的人并输出 
{
	char name[20];
	printf("请输入要查找人的名字\n");
	scanf("%s",name);
	int i;
	for(i=0;i<ps->num;i++)
	{
		if(strcmp(name,ps->date[i].name)==0)
		{
			printf("%20s\t%4d\t%5s\t%12s\t%20s\n",
			ps->date[i].name,
			ps->date[i].age,
			ps->date[i].sex,
			ps->date[i].tele,
			ps->date[i].add); 
		}
	}
	if(i==ps->num)
	printf("要查找的人不存在\n");
}
void modifycontact(contast*ps)
{
	char name[20];
	printf("请输入要修改的名字\n");
	scanf("%s",name);
	int i;
	for(i=0;i<ps->num;i++)
	{
		if(strcmp(name,ps->date[i].name)==0)
		{
		printf("请输入名字>\n"); 
		scanf("%s",ps->date[i].name);//ps->date指向数组ps->num使用结构体中的数据num作为数组的下标 .表示访问结构体数组中的内容 
		printf("请输入年龄>\n");
		scanf("%d",&(ps->date[i].age));
		printf("请输入性别\n");
		scanf("%s",ps->date[i].sex);
		printf("请输入地址\n");
		scanf("%s",ps->date[i].add);
		printf("请输入电话号码\n");
		scanf("%s",ps->date[i].tele); 
		printf("修改完成\n"); 
		}
	}
	if(i==ps->num)
	printf("找不到\n");
	
}
void savecontact(contast*ps)
{
	FILE*pf=fopen("test.txt","wb");
	if(pf==NULL)
	{
		printf("%s\n",strerror(errno));
	}
	char arr[1024];
	for(int i=0;i<ps->num;i++)
	{
	fwrite(&(ps->date[i]),sizeof(struct info),1,pf);
	}
}
void destroycontact(contast*ps)
{
	free(ps->date);
	ps->date=NULL;
}
int main()
{
	int i;
	contast con;//{(struct info*)malloc(3*sizeof(struct info)),0,3};//通讯录的初始化,全部赋值为0
	initializationcontact(&con); 
	printf("当前容量%d\n",con.capacity);
	do
	{
		menu();
		printf("请选择");
		scanf("%d", &i);
		switch (i)
		{
		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:
			break;
		case save:
			savecontact(&con);
			break;
		case 0:
			savecontact(&con);
			destroycontact(&con);
			printf("退出通讯录\n");
			break;
		default:
			printf("选择错误,重新选择\n");
			break;
		}
	} while (i);
	return 0;
}

阅读了此大段代码之后我们对代码进行部分介绍。

三.代码框架

1.因为通讯录是存放一个用户的信息,用一个结构体来整体存放需要存放的用户的信息

struct info
{
	char name[20];
	char tele[100];
	int age;
	char sex[5];
	char add[30];
};//该结构体负责记录每个用户的各类信息

我们看到这个结构体是用来存放用户的信息,此处可以有一个小范围的修改,使用#define定义宏来代替直接在数组中输入数值,便于以后的修改,此处我们不多赘述

typedef struct contct
{
	struct info* date;//结构体数组记录1000个用户的信息 此时是静态的内存开辟,可能会造成内存的浪费 
	int num;//num负责记录当前的用户个数
	int capacity;//记录当前的用户容量 
}contast;//该结构体负责记录用户个数和大框架

2. 此处我们再使用一个结构体用来存放当前存放的数量和该通讯录的容量

此时我们定义了一个结构体指针,该指针是用于以后的动态内存的开辟,如果我们直接使用一个结构体数组的话不仅不能确定数量还有可能造成空间的浪费,所以我们此时使用结构体指针,使用动态内存开辟来节约空间提高效率,

值得我们注意的是此处使用了一个typedef来修改我们此处定义的结构体命名为(contast)方便我们以后的操作

3.枚举的使用和菜单的打印

void menu()
{
	printf("********************\n");
	printf("*****1.添加       2.删除\n");
	printf("*****3.寻找    4.修改\n");
	printf("*****5.显示      6.排序\n");
	printf("*****0.退出      7.存储      \n");
	printf("***********************\n");
}
enum opition//枚举 
{

	add=1,
	del,
	search,
	modify,
	show,
	sort,
	save 
};

枚举类型可以对枚举中的内容规定顺序,以便于我们在主函数中使用Switch case语句

菜单就是简单的printf打印菜单,此处不多赘述 

4.接下来是主函数中的内容

int main()
{
	int i;
	contast con;//{(struct info*)malloc(3*sizeof(struct info)),0,3};//通讯录的初始化,全部赋值为0
	initializationcontact(&con); 
	printf("当前容量%d\n",con.capacity);
	do
	{
		menu();
		printf("请选择");
		scanf("%d", &i);
		switch (i)
		{
		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:
			break;
		case save:
			savecontact(&con);
			break;
		case 0:
			savecontact(&con);
			destroycontact(&con);
			printf("退出通讯录\n");
			break;
		default:
			printf("选择错误,重新选择\n");
			break;
		}
	} while (i);
	return 0;
}

此处我们先定义一个结构体变量,以后的操作都在这个变量的基础上进行操作,

首先我们对该结构体进行初始化,我们这里规定该结构体重最初的最大容量为3,存储的数据为0

之后我们使用do while循环来实现每次我们对通讯录的操作,每次的操作都需要打印菜单,做出选择,使用Switch case语句来实现我们选择内容的执行

四.通讯录中函数部分的详解 

1.初始化

void initializationcontact(contast *ps)
{
	ps->date=(struct info*)malloc(3*sizeof(struct info));
	if(ps->date==NULL)
	return ;
	ps->num=0;
	ps->capacity=3;
	loadcontact(ps); 
}

初始化结构体,我们使用malloc动态开辟一个内存空间给结构体中的数据,如果开辟失败直接退出,否则对结构体中的内容初始化。 

2.增加

void addcontact(contast*ps)
{
	check_capacity(ps);
	//空间满了增加空间,否则不操作,此处我们还需要引用一个检查通讯录空间的函数
	if(ps->num==ps->capacity) 
	printf("通讯录已满\n"); 
	else
	{
		printf("请输入名字>\n"); 
		scanf("%s",ps->date[ps->num].name);//ps->date指向数组ps->num使用结构体中的数据num作为数组的下标 .表示访问结构体数组中的内容 
		printf("请输入年龄>\n");
		scanf("%d",&(ps->date[ps->num].age));
		printf("请输入性别\n");
		scanf("%s",ps->date[ps->num].sex);
		printf("请输入地址\n");
		scanf("%s",ps->date[ps->num].add);
		printf("请输入电话号码\n");
		scanf("%s",ps->date[ps->num].tele); 
		ps->num++;
		printf("添加完成\n");  
		printf("%d\n",ps->num);
	}
}

对通讯录中的内容增加时我们使用scanf函数输入我们需要的数据,当我们对动态内存开辟的内存进行访问时使用[],函数的形参是结构体指针,结构体指针访问结构体中的变量使用->,对结构中的内容进行访问使用引用符号.

​
void check_capacity(contast*ps)
{
	if(ps->num==ps->capacity)
	{
		//增容
	    	struct info *ptr=(struct info*)realloc(ps->date,(ps->capacity+2)*sizeof(struct info)); 
	    	if(ptr!=NULL)
	    	{
	    	ps->date=ptr;
	    	ps->capacity+=2;
	    	puts("增容成功"); 
	    	}
	    	else
			puts("增容失败"); 
	    	
	}
 }

​

 检查通讯录的容量和内存的数量函数,如果此时通讯录中的数量已经等于最大容量时我们使用realloc函数对结构体中开辟的内存进行修改,实现扩容

3.删除

void delcontact(contast*ps)//删除指定的联系人 
{
	char name[20];
	printf("请输入要删除人的名字\n");
	scanf("%s",name); 
	int i;
	for(i=0;i<ps->num;i++)
	{
		if(strcmp(name,ps->date[i].name)==0)
		{
			break;
		}
	}
	if(i==ps->num){
	printf("找不到\n"); }
	else
	{
		//删除数据  
		for(int j=i;j<ps->num-1;j++)
		{
			ps->date[j]=ps->date[j+1];
		}
		ps->num--;
		printf("删除成功\n"); 
   } 
}

在删除时我们首先要找到要删除的对象,我们先输入一个用户的,我们通过对现有的用户数据进行遍历,通过使用strcmp函数作比较,找到要删除的人,然后再把该个数据之后的数据统一向前移动一个名额实现对前面数据的覆盖。

4.显示

void showcontact( const contast*ps)
{
	
	if(ps->num==0)
	{
		printf("通讯录为空\n");
	}
	else
	{
	printf("%20s\t%4s\t%5s\t%12s\t%20s\n","名字","年龄","性别","电话","地址"); 
		for(int i=0;i<ps->num;i++)
		{
			printf("%20s\t%4d\t%5s\t%12s\t%20s\n",
			ps->date[i].name,
			ps->date[i].age,
			ps->date[i].sex,
			ps->date[i].tele,
			ps->date[i].add); //输出的时候前面加-为左对齐,不加符号或者加正号表示右对齐 
		}
	}
}

关于显示我们没有什么多余要讲的,只是简单的printf函数和一些简单的转义字符

5.修改

void modifycontact(contast*ps)
{
	char name[20];
	printf("请输入要修改的名字\n");
	scanf("%s",name);
	int i;
	for(i=0;i<ps->num;i++)
	{
		if(strcmp(name,ps->date[i].name)==0)
		{
		printf("请输入名字>\n"); 
		scanf("%s",ps->date[i].name);//ps->date指向数组ps->num使用结构体中的数据num作为数组的下标 .表示访问结构体数组中的内容 
		printf("请输入年龄>\n");
		scanf("%d",&(ps->date[i].age));
		printf("请输入性别\n");
		scanf("%s",ps->date[i].sex);
		printf("请输入地址\n");
		scanf("%s",ps->date[i].add);
		printf("请输入电话号码\n");
		scanf("%s",ps->date[i].tele); 
		printf("修改完成\n"); 
		}
	}
	if(i==ps->num)
	printf("找不到\n");
	
}

修改需要像删除一样,需要对已有的用户数据进行遍历,找到要修改的对象,进行修改

6.保存

void savecontact(contast*ps)
{
	FILE*pf=fopen("test.txt","wb");
	if(pf==NULL)
	{
		printf("%s\n",strerror(errno));
	}
	char arr[1024];
	for(int i=0;i<ps->num;i++)
	{
	//sprintf(arr,"%s %s %d %s %s",ps->date->name,ps->date->tele,ps->date->age,ps->date->sex,ps->date->add);
	fwrite(&(ps->date[i]),sizeof(struct info),1,pf);
	}
}

当我们结束对用户数据的输入时需要对用户数据实现保存,这里我们使用文件函数,先打开一个文件,然后使用发fwrite函数把输入的用户信息存储到我们已经设置好的一个文件中。

7.加载

void loadcontact(contast*ps)
{
	FILE*pf=fopen("test.txt","rb");
	if(pf==NULL)
	printf("%s\n",strerror(errno));
	struct info tmp={0
	};
	while(fread(&tmp,sizeof(struct info),1,pf))
	{
		ps->date[ps->num]=tmp;
		ps->num++;
	}
	fclose(pf);
	pf=NULL;
}

当我们打开一个通讯录进行修改时可以看到我们以前存储的用户数据,这时候就需要我们对已经存储的用户数据进行加载,加载时我们使用fread函数,对文件中的数据实现逐一加载。

五.总结

通讯录使我们在学习完c语言之后检验我们学习效果的一个好的选择,本文介绍了一种较为全面的通讯录实现的方式,希望帮助到大家。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值