进阶版——C语言实现基础通讯录系统

前言:

相较于第一版而言:

1.容量由静态存储改为动态开辟

2.添加了文件保存功能

3.在选择功能界面输入非数字的指令不会死循环

错误:依旧好多,而且老是搞不明白数据存放位置,该用content还是message

头文件

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<memory.h>
#include<string.h>
#include<stdlib.h>
#include<ctype.h>

#define max_name 12
#define max_sex 10
#define max_address 30
#define init_cap 5
#define add_cap 2
//#define max_person 10000

//信息包含的内容
typedef struct message_content//漏掉typedef,导致main.c中struct无法省略
{
	char name[max_name];
	char sex[max_sex];
	int age;
	int tele;
	char address[max_address];
}content;

//动态访问
typedef struct person_message
{
	content* all_data;//指向内存申请的空间,存放数据信息
	int capacity;//通讯录总容量
	int available_data;//可用信息的个数
}message;

//静态存储
通讯录
//typedef struct person_message
//{
//	content all_data[max_person];//通讯录总容量
//	int available_data;//可用信息的个数
//}message;


//函数声明(写在message上面且无struct的话会报错)
void init_message(message* mess);
void add_message(message* mess);
void print_message(const message* mess);//根本没想到要固定mess
void delete_message(message* mess);
void search_message(message* mess);
int find_all_names(message* mess, char name[], int ret[]);//没考虑到删除、修改、查询都要用到"查找"指令
void modify_message(message* mess);
void sort_message(message* mess);
void sort_menu();
void sort1(message* mess);
void sort2(message* mess);
void release_message(message* mess);
void fwrite_message(message* mess);
void fread_message(message* mess);
void expend_capacity(message* mess);

主函数

cpp 复制代码
#include"address_book.h"

void menu()
{
	printf("\n  ********************************************\n");
	printf("  ********************************************\n");
	printf("  ******    通   讯   录   系   统     *******\n");
	printf("  ******    0.退出系统     1.增加信息  *******\n");
	printf("  ******    2.删除信息     3.查找信息  *******\n");
	printf("  ******    4.修改信息     5.排序信息  *******\n");
	printf("  ******    6.打印信息     7.保存信息  *******\n");
	printf("  ********************************************\n");
	printf("  ********************************************\n");
}

enum option
{
	EXIT,
	ADD,
	DELETE,
	SEARCH,
	MODIFY,
	SORT,
	PRINT,
	SAVE
};
int main()
{
	//创建通讯录
	message mess;
	//初始化通讯录(&mess不是mess或 * mess;传地址而不是传值)
	init_message(&mess);
	int input = 0;
	if (isdigit)
	{
		do
		{
			menu();
			printf("请输入你的选择: ");
			scanf("%d", &input);
			switch (input)
			{
			case EXIT:
				printf("退出系统!\n");
				//内存释放/回收
				release_message(&mess);
				break;
			case ADD:
				printf("增加信息:\n");
				add_message(&mess);
				break;
			case DELETE:
				printf("删除信息:\n");
				delete_message(&mess);
				break;
			case SEARCH:
				printf("查找信息:\n");
				search_message(&mess);
				break;
			case MODIFY:
				printf("修改信息:\n");
				modify_message(&mess);
				break;
			case SORT:
				printf("排序信息:\n");
				sort_message(&mess);
				break;
			case PRINT:
				printf("打印信息:\n");
				print_message(&mess);
				break;
			case SAVE:
				//数据写入
				fwrite_message(&mess);
				printf("保存信息成功!\n");
				break;
			default:
				printf("无该选项,请重新输入!\n");
				break;
			}
		} while (input);
	}
	return 0;
}

功能实现

cpp 复制代码
#include"address_book.h"

//动态存储
void init_message(message* mess)
{
	//mess->all_data写成content* all_data,导致无法输入信息
	mess->all_data = (content*)malloc(init_cap * sizeof(content));
	if (mess->all_data == NULL)//不记得要考虑没内存的前提
	{
		perror("init_message");
		return;
	}
	mess->capacity = init_cap;
	mess->available_data = 0;

	//读取信息(不能在主函数add中调用,否则删不掉)
	fread_message(mess);//没在这里调用,导致始终无法读取数据!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
}

void fread_message(message* mess)
{
	content temp = { 0 };
	FILE* pf = fopen("C:\\Users\\张艺兴的圈外女友\\source\\repos\\Project5\\通讯录(动态开辟)\\address_book.txt", "r");
	while (fread(&temp,sizeof(content),1,pf))
	{
		expend_capacity(mess);
		mess->all_data[mess->available_data] = temp;
		mess->available_data++;
	}
	fclose(pf);
	pf = NULL;
}

初始化通讯录信息
//void init_message(message* mess)
//{
//	mess->available_data = 0;
//	memset(mess->all_data, 0, sizeof(mess->all_data));//memset内存设置(初始化可全0或非全0)
//}

void expend_capacity(message* mess)
{
	if (mess->available_data == mess->capacity)
	{
		//(mess->capacity + add_cap)*sizeof(content)写成mess->capacity + add_cap
		content* ptr = (content*)realloc(mess->all_data, (mess->capacity + add_cap) * sizeof(content));
		if (ptr != NULL)
		{
			mess->all_data = ptr;
			mess->capacity += add_cap;
			printf("扩容成功!\n");
		}
		else
		{
			printf("存储已满,无法添加信息!\n");
		}
	}
}

//动态访问
void add_message(message* mess)
{
	//扩容
	expend_capacity(mess);
	if (isalnum)
	{
		printf("请输入姓名:");
		scanf("%s", mess->all_data[mess->available_data].name);
		printf("请输入性别:");
		scanf("%s", mess->all_data[mess->available_data].sex);//无法读取性别原因:scanf中加了"\n"!!!!!!
		//int i=0;scanf("%d",&i);
		//要取地址
		printf("请输入年龄:");
		scanf("%d", &(mess->all_data[mess->available_data].age));
		printf("请输入联系方式:");
		scanf("%d", &(mess->all_data[mess->available_data].tele));
		printf("请输入住址:");
		scanf("%s", mess->all_data[mess->available_data].address);

	}
	mess->available_data++;//已用空间+1
	printf("输入信息成功!\n");
}

void fwrite_message(message* mess)
{
	int i = 0;
	FILE* pf = fopen("C:\\Users\\张艺兴的圈外女友\\source\\repos\\Project5\\通讯录(动态开辟)\\address_book.txt", "w");
	if (pf == NULL)
	{
		perror("fwrite_message");
		return;
	}
	for ( i = 0; i < mess->available_data; i++)
	{
		//mess->all_data+i:数组名+i该元素的地址
		fwrite(mess->all_data+i, sizeof(content), 1, pf);
	}
	fclose(pf);
	pf = NULL;
}

void release_message(message* mess)
{
	
	free(mess->all_data);
	mess->all_data = NULL;
	mess->available_data = 0;
	mess->capacity = 0;
}

静态存储
//void add_message(message* mess)
//{
//	if (mess->available_data == max_person)
//	{
//		printf("通讯录可用信息容量已满,无法添加新信息!\n");
//		return;//结束执行,但不用返回值
//	}
//	else
//	{
//		// mess->all_data[mess->available_data]:将可用空间的信息放进总容量对应下标的空间
//		// 把第n个添加进来的信息放在总数据中下标为n的位置
//		// available=1 ==》all_data=1
//		// 例:第0个学生放在下标为0的all_data中
//		printf("请输入姓名:");
//		scanf("%s", mess->all_data[mess->available_data].name);
//		printf("请输入性别:");
//		scanf("%s", mess->all_data[mess->available_data].sex);//无法读取性别原因:scanf中加了"\n"!!!!!!
//		//int i=0;scanf("%d",&i);
//		//要取地址
//		printf("请输入年龄:");
//		scanf("%d", &(mess->all_data[mess->available_data].age));
//		printf("请输入联系方式:");
//		scanf("%d",&(mess->all_data[mess->available_data].tele));
//		printf("请输入住址:");
//		scanf("%s", mess->all_data[mess->available_data].address);
//		mess->available_data++;//已用空间+1
//		printf("输入信息成功!\n");
//	}
//}

void print_message(const message* mess)//根本没想到要固定mess
{
	//打印标题(-左对齐)
	printf("%-8s\t%-8s\t%-8s\t%-20s\t%-20s\n", "姓名", "性别", "年龄", "联系方式", "住址");
	//打印内容
	int i = 0;
	for ( i = 0; i < mess->available_data; i++)
	{
		printf("%-8s\t%-8s\t%-8d\t%-20d\t%-20s\n",//忘记把 "年龄", "联系方式"改为"%d"
			mess->all_data[i].name,//没有考虑到【i】,而是写成all_data.name
			mess->all_data[i].sex,
			mess->all_data[i].age,
			mess->all_data[i].tele,
			mess->all_data[i].address);
	}
}
//static:只能在本源文件用,不能被其他源文件使用
//又是一个想不到的地方
static int find_all_names(message* mess, char name[], int ret[])
{
	int i = 0;
	int j = 0;
	for (i = 0; i < mess->available_data; i++)
	{
		//两个字符串不能直接用等号!!!!!
		if (strcmp(name, mess->all_data[i].name) == 0)
		{
			ret[j] = i;//将所有名字相同的下标存储到一个数组里
			j++;//记录存储在ret数组中的索引数量
		}
	}
	return j;
}

void delete_message(message* mess)
{
	//判断
	if (mess->available_data == 0)
	{
		printf("通讯录为空!无法删除!\n");
		return;
	}
	int* ret = (int*)malloc(mess->capacity * sizeof(int));
	if (ret == NULL)//差点忘了判断
	{
		return;
	}
	//int ret[max_person] = { 0 };//将所有名字相同的下标存储到一个数组里
	char name[max_name] = { 0 };//写成int类型(无语)
	printf("请输入你要删除的(信息)名字:");
	//int改成char之后,%d没改成%s,导致无论输什么,都提示信息不存在!!!!
	scanf("%s", name);
	int count = find_all_names(mess, name, ret);//没考虑到删除、修改、查询都要用到"查找"指令
	if (count == 0)
	{
		printf("你输入的信息不存在!无法删除!\n");
		return;
	}
	//执行
	int i = 0;
	//count:相同名字的数量,for条件要么写成count-1,i>0;要么写成count,i>=0,导致怎么删除都不对!!!
	for (i = count - 1; i >= 0; i--)
	{
		int index = ret[i];//想不到!根本想不到!!!!
		while (index < mess->available_data - 1)
		{
			//available_data - 1,count - 1原因:最后一个数字替换之后,还会出现最后一个数字下标+1的数,栈溢出
			//例:1 2 3 4 5,把1删除后,2覆盖到1的位置,3覆盖2位置,4覆盖3,5覆盖4,如果不减一,会出现5后面的空值覆盖5的情况
			mess->all_data[index] = mess->all_data[index + 1];
			index++;
		}
		mess->available_data--; //--写成++,导致无论怎么删,打印依旧还存在
	}
	printf("删除成功!\n");
	free(ret);//一开始没加进去,私密马赛
	ret = NULL;//这也能忘
}

void search_message(message* mess)
{
	char name[max_name] = { 0 };
	while (1)
	{
		printf("请输入你要查找的名字:");
		scanf("%s", name);
		int* ret = (int*)malloc(mess->capacity * sizeof(int)); 
		if (ret == NULL)//差点忘了判断
		{
			return;
		}
		//int ret[max_person] = { 0 };//想不到,根本想不到!!!!!!
		int count = find_all_names(mess, name, ret);
		if (count == 0)
		{
			printf("你输入的信息不存在!无法查找!\n");
			return;
		}
		else
		{
			printf("已找到信息!\n");
			printf("%-8s\t%-8s\t%-8s\t%-20s\t%-20s\n", "姓名", "性别", "年龄", "联系方式", "住址");
			int i = 0;
			for (i = 0; i < count; i++)
			{
				printf("%-8s\t%-8s\t%-8d\t%-20d\t%-20s\n",
					mess->all_data[ret[i]].name,//ret[i]:将所有名字相同的下标存储到一个数组里
					mess->all_data[ret[i]].sex,
					mess->all_data[ret[i]].age,
					mess->all_data[ret[i]].tele,
					mess->all_data[ret[i]].address);
			}
			break;
		}
		free(ret);
		ret = NULL;
	}
}

void modify_message(message* mess)
{
	char name[max_name] = { 0 };
	printf("请输入你要修改的数据姓名:");
	scanf("%s", name);
	int* ret = (int*)malloc(mess->capacity * sizeof(int));
	if (ret == NULL)//差点忘了判断
	{
		return;
	}
	//int ret[max_person] = { 0 };//想不到,根本想不到!!!!!!
	int count = find_all_names(mess, name, ret);
	int i = 0;
	if (count == 0)
	{
		printf("你输入的信息不存在!无法修改!\n");
		return;
	}
	for (i = 0; i < count; i++) 
	{
		char choice = 0;
		printf("找到第%d个匹配的人,名字是%s。确定修改信息吗(y/n)? 或者继续输入名字:%s 进行查询?\n", i + 1, mess->all_data[ret[i]].name, mess->all_data[ret[i]].name);
		scanf(" %c", &choice); //注意空格,它可以消除缓冲区中的任何空白字符(想不到!根本想不到!!!!)
		if (choice == 'y' || choice == 'Y') 
		{
			printf("请输入姓名:");
			scanf("%s", mess->all_data[ret[i]].name);// ret[i]:将所有名字相同的下标存储到一个数组里
			printf("请输入性别:");
			scanf("%s", mess->all_data[ret[i]].sex);
			printf("请输入年龄:");
			scanf("%d", &(mess->all_data[ret[i]].age));
			printf("请输入联系方式:");
			scanf("%d", &(mess->all_data[ret[i]].tele));
			printf("请输入住址:");
			scanf("%s", mess->all_data[ret[i]].address);
			printf("修改信息成功!\n");
			break;
		}
	}
	if (i == count)
	{
		printf("共%d个数据,已查询完毕,请重新选择!\n",count);
	}
	free(ret);
	ret = NULL;

}

void sort_menu()
{
	printf("*********************\n");
	printf("******* 1.正序 ******\n");
	printf("******* 2.倒序 ******\n");
	printf("*********************\n");
}
void sort1(message* mess)
{
	int i = 0;
	int j = 0;
	int temp_age = 0;
	int temp_tele = 0;
	char temp_name[max_name] = { 0 };
	char temp_sex[max_sex] = { 0 };
	char temp_address[max_address] = { 0 };
	for ( i = 0; i < mess->available_data - 1; i++)
	{
		for ( j = 0; j < mess->available_data - 1 - i; j++)
		{
			if (mess->all_data[j].age > mess->all_data[j + 1].age)
			{
				temp_age = mess->all_data[j].age;//没把name改为age,导致报错
				mess->all_data[j].age = mess->all_data[j + 1].age;
				mess->all_data[j + 1].age = temp_age;

				temp_tele = mess->all_data[j].tele;//没把name改为age,导致报错
				mess->all_data[j].tele = mess->all_data[j + 1].tele;
				mess->all_data[j + 1].tele = temp_tele;

				strcpy(temp_name, mess->all_data[j].name);//不交换name,就显示只交换了年龄,其他信息未排序
				strcpy(mess->all_data[j].name, mess->all_data[j + 1].name);
				strcpy(mess->all_data[j + 1].name, temp_name);

				strcpy(temp_sex, mess->all_data[j].sex);
				strcpy(mess->all_data[j].sex, mess->all_data[j + 1].sex);
				strcpy(mess->all_data[j + 1].sex, temp_sex);

				strcpy(temp_address, mess->all_data[j].address);
				strcpy(mess->all_data[j].address, mess->all_data[j + 1].address);
				strcpy(mess->all_data[j + 1].address, temp_address);
			}
		}
	}
	printf("%-8s\t%-8s\t%-8s\t%-20s\t%-20s\n", "姓名", "性别", "年龄", "联系方式", "住址");
	for (j = 0; j < mess->available_data; j++)
	{
		printf("%-8s\t%-8s\t%-8d\t%-20d\t%-20s\n",
			mess->all_data[j].name,
			mess->all_data[j].sex,
			mess->all_data[j].age,
			mess->all_data[j].tele,
			mess->all_data[j].address);
	}
}
void sort2(message* mess)
{
	int i = 0;
	int j = 0;
	int temp_age = 0;
	int temp_tele = 0;
	char temp_name[max_name] = { 0 };
	char temp_sex[max_sex] = { 0 };
	char temp_address[max_address] = { 0 };
	for (i = 0; i < mess->available_data - 1; i++)
	{
		for (j = 0; j < mess->available_data - 1 - i; j++)
		{
			if (mess->all_data[j].age < mess->all_data[j + 1].age)
			{
				temp_age = mess->all_data[j].age;//没把name改为age,导致报错
				mess->all_data[j].age = mess->all_data[j + 1].age;
				mess->all_data[j + 1].age = temp_age;

				temp_tele = mess->all_data[j].tele;//没把name改为age,导致报错
				mess->all_data[j].tele = mess->all_data[j + 1].tele;
				mess->all_data[j + 1].tele = temp_tele;

				strcpy(temp_name, mess->all_data[j].name);//不交换name,就显示只交换了年龄,其他信息未排序
				strcpy(mess->all_data[j].name, mess->all_data[j + 1].name);
				strcpy(mess->all_data[j + 1].name, temp_name);

				strcpy(temp_sex, mess->all_data[j].sex);
				strcpy(mess->all_data[j].sex, mess->all_data[j + 1].sex);
				strcpy(mess->all_data[j + 1].sex, temp_sex);

				strcpy(temp_address, mess->all_data[j].address);
				strcpy(mess->all_data[j].address, mess->all_data[j + 1].address);
				strcpy(mess->all_data[j + 1].address, temp_address);
			}
		}
	}
	printf("%-8s\t%-8s\t%-8s\t%-20s\t%-20s\n", "姓名", "性别", "年龄", "联系方式", "住址");
	for (j = 0; j < mess->available_data; j++)
	{
		printf("%-8s\t%-8s\t%-8d\t%-20d\t%-20s\n",
			mess->all_data[j].name,
			mess->all_data[j].sex,
			mess->all_data[j].age,
			mess->all_data[j].tele,
			mess->all_data[j].address);
	}
}
void sort_message(message* mess)
{
	int i = 0;
	sort_menu();
	printf("请输入你要排序的方式(年龄):");
	scanf("%d", &i);
	switch (i)
	{
	case 1:
		sort1(mess);//woc!!!!!!!!这里是传mess,而不是&mess,否则始终无法输出数据
		break;
	case 2:
		sort2(mess);
		break;
	default:
		break;
	}
}
//mess已经是一个指针,它存储了message结构体的地址。当你使用&mess时,你其实是在获取这个指针变量自身的地址,而不是它所指向的message结构体。
// 因此,在这种情况下,你应该直接传递mess(即地址),而不是&mess。
// 这样,sort1函数就能正确地接收到一个指向message结构体的指针,并能够操作这个结构体。
//总的来说,选择传值还是传地址取决于你是否需要在函数内部修改实参。
//如果需要修改实参,那么应该使用传地址;如果不需要修改实参或者不希望实参被修改,那么应该使用传值。
相关推荐
SRY122404192 小时前
javaSE面试题
java·开发语言·面试
lb36363636363 小时前
介绍一下数组(c基础)(详细版)
c语言
无尽的大道3 小时前
Java 泛型详解:参数化类型的强大之处
java·开发语言
ZIM学编程3 小时前
Java基础Day-Sixteen
java·开发语言·windows
放逐者-保持本心,方可放逐3 小时前
react 组件应用
开发语言·前端·javascript·react.js·前端框架
一丝晨光4 小时前
编译器、IDE对C/C++新标准的支持
c语言·开发语言·c++·ide·msvc·visual studio·gcc
阮少年、4 小时前
java后台生成模拟聊天截图并返回给前端
java·开发语言·前端
代码小鑫4 小时前
A027-基于Spring Boot的农事管理系统
java·开发语言·数据库·spring boot·后端·毕业设计
程序猿-瑞瑞5 小时前
11 go语言(golang) - 数据类型:结构体
开发语言·golang
奶味少女酱~5 小时前
常用的c++特性-->day02
开发语言·c++·算法