c语言通讯录实现(动态内存分配+操作文件)3.0

这个通讯录能比较简单实现增、删、除、改等功能,并且用了动态开辟函数和文件函数能实现通讯录大小动态增长并将通讯录信息保存在你本地文件中。各位小伙伴们可以看看思路,或者优化一下为学校的大作业做准备

主要的代码运行框架

#include"address list.h"
enum selection     //这里巧妙地将菜单选项设置为枚举常量,利用其对应的整型值来做选择,使代码可读性提高
{
	EXIT,
	ADd,
	DELE,
	SEARCH,
	MODIFY,
	SHOW,
	CLEAN,
	SORT
};
void menu()
{
	printf("    ***********************\n");
	printf("****** 1.add     2.delete ******\n");
	printf("****** 3.search  4.modify ******\n");
	printf("****** 5.show    6.clean  ******\n");
	printf("****** 7.sort    0.exit   ******\n");
	printf("    ***********************\n");
}
void test()
{
	menu();
	Contact con;
	Initcon(&con);
	int input;
	do {                    //这里用do while循环最适合不过,先执行一次再选择要不要继续
		printf("\n请选择:>\n");
		scanf("%d", &input);
		switch (input)
		{
		case ADd:
			Add(&con);
			break;
		case DELE:
			Delete(&con);
			break;
		case SEARCH:
			Search(&con);
			break;
		case MODIFY:
			Modify(&con);
			break;
		case SHOW:
			Show(&con);
			break;
		case CLEAN:
			Clean(&con);
			break;
		case SORT:
			Sort(&con);
			break;
		case EXIT:
			Save(&con);
			Destroy(&con);
			printf("退出成功\n");
			break;
		default:
			printf("输入错误\n");
		}
	} while (input);
	return;
}
int main()
{
	test();
	return 0;
}

所需代码声明

#include<stdio.h>
#include<assert.h>
#include<string.h>
#include<stdlib.h>
#include<errno.h>
//#define定义的标识符常量,方便更改数组或者其余信息大小
#define DEFAULT_SZ 3  //通讯录初始默认大小 
#define NAME 20       //存姓名信息的数组默认大小
#define SEX 5         //存性别信息的数组默认大小
#define TELE 11       //存电话号码的数组默认大小
#define ADD 20        //存地址的数组默认大小

typedef struct Peoinfor  //将人的信息用一个结构体表示
{
	char name[NAME];
	char sex[SEX];
	int age;
	char tele[TELE];
	char add[ADD];
}Peoinfor;
typedef struct Contact  //把数据信息和人数两个数据弄成合集组合称为通讯录
{
	Peoinfor* list;       //通讯录地址
	int num;             //通讯录人数个数
	int capacity;        //容量
}Contact;
void Save(Contact* con);    //程序结束后将通讯录信息保存到本地文件中
void Destroy(Contact* con); //释放内存,销毁通讯录
void Is_full(Contact* con); 检查通讯录是否放满,扩容的
void Initcon(Contact* con); //初始化通讯录结构体信息,为存放人物信息开辟空间,腾挪上一次存放在本地文件中的通讯录信息
void Clean(Contact* con);   //清除所有数据
void Add(Contact* con);     //添加联系人
void Show(Contact* con);    //展示通讯录信息
int Findby_name(Contact* con, char* str); //通过姓名的方式查找信息的函数,因为删除函数、修改函数都要用到这个功能,所以写成函数分装出来方便使用
void Search(Contact* con);  //查找联系人函数
void Delete(Contact* con);  //删除联系人
void Modify(Contact* con);  //修改通讯录信息
void Sort(Contact* con);    //按姓名拼音首字母排序

所需代码实现

#include"address list.h"
void Save(Contact* con)
{
	assert(con);   //assert断言函数判断传进指针是否为空,是的话直接报错并显示错误所在文件、行数信息
	//打开文件,以二进制方式输出
	FILE* pf = fopen("Contact.text", "wb");
	if (pf == NULL)      //判断是否打开文件成功
	{
		printf("Save::%s\n", strerror(errno));  //打开失败的话显示错误信息及所在函数位置 
		return;          //打开失败就直接提前结束函数
	}
	//写文件
	int i;
	for (i = 0; i < con->num; i++)
	{
		fwrite(con->list+i, sizeof(Peoinfor), 1, pf);
	}//关闭文件
	fclose(pf);
	pf = NULL;
}
void Destroy(Contact* con)  
{
	assert(con);   
	free(con->list);
	con->list = NULL;
	con->capacity = 0;
	con->num = 0;
}
void Is_full(Contact* con)      
{
	assert(con);
	if (con->capacity == con->num)
	{   //这里不能直接用原本内存指针直接接收返回地址,因为realloc函数的返回值有三种情况:
		//1.在原本内存地址后直接扩容成功(这种是最好的情况)
		//2.在原本内存地址后找不到足够大空间扩容,但是在堆得其他位置找到了足够大的连续空间,此时返回值则会是这一块新空间的初始地址
		//3.没有找到足够大的连续空间,此时会返回NULL
		//2、3这两种情况用原指针直接接收返回值都会导致找不回原指针指向空间
		Peoinfor* p = (Peoinfor*)realloc(con->list, (2 + con->capacity) * sizeof(Peoinfor)); //这里默认每次增容两个位置
		if (!p)//判断是否增容成功
		{
			printf("If_full()::%s\n", strerror(errno));
			return;
		}
		
			con->list = p;          
			con->capacity += 2;  //增容多少位,容量就增加多少位
			printf("增容成功\n");
		
	}
}

void Initcon(Contact* con)
{
	int i = 0;
	assert(con);
	Peoinfor* p = (Peoinfor*)malloc(DEFAULT_SZ * sizeof(Peoinfor));
	if (!p)//判断是否动态开辟内存成功
	{
		printf("Initcon():%s\n", strerror(errno));
		return;
	}
	con->list = p;
	con->capacity = DEFAULT_SZ;     //将容量初始化为设置的通讯录默认大小
	con->num = 0;              
	//把上一次保存在本地文件中的数据腾挪到内存中
	//以只读二进制文件的方式打开文件
	FILE* pf = fopen("Contact.text", "rb");
	if (pf == NULL)
	{
		printf("Initcon:: %s\n", strerror(errno));   //显示错误信息及所在函数位置
		return;
	}
	Peoinfor buf = { 0 };
	while (fread(con->list+i, sizeof(Peoinfor), 1, pf))
	{
		Is_full(con);
		i++;
		con->num++;
	}
	//关闭文件
	fclose(pf);
	pf = NULL;
	i = 0;
}
void Clean(Contact* con)
{
	assert(con);
	memset(con->list, '0', sizeof(con->list));  //内存设置函数,将通讯录信息都清除为0;
	con->num = 0;                               //当然别忘记要同时把通讯录有效个数赋为0
}
void Add(Contact* con)      //根据动态分配函数修改一下
{
	assert(con);
	Is_full(con);
	printf("请输入联系人姓名:>\n");
	scanf("%s", con->list[con->num].name);
	printf("请输入性别:>\n");
	scanf("%s", con->list[con->num].sex);
	printf("请输入年龄:>\n");
	scanf("%d", &con->list[con->num].age);
	printf("请输入电话:>\n");
	scanf("%s", con->list[con->num].tele);
	printf("请输入住址:>\n");
	scanf("%s", con->list[con->num].add);
	printf("增加联系人成功!\n");
	con->num++;   //增加完记得让有效个数+1
}
void Show(Contact* con)
{
	assert(con);
	int i;
	printf("%-15s\t%-5s\t%-4s\t%-11s\t%-15s\n", "姓名",
		"性别", "年龄", "电话", "住址");
	for (i = 0; i < con->num; i++)
	{
		printf("%-15s\t%-5s\t%-3d\t%-11s\t%-15s\n", con->list[i].name,
			con->list[i].sex, con->list[i].age, con->list[i].tele, con->list[i].add);
	}
	return;
}
int Findby_name(Contact* con, char* str)
{
	assert(con);
	int i;
	for (i = 0; i < con->num; i++)
	{
		if (!strcmp(con->list[i].name, str))    //用strcmp函数对比需查找姓名和被查找的姓名是否一致,是则返回其位置
			return i;
	}
	return -1;
}
void Search(Contact* con)
{
	assert(con);
	printf("请输入你要查找的联系人姓名:>\n");
	char s[20] = "0";
	scanf("%s", s);
	if (Findby_name(con, s))
		printf("%-15s\t%-5s\t%-4d\t%-11s\t%-15s\n", con->list[Findby_name(con, s)].name,
			con->list[Findby_name(con, s)].sex, con->list[Findby_name(con, s)].age,
			con->list[Findby_name(con, s)].tele, con->list[Findby_name(con, s)].add);
	else
		printf("找不到该联系人\n");
}
void Delete(Contact* con)
{
	assert(con);
	char s[20] = "0";
	printf("请输入你要删除的对象:>\n");
	scanf("%s", s);
	int pos = Findby_name(con, s);
	if (pos != -1)
	{
		memmove(&(con->list[pos]), &(con->list[pos + 1]), sizeof(Peoinfor) * (con->num - pos - 1));
		//因为删除函数本质就是将这个联系人后面的信息全部往前挪,覆盖掉这个信息而已,所以直接用memmove函数简单粗暴
	}

	else
		printf("找不到该删除对象\n");  
	con->num--;  //同样的别忘记让有效个数-1
}
void Modify(Contact* con)
{
	assert(con);
	int input;
	printf("请输入你要修改的联系人姓名:>\n");
	char s[20] = "0";
	scanf("%s", s);
	int pos = Findby_name(con, s);
	if (pos == -1)
		printf("找不到该联系人\n");
	else
	{
		do
		{
			printf("请选择你要修改的信息:>\n1.姓名 2.性别 3.年龄 4.电话 5.地址 0.完成\n");//这里人性化,可以选择单独修改一项信息
			scanf("%d", &input);
			switch (input)
			{
			case 1:
				scanf("%s", con->list[pos].name);
				break;
			case 2:
				scanf("%s", con->list[pos].sex);
				break;
			case 3:
				scanf("%d", &(con->list[pos].age));
				break;
			case 4:
				scanf("%s", con->list[pos].tele);
				break;
			case 5:
				scanf("%s", con->list[pos].add);
				break;
			case 0:
				printf("修改成功\n");
				break;
			default:
				printf("输入有误,请重新输入!\n");
				break;
			}
		} while (input);
	}

}

void Sort(Contact* con)
{
	assert(con);
	int i, j;
	Peoinfor t;
	for (i = 0; i < con->num - 1; i++)      //冒泡排序法的内核
	{
		for (j = 0; j < con->num - 1 - i; j++)
		{
			if (0 < strcmp(con->list[j].name, con->list[j + 1].name))
			{
				t = con->list[j];
				con->list[j] = con->list[j + 1];
				con->list[j + 1] = t;
			}
		}
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值