C语言顺序表实现动态通讯录,并将通讯录文件储存在二进制文件中(手把手教学调试,小白入)

前言与生成结果

在这里插入图片描述

在这里插入图片描述

加载通讯录表示将二进制文件的数据导入结构体中。

初始化通讯录成功表示:成功的开辟了一块动态的空间。

可以实现增删查找,全部清空通讯录,查重,保存功能。

在这里插入图片描述
在保存后会自动在桌面生成一个通讯录文本。

1.具体代码实现(生成通讯录,添加信息并查重,打印通讯录人的信息)

先考虑头文件

首先我们要声明一个人的结构体

#define MAX_NAME 20

#define MAX_SEX 5

#define MAX_TELE 20

#define MAX_ADDRESS 20

#define MAX_CAP 3

typedef struct people
{
    
    
	char name[MAX_NAME];//姓名
	int age;//年龄
	char sex[MAX_SEX];//性别
	char tele[MAX_TELE];//电话
	char address[MAX_ADDRESS];//地址
}people;

之后我们还要定义一个通讯录结构体

typedef struct contect
{
    
    
	int sz;//当前通讯录人数
	people* data;//动态开辟的空间地址
	int capacity;//最大容量
}contect;

注意:

如果有一个通讯录结构体指针p,

p->有3种情况.

1.int sz;
2.int capacity;
3.一块连续空间的地址,地址中放的都是people结构体的数据

所以我们可以知道
p->data[p->sz]与p->*(data+sz)相同找到了下标为sz的people结构体
具体数组与指针的关系见
数组与指针的关系

所以p->data[p->sz] . 就可以找到sz位置上的结构体上的不同信息

在之后我们要打印一个菜单,让人可以选择

void menu();

类似这样
在这里插入图片描述
通过选择不同的数字,执行不同的函数。

之后还要实现初始化 增删,打印通讯录的功能

void initcontect(contect* p);

void Addcontect(contect* p);

void Showcontect(contect* p);

在这里插入图片描述

我们不急去想接下来的功能,先把这些接口写好,在.c文件中写下函数的实现

菜单的打印没什么好说的

void menu()
{
    
    
	printf("|-----------------------|\n");
	printf("|        contact        |\n");
	printf("|   1.Add     2.Delete  |\n");
	printf("|   3.Search  4.Modify  |\n");
	printf("|   5.Show    6.Sort    |\n");
	printf("|        0.Exit         |\n");
	printf("|-----------------------|\n");
}

初始化通讯录结构体

void initcontect(contect* p)
{
    
    
	p->sz = 0;//起始数据为0
	p->capacity = MAX_CAP;//之前宏定义过容量
	p->data = (people*)malloc(3*sizeof(people));
	//动态开辟一块大小为3个people结构体大小的空间
	if (p->data == NULL)
	{
    
    
		printf("通讯录初始化失败,请退出程序重试\n");
		exit(1);
	}
}

添加一个人的信息

注意:

在添加人之后还要查重,万一这个人我们之前就已经记录过,就没有必要在储存她的信息了。

所以我们要一个查重函数,和一个删除函数。检查已有人的名字,当有相同的名字时,再自动把这个人删掉。而姓名是字符串strcmp()在遇到相同的字符时返回非零值,所以可以用strcmp来实现

void Addcontect(contect* p)
{
    
    
	int pos = -1;
	if (p->sz == p->capacity)
	//当现在的通讯录人=最大容量时再次调用增加函数时增容
	{
    
    
		people* ptr =
 (people*)realloc(p->data, (p->capacity + 2) * sizeof(people));
 //每次增容2个people,也可以增加不同值。
		if (ptr==NULL)
		{
    
    
			printf("内存不足,增容失败\n");
		}
		else
		{
    
    
			p->data = ptr;
		//realloc增容通讯录结构体地址可能改变,
		//详情见realloc函数所以要重新获得地址
			p->capacity += 2;//增容后总大小增大。
			printf("提示:通讯录增容成功\n");

		}
	}//储存人的信息
		printf("请输入姓名:>\n");
		scanf("%s",p->data[p->sz].name);
		printf("请输入年龄:>\n");
		scanf("%d",&p->data[p->sz].age);
		printf("请输入性别:>\n");
		scanf("%s",p->data[p->sz].sex);
		printf("请输入电话:>\n");
		scanf("%s",p->data[p->sz].tele);
		printf("请输入地址:>\n");
		scanf("%s",p->data[p->sz].address);
		p->sz++;//储存成功后现有大小加1
		system("cls");
		
		pos = search_name2(p->data[p->sz-1].name, p);
		//当search_name2函数返回-1,表示没有重名
		//当search_name2 返回其他值表示重名
		if (pos == -1)
		{
    
    
			printf("添加成功\n");
		}
		else
		{
    
    
	//发现重名,提示一下再自动把刚添加的人的信息删掉
			printf("通讯录中已经有此人\n");
			dele_name2(p->sz,p);//删除函数
			p->sz--;//大小-1
		}
}
int search_name2(char name[], contect* ps)
{
    
    
	for (int i = 0; i < ps->sz-1; i++)
	{
    
    
		if (strcmp(name, ps->data[i].name) == 0)
		{
    
    
			return i;//有重名返回非0值
		}
	}
	return -1;
}
void dele_name2(int p,contect* ps)
{
    
    
	ps->data[p - 1] = ps->data[p];
	//直接将要删除的位置后一位随机值数据放在这一位,
	//其实也可以不写,直接将记录当前通讯录人数的size减少1就行。
}
void Showcontect(contect* p)
{
    
    
	if (p->sz == 0)
	{
    
    
		system("cls");
		printf("通讯录为空\n");//当通讯录为空时直接不打印
	} 
	else
	{
    
    
		system("cls");
		printf("%-15s\t %-5s\t %-5s\t %-12s\t %-20s\t\n",
		//控制通讯录的打印形式
		 "姓名","年龄","性别","电话","地址");
		for (int i = 0; i < p->sz; i++)
		{
    
    
			printf("%-15s\t %-5d\t %-5s\t %-12s\t %-20s\t\n",
			p->data[i].name,
			p->data[i].age,p->data[i].sex,
			p->data[i].tele,p->data[i].address);
		}

	}
}

第一阶段测试

写到这里我们先开始测试我们的代码

把我们刚写的函数再添加到头文件中

#pragma once//防止重复包含头文件
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#define MAX_NAME 20

#define MAX_SEX 5

#define MAX_TELE 20

#define MAX_ADDRESS 20

#define MAX_CAP 3

typedef struct people
{
    
    
	char name[MAX_NAME];//姓名
	int age;//年龄
	char sex[MAX_SEX];//性别
	char tele[MAX_TELE];//电话
	char address[MAX_ADDRESS];//地址
}people;

typedef struct contect
{
    
    
	int sz;//当前通讯录人数
	people* data;//动态开辟的空间地址
	int capacity;//最大容量
}contect;

void menu();

void initcontect(contect* p);

void Addcontect(contect* p);

void Showcontect(contect* p);

void dele_name2(int p, contect* ps);

int search_name2(char name[], contect* ps);

在main函数中写下测试接口

#include"标头.h"//包含自己写的头文件

int main()
{
    
    
	contect con;//生成通讯录
	initcontect(&con);//初始化通讯录;
	Addcontect(&con);//添加通讯录;
	Addcontect(&con);//添加通讯录;
	Addcontect(&con);//添加通讯录;
	Addcontect(&con);//添加通讯录;
	Showcontect(&con);//打印通讯录

	return 0;
}

在这里插入图片描述
初始化通讯录接口:
在这里插入图片描述
初始化成功
在这里插入图片描述
添加信息接口:
在这里插入图片描述
在这里插入图片描述

再输入相同的人的名字检查,查重接口:

在这里插入图片描述
再添加3个不同人的信息来检查增容接口:

在这里插入图片描述

之后进入打印函数接口

在这里插入图片描述

在这里插入图片描述
清屏打印,没有问题

之后我们就可以考虑接下来的功能了

2.具体代码实现(通讯录信息查找,通讯录人信息的修改,动态通讯录内存Free)

光有这些功能还不能称为通讯录

我们还要实现查找通讯录人的功能。还要实现修改人信息的功能。

修改人的信息首先要查找到这个人的信息所以我们先写查找函数。

查找函数其实我们已经在之前写过了,原理为之前的查重函数。只不过对找到的信息处理不同而已。之前写查重函数,找到相同的名字进行删除,查找函数找到相同函数进行打印。

其次查找函数还可以选择通过名字查找,也可以选择通过电话查找,我们再给它写一个菜单。

要新加两个函数,一个是菜单函数,一个是电话查找函数

void Searchcontect(contect*ps)
{
    
    
	char name[20] = {
    
     0 };
	char tele[20] = {
    
     0 };
	int input = 0;
	int pos = 0;
	system("cls");
	do
	{
    
    
		menu_search();
		scanf("%d",&input);
		switch (input)
		{
    
    
		case 1:
			system("cls");
			printf("请输入你要查找人的名字\n");
			scanf("%s",name);
			 pos = search_name2(name, ps);
			if (pos == -1)
			{
    
    
				printf("你要找的名字不存在\n");
			}
			else
			{
    
    
				printf("%-15s\t%-4s\t%-5s\t%-12s\t%-20s\n",
				 "姓名", "年龄", "性别", "电话", "住址");
				printf("%-15s\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].address);
			}
			printf("\n");
			printf("\n");
			break;
		case 2:
			system("cls");
			printf("请输入你要查找人的电话号码\n");
			scanf("%s",tele);
			 pos = search_tele(tele, ps);
			if (pos == -1)
			{
    
    
				printf("你要查找的电话号码不存在\n");
			}
			else
			{
    
    
				printf("%-15s\t%-4s\t%-5s\t%-12s\t%-20s\n",
				 "姓名", "年龄", "性别", "电话", "住址");
				printf("%-15s\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].address);
			}
			printf("\n");
			printf("\n");
			break;
		case 0:
			system("cls");
			printf("返回成功\n");
			break;
		default:
			system("cls");
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);
}

菜单函数:

void menu_search()
{
    
    
	printf("****1.通过名字查找****\n");
	printf("****2.通过电话查找****\n");
	printf("*****0.返回上一步*****\n");
	//返回主函数的一个循环相当于返回
}

这里电话查找本质是字符串,所以原理与名字查找相同:

找到相同的字符串返回非零,找不到字符串返回-1.

int search_tele(char tele[], contect* ps)
{
    
    
	for (int i = 0; i < ps->sz; i++)
	{
    
    
		if (strcmp(tele, ps->data[i].tele) == 0)
		{
    
    
			return i;
		}
	}
	return -1;
}

再把我们新写的函数加到头文件中
在这里插入图片描述
之后我们再实现修改人信息函数:

修改的时候我们先要查找人的信息,如果找不到这个人就提示一下,找到了我们要选择要修改的信息

之后同样,我们要给一个菜单,选择你要修改什么信息

类似于

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
这里返回上一步返回到了输入修改人的名字这一步。

查找函数菜单;

void menu_modify()
{
    
    
	printf("****1.通过名字查找****\n");
	printf("****2.通过电话查找****\n");
	printf("*****0.返回上一步*****\n");
}

修改菜单函数:

void menu_modify_option()
{
    
    
	printf("****1.修改姓名****\n");
	printf("****2.修改年龄****\n");
	printf("****3.修改性别****\n");
	printf("****4.修改电话****\n");
	printf("****5.修改地址****\n");
	printf("***0.返回上一步***\n");
}

具体实现将用到两个switch嵌套。

void Modifycontect(contect* ps)
{
    
    
	char name[MAX_NAME] = {
    
     0 };
	char tele[MAX_TELE] = {
    
     0 };
	int pos = 0;
	int input = 0;
	do
	{
    
    
		menu_modify();//查找菜单
		scanf("%d",&input);
		switch (input)
		{
    
    
		case 1://通过名字查找人
			system("cls");
			printf("请输入你要修改人的名字\n");
			scanf("%s", name);
			pos = search_name2(name, ps);//第一阶段写过的查找名字函数
			if (pos == -1)
			{
    
    
				printf("要修改的人不存在,请确定通讯录信息\n");
				int i = 0;
				printf("\n");
				printf("是否需要返回菜单确认信息 1.返回 2.重新寻找\n");
				scanf("%d",&i);
				if(i == 1)
				{
    
    
					return;//回到主函数的switch中
				}
			}
			else
			{
    
    
				int input2 = 0;
				do
				{
    
    
					menu_modify_option();
					scanf("%d",&input2);
					switch (input2)
					{
    
    
					case 1:
						printf("你要将原来的名字修改为\n");
						scanf("%s",ps->data[pos].name);
						system("cls");
						break;
					case 2:
						printf("你要将原来的年龄改为\n");
						scanf("%d",&ps->data[pos].age);
						system("cls");
						break;
					case 3:
						printf("你要将原来的性别改为\n");
						scanf("%s",ps->data[pos].sex);
						system("cls");
						break;
					case 4:
						printf("你要将原来的电话改为\n");
						scanf("%s",ps->data[pos].tele);
						system("cls");
						break;
					case 5:
						printf("你要将原来的地址改为\n");
						scanf("%s",ps->data[pos].address);
						system("cls");
						break;
					case 0:
						system("cls");
						printf("返回成功\n");
						break;
					default:
						system("cls");
						printf("输入错误,请重新输入\n");
						break;
					}
				} while (input2);
			}
			break;
		case 2://通过电话查找人
			system("cls");
			printf("请输入你要修改人的电话号码\n");
			scanf("%s", tele);
			pos = search_tele(tele, ps);//前面实现过此函数
			if (pos == -1)
			{
    
    
				printf("要修改的人不存在,请确定通讯录信息\n");
				int i = 0;
				printf("\n");
				printf("是否需要返回菜单确认信息 1.返回 2.重新寻找\n");
				scanf("%d", &i);
				if (i == 1)
				{
    
    
					return;
				}
			}
			else
			{
    
    
				int input2 = 0;
				do
				{
    
    
					menu_modify_option();
					scanf("%d", &input2);
					switch (input2)
					{
    
    
					case 1:
						printf("你要将原来的名字修改为\n");
						scanf("%s", ps->data[pos].name);
						system("cls");
						break;
					case 2:
						printf("你要将原来的年龄改为\n");
						scanf("%d", &ps->data[pos].age);
						system("cls");
						break;
					case 3:
						printf("你要将原来的性别改为\n");
						scanf("%s", ps->data[pos].sex);
						system("cls");
						break;
					case 4:
						printf("你要将原来的电话改为\n");
						scanf("%s", ps->data[pos].tele);
						system("cls");
						break;
					case 5:
						printf("你要将原来的地址改为\n");
						scanf("%s", ps->data[pos].address);
						system("cls");
						break;
					case 0:
						system("cls");
						printf("返回成功\n");
						break;
					default:
						system("cls");
						printf("输入错误,请重新输入\n");
						break;
					}
				} while (input2);
			}
			break;
		case 0:
			system("cls");
			printf("返回成功\n");
			break;
		default:
			system("cls");
			printf("选择错误,请重新选择\n");
			break;
		}

	} while (input);
}

因为我们动态通讯录空间是malloc的就自然而然想到Free防止内存泄漏

void Deletinformation(contect* p)
{
    
    
	free(p->data);//释放空间
	p->data= NULL;//指针指空
	p->sz = 0;//通讯录现有大小为0;
}

写完后我们将函数放在头文件声明一下我们写过的函数:

在这里插入图片描述

第二阶段调试

在main函数中添加

#include"标头.h"//包含你写的头文件名字!!!

int main()
{
    
    
	contect con;//生成通讯录;
	initcontect(&con);//初始化通讯录;
	Addcontect(&con);//添加通讯录;
	Addcontect(&con);//添加通讯录;
	Searchcontect(&con);//查找已经存在的人;
	Searchcontect(&con);//查找不存在的人;
	Modifycontect(&con);//修改不存在的人;
	Modifycontect(&con);//修改存在人的姓名;
	Showcontect(&con);//打印修改后的通讯录;
	Modifycontect(&con);//修改存在人的地址;
	Showcontect(&con);
	//类似测试修改的其他功能是否正常
	Deletinformation(&con);
	return 0;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
查找接口没有问题。

在这里插入图片描述
在这里插入图片描述
修改不存在人正常

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
修改之后的信息,修改接口正常
在这里插入图片描述

在这里插入图片描述
销毁通讯录接口正常

至此我们通讯录都可以正常工作。

3.具体代码实现(排序通讯录人的信息)

我们在实现了通讯录的增加,修改,查找,查重功能后,我们还想要排序一下通讯录人的信息。这里选择用qsort排序

void Sortcontect(contect* ps)
{
    
    
	qsort(ps->data,ps->sz,sizeof(people), CmpByName);
//要排序data空间,排序大小,每一个数据的大小是结构体people大小,函数指针
	system("cls");
	printf("排序成功\n");
}

这里要写一个函数指针,也是非常简单

int CmpByName(const void* e1, const void* e2)
{
    
    
	return strcmp((const char*)e1, (const char*)e2);
}

qsort函数的用法

在头文件包含一下写的排序函数

在这里插入图片描述

第三阶段调试

在main函数中

#include"标头.h"//包含自己的头文件!!!!

int main()
{
    
    
	contect con;//生成通讯录;
	initcontect(&con);//初始化通讯录;
	Addcontect(&con);//添加通讯录;
	Addcontect(&con);//添加通讯录;
	Addcontect(&con);//添加通讯录;
	 Showcontect(&con);//打印添加前的通讯录
	Sortcontect(&con);//排序
	 Showcontect(&con);//打印排序后的通讯录
	Deletinformation(&con);//释放内存
	return 0;
}

排序前:
在这里插入图片描述
在这里插入图片描述
排序后:
在这里插入图片描述
排序接口完成

4.整体代码框架的构建

通讯录的功能大体实现了,这里我们大体构建一下通讯录信息的基本框架

并且选择不同数字对应不同的接口

为了方便我们书写代码,这里在头文件中定义枚举类型。

enum option
{
    
    
	Exit,//数值为0对应菜单中的0退出
	Add,//1
	Delete,//2
	Search,//3
	Modify,//4
	Show,//5
	Sort,//6
	Save,//7
};

在main函数中

int main()
{
    
    
	int input = 0;
	int input2 = 0;
	contect con;//创建通讯录
	initcontect(&con);//初始化通讯录
	do//循环执行接口,直到输入0时退出
	{
    
    
		menu();//打印基本菜单
		scanf("%d",&input);
//因为主函数是一个switch句
//所以之前设计的函数返回上一步选项都会回到主函数这里,不在赘述
		switch (input)
		{
    
    
		case Add://Add数值大小为1
			Addcontect(&con);//加法接口
			break;
		case Delete:
			Deletecontect(&con);//删除接口,我们目前没有写
			break;
		case Search:
			Searchcontect(&con);//查找接口
			break;
		case Modify:
			Modifycontect(&con);//修改接口
			break;
		case Show:
			Showcontect(&con);//打印接口
			break;
		case Sort:
			Sortcontect(&con);//排序接口
			break;
		case Save:
			Savecontect(&con);//保存接口,我们目前没有写
			system("cls");
			printf("保存成功\n");
			break;
		case Exit://退出
			system("cls");
			printf("退出通讯录后,如不保存数据会丢失,请先保存再退出\n");//提示用户保存
			printf("1.退出     0.取消退出\n");
			scanf("%d",&input2);
			if (input2 == 0)
			{
    
    
				system("cls");
				input = 1;//如果取消退出了把input改成非零就行
				printf("取消退出成功\n");
			}
			else
			{
    
    
				Deletinformation(&con);//继续退出的话销毁申请的空间
				system("cls");
				printf("退出通讯录成功\n");
			}
			break;
		default:
			system("cls");
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);
	return 0;
}

5.具体代码实现(保存通讯录信息,加载通讯录,删除通讯录的人或整体的信息)

我们首先来实现保存通讯录功能。

void Savecontect(contect*ps)
{
    
    
	FILE* pf = fopen("contect.data", "wb");
	//以二进制的写的形式打开contect.data文件。
	//如果没有此文件,会自己自动创建
	if (pf == NULL)
	{
    
    
		printf("内存不足,保存失败\n");
		return;
	}
	//用循环的方式以二进制形式写入pf文件指针指向的文件
	for (int i = 0; i < ps->sz; i++)
	{
    
    
	//这四个参数分别为
	//要写数据的地址,每一个数据的大小,每次写几个数据,写到文件
		fwrite(&(ps->data[i]),sizeof(people),1,pf);
	}
	fclose(pf);//关闭文件
	pf = NULL;//文件指针置空
	printf("保存成功\n");
}

那么我们保存到文件中的信息在下次打开通讯录的时候要加载,所以我们要对初始化通讯录函数做调整

先写出加载函数

加载时要判断是否要扩容

void LodeContact(contect* p)
{
    
    
	FILE* pf = fopen("contect.data", "rb");
	//以二进制读的方式打开文件contect.data
	//如果没有此文件会返回空指针
	if (pf == NULL)
	{
    
    
		printf("加载通讯录失败\n");
		//说明没有这个文件,没有数据可以加载
		return;
	}
//创建一个临时的people结构体变量
//将文件的一个数据先放到临时的变量中
	people tmp = {
    
    0};
	//fread函数会返回读取的读取数据的个数
	//所以用while循环每次读取一个people结构体
	//当读取不到数据的时候返回0,停止循环。
//fread函数的参数为
//读到的数据放到哪里,每一个数据的大小,一次读几个数据,从哪里读
	while (fread(&tmp, sizeof(people), 1, pf))
	{
    
    
		CheckCapacity(p);//每一次都要检查是否要扩容
		p->data[p->sz] = tmp;
	//把临时的通讯录数据放到,我们在main函数创建的通讯录
		p->sz++;//通讯录的大小增加1。
	}
	fclose(pf);//关闭文件
	pf = NULL;//文件指针置空
}

我们发现我们还要设计一个检查是否要增容的函数

发现这个函数实现思想与我们写的Addcontect(&con)函数相类似

void CheckCapacity(contect* p)
{
    
    
	if (p->sz == p->capacity)
	//如果现有的容量=最大大小下一次调用会扩容
	{
    
    
		people* ptr =
 (people*)realloc(p->data, (p->capacity + 2) * sizeof(people));
		if (ptr == NULL)
		{
    
    
			printf("内存不足,增容失败\n");
		}
		else
		{
    
    
			p->data = ptr;
			p->capacity += 2;
			printf("提示:通讯录增容成功\n");

		}
	}
}

之前我们也使用过这种检查是否要扩容的片段可以写成函数来减少代码长度。

同时要修改初始化函数

void initcontect(contect* p)
{
    
    
	p->sz = 0;
	p->capacity = MAX_CAP;
	p->data = (people*)malloc(3*sizeof(people));
	if (p->data == NULL)
	{
    
    
		printf("通讯录初始化失败,请退出程序重试\n");
		exit(1);
	}
	LodeContact(p);//初始化成功后调用加载信息函数
	printf("初始化通讯录成功!\n");
}

最后我们来实现删除人数据的函数

删除要有两种删除

1.删除一个人的信息。
查找人的信息,后再删除。同样我们要给一个菜单。
删除的原理是数据覆盖,并将表示通讯录现有人数的size-1就行。

2.删除通讯录整体所有人的信息
删除所有人的信息利用了 fopen函数
fopen函数以二进制写的方式再一次打开时会删除之前文件已经写好的信息,我们只要再free掉之前申请的空间,并且将记录通讯录大小的size改成0就ok了

代码与之前的相同,不在赘述。

void Deletecontect(contect* p)
{
    
    
	int input = 0;
	int input2 = 0;
	int pos = 0;
	char  name[10] = {
    
     0 };
	char tele[20] = {
    
     0 };
	system("cls");
	do
	{
    
    
		menu_deleat();//打印删除的菜单
		scanf("%d",&input);
		switch (input)
		{
    
    
		case 1:
			printf("请输入你要删除的人名字\n");
			scanf("%s",name);
			pos= search_name2(name,p);
			if (pos == -1)
			{
    
    
				printf("要删除的名字不存在\n");
				printf("\n");
			}
			else
			{
    
    
				printf("删除成功\n");
				int j = 0;
				for (j = pos; j < p->sz - 1; j++)
				{
    
    
					p->data[pos] = p->data[pos + 1];
				}
				p->sz--;
			}

			break;
		case 2:
			printf("请输入你要删除的人的电话号码\n");
			scanf("%s", tele);
			pos = search_tele(tele, p);
			if (pos == -1)
			{
    
    
				printf("要删除的电话号码不存在\n");
				printf("\n");
			}
			else
			{
    
    
				printf("删除成功\n");
				int j = 0;
				for(j = pos; j < p->sz - 1; j++)
				{
    
    
					p->data[pos] = p->data[pos + 1];
					//把后一个的值赋给前一个
				}
				p->sz--;
			}
			break;
		case 3:
			printf("你确定要全部删除通讯录吗? 1.确定 2.回到上一步\n");
			scanf("%d",&input2);
			if (input2 == 2);
			else
			{
    
    
				FILE* pf = fopen("contect.data", "wb");
				//再打开一次文件但是不写,相当于清空了文件的内容
				Deletinformation(p);//释放空间
				system("cls");
				return;
			}
			break;
		case 0:
			system("cls");
			printf("返回成功\n");
			return;
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);

}

打印删除菜单

void menu_deleat()
{
    
    
	printf("****1.通过名字删除****\n");
	printf("****2.通过电话删除****\n");
	printf("*****  3.全部删除*****\n");
	printf("*****0.返回上一步*****\n");
}

最后不要忘了在头文件声明一下我们写的函数,以及我们所声明的枚举类型
在这里插入图片描述

最后阶段调试!!!

因为之前的接口已经调试过,只用检查,保存,删除,加载接口即可
在这里插入图片描述
开始因为没有contect.data文件所以显示加载通讯录失败

在这里插入图片描述

添加一个人的信息并打印。

点击保存生成一个contect文件
在这里插入图片描述

再关闭文件后点开记事本已经存入数据

在这里插入图片描述

在这里插入图片描述

再点开生成的可执行程序,因为已经有了contect.data且有数据所以显示初始化成功

直接打印通讯录·的内容发现内容加载成功
在这里插入图片描述
全部删除后,
在这里插入图片描述
记事本变空
在这里插入图片描述

至此,我们的通讯录就写完了。

6.全部代码付下

save_contect.h

#define _CRT_SECURE_NO_WARNINGS 1//vs2019防止报错

#define MAX_NAME 20

#define MAX_SEX 5

#define MAX_TELE 20

#define MAX_ADDRESS 20

//#define MAX 1000 //锁定通讯录大小为1000个

#define MAX_CAP 3

#include<stdio.h>

#include<windows.h>

#include<stdlib.h>

#include<string.h>

enum option
{
    
    
	Exit,
	Add,
	Delete,
	Search,
	Modify,
	Show,
	Sort,
	Save,
};

typedef struct people
{
    
    
	char name[MAX_NAME];
	int age;
	char sex[MAX_SEX];
	char tele[MAX_TELE];
	char address[MAX_ADDRESS];
}people;

typedef struct contect
{
    
    
	int sz;
	people* data;
	int capacity;
}contect;

void menu();

void initcontect(contect* p);

void Addcontect(contect* p);

void Showcontect(contect* p);

void Deletecontect(contect* p);

void menu_search();

int search_name(char name[], contect* ps);

int search_tele(char tele[], contect* ps);

int search_name2(char name[], contect* ps);

void dele_name2(int p,contect* ps); 

void Searchcontect(contect* ps);

void Modifycontect(contect* ps);

void menu_modify();

void menu_modify_option();

void Sortcontect(contect* ps);

int CmpByName(const void* e1, const void* e2);

void Deletinformation(contect*ps);

void Savecontect(contect*ps);

void CheckCapacity(contect* ps);

void LodeContact(contect* ps);

save_test.c

#define _CRT_SECURE_NO_WARNINGS 1

#include"save_contect.h"

int main()
{
    
    
	int input = 0;
	int input2 = 0;
	contect con;
	initcontect(&con);
	do
	{
    
    
		menu();
		scanf("%d",&input);
		switch (input)
		{
    
    
		case Add:
			Addcontect(&con);
			break;
		case Delete:
			Deletecontect(&con);
			break;
		case Search:
			Searchcontect(&con);
			break;
		case Modify:
			Modifycontect(&con);
			break;
		case Show:
			Showcontect(&con);
			break;
		case Sort:
			Sortcontect(&con);
			break;
		case Save:
			Savecontect(&con);
			system("cls");
			printf("保存成功\n");
			break;
		case Exit:
			system("cls");
			printf("退出通讯录后,如不保存数据会丢失,请先保存再退出\n");
			printf("1.退出     0.取消退出\n");
			scanf("%d",&input2);
			if (input2 == 0)
			{
    
    
				system("cls");
				input = 1;
				printf("取消退出成功\n");
			}
			else
			{
    
    
				Deletinformation(&con);
				system("cls");
				printf("退出通讯录成功\n");
			}
			break;
		default:
			system("cls");
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);
	return 0;
}

save_contect.c

#define _CRT_SECURE_NO_WARNINGS 1

#include"save_contect.h"

void menu()
{
    
    
	printf("|-----------------------|\n");
	printf("|        contact        |\n");
	printf("|   1.Add     2.Delete  |\n");
	printf("|   3.Search  4.Modify  |\n");
	printf("|   5.Show    6.Sort    |\n");
	printf("|   0.Exit    7.Save    |\n");
	printf("|-----------------------|\n");
}

void menu_deleat()
{
    
    
	printf("****1.通过名字删除****\n");
	printf("****2.通过电话删除****\n");
	printf("*****  3.全部删除*****\n");
	printf("*****0.返回上一步*****\n");
}

void menu_search()
{
    
    
	printf("****1.通过名字查找****\n");
	printf("****2.通过电话查找****\n");
	printf("*****0.返回上一步*****\n");
}

void menu_modify()
{
    
    
	printf("****1.通过名字查找****\n");
	printf("****2.通过电话查找****\n");
	printf("*****0.返回上一步*****\n");
}

void menu_modify_option()
{
    
    
	printf("****1.修改姓名****\n");
	printf("****2.修改年龄****\n");
	printf("****3.修改性别****\n");
	printf("****4.修改电话****\n");
	printf("****5.修改地址****\n");
	printf("***0.返回上一步***\n");
}

void initcontect(contect* p)
{
    
    
	p->sz = 0;
	p->capacity = MAX_CAP;
	p->data = (people*)malloc(3*sizeof(people));
	if (p->data == NULL)
	{
    
    
		printf("通讯录初始化失败,请退出程序重试\n");
		exit(1);
	}
	LodeContact(p);
	printf("初始化通讯录成功!\n");
}

void CheckCapacity(contect* p)
{
    
    
	if (p->sz == p->capacity)
	{
    
    
		people* ptr = (people*)realloc(p->data, (p->capacity + 2) * sizeof(people));
		if (ptr == NULL)
		{
    
    
			printf("内存不足,增容失败\n");
		}
		else
		{
    
    
			p->data = ptr;
			p->capacity += 2;
			printf("提示:通讯录增容成功\n");

		}
	}
}

void Addcontect(contect* p)
{
    
    
	int pos = -1;
	CheckCapacity(p);
		printf("请输入姓名:>\n");
		scanf("%s",p->data[p->sz].name);
		printf("请输入年龄:>\n");
		scanf("%d",&p->data[p->sz].age);
		printf("请输入性别:>\n");
		scanf("%s",p->data[p->sz].sex);
		printf("请输入电话:>\n");
		scanf("%s",p->data[p->sz].tele);
		printf("请输入地址:>\n");
		scanf("%s",p->data[p->sz].address);
		p->sz++;
		system("cls");
		pos = search_name2(p->data[p->sz-1].name, p);
		if (pos == -1)
		{
    
    
			printf("添加成功\n");
		}
		else
		{
    
    
			printf("通讯录中已经有此人\n");
			dele_name2(p->sz,p);
			p->sz--;
		}
}

void Showcontect(contect* p)
{
    
    
	if (p->sz == 0)
	{
    
    
		system("cls");
		printf("通讯录为空\n");
	} 
	else
	{
    
    
		system("cls");
		printf("%-15s\t %-5s\t %-5s\t %-12s\t %-20s\t\n", "姓名","年龄","性别","电话","地址");
		for (int i = 0; i < p->sz; i++)
		{
    
    
			printf("%-15s\t %-5d\t %-5s\t %-12s\t %-20s\t\n",p->data[i].name,
			p->data[i].age,p->data[i].sex,p->data[i].tele,p->data[i].address);
		}

	}
}

int search_name2(char name[], contect* ps)
{
    
    
	for (int i = 0; i < ps->sz-1; i++)
	{
    
    
		if (strcmp(name, ps->data[i].name) == 0)
		{
    
    
			return i;
		}
	}
	return -1;
}

int search_name(char name[], contect* ps)
{
    
    
	for (int i = 0; i < ps->sz; i++)
	{
    
    
		if (strcmp(name, ps->data[i].name) == 0)
		{
    
    
			return i;
		}
	}
	return -1;
}

int search_tele(char tele[], contect* ps)
{
    
    
	for (int i = 0; i < ps->sz; i++)
	{
    
    
		if (strcmp(tele, ps->data[i].tele) == 0)
		{
    
    
			return i;
		}
	}
	return -1;
}

void dele_name2(int p,contect* ps)
{
    
    
	ps->data[p - 1] = ps->data[p];
}

void Deletecontect(contect* p)
{
    
    
	int input = 0;
	int input2 = 0;
	int pos = 0;
	char  name[10] = {
    
     0 };
	char tele[20] = {
    
     0 };
	system("cls");
	do
	{
    
    
		menu_deleat();
		scanf("%d",&input);
		switch (input)
		{
    
    
		case 1:
			printf("请输入你要删除的人名字\n");
			scanf("%s",name);
			pos= search_name2(name,p);
			if (pos == -1)
			{
    
    
				printf("要删除的名字不存在\n");
				printf("\n");
			}
			else
			{
    
    
				printf("删除成功\n");
				int j = 0;
				for (j = pos; j < p->sz - 1; j++)
				{
    
    
					p->data[pos] = p->data[pos + 1];
				}
				p->sz--;
			}

			break;
		case 2:
			printf("请输入你要删除的人的电话号码\n");
			scanf("%s", tele);
			pos = search_tele(tele, p);
			if (pos == -1)
			{
    
    
				printf("要删除的电话号码不存在\n");
				printf("\n");
			}
			else
			{
    
    
				printf("删除成功\n");
				int j = 0;
				for(j = pos; j < p->sz - 1; j++)
				{
    
    
					p->data[pos] = p->data[pos + 1];
				}
				p->sz--;
			}
			break;
		case 3:
			printf("你确定要全部删除通讯录吗? 1.确定 2.回到上一步\n");
			scanf("%d",&input2);
			if (input2 == 2);
			else
			{
    
    
				FILE* pf = fopen("contect.data", "wb");
				Deletinformation(p);
				system("cls");
				return;
			}
			break;
		case 0:
			system("cls");
			printf("返回成功\n");
			return;
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);

}

void Searchcontect(contect*ps)
{
    
    
	char name[20] = {
    
     0 };
	char tele[20] = {
    
     0 };
	int input = 0;
	int pos = 0;
	system("cls");
	do
	{
    
    
		menu_search();
		scanf("%d",&input);
		switch (input)
		{
    
    
		case 1:
			system("cls");
			printf("请输入你要查找人的名字\n");
			scanf("%s",name);
			 pos = search_name(name, ps);
			if (pos == -1)
			{
    
    
				printf("你要找的名字不存在\n");
			}
			else
			{
    
    
				printf("%-15s\t%-4s\t%-5s\t%-12s\t%-20s\n", "姓名", "年龄", "性别", "电话", "住址");
				printf("%-15s\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].address);
			}
			printf("\n");
			printf("\n");
			break;
		case 2:
			system("cls");
			printf("请输入你要查找人的电话号码\n");
			scanf("%s",tele);
			 pos = search_tele(tele, ps);
			if (pos == -1)
			{
    
    
				printf("你要查找的电话号码不存在\n");
			}
			else
			{
    
    
				printf("%-15s\t%-4s\t%-5s\t%-12s\t%-20s\n", "姓名", "年龄", "性别", "电话", "住址");
				printf("%-15s\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].address);
			}
			printf("\n");
			printf("\n");
			break;
		case 0:
			system("cls");
			printf("返回成功\n");
			break;
		default:
			system("cls");
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);
}

void Modifycontect(contect* ps)
{
    
    
	char name[MAX_NAME] = {
    
     0 };
	char tele[MAX_TELE] = {
    
     0 };
	int pos = 0;
	int input = 0;
	do
	{
    
    
		menu_modify();
		scanf("%d",&input);
		switch (input)
		{
    
    
		case 1:
			system("cls");
			printf("请输入你要修改人的名字\n");
			scanf("%s", name);
			pos = search_name(name, ps);
			if (pos == -1)
			{
    
    
				printf("要修改的人不存在,请确定通讯录信息\n");
				int i = 0;
				printf("\n");
				printf("是否需要返回菜单确认信息 1.返回 2.重新寻找\n");
				scanf("%d",&i);
				if(i == 1)
				{
    
    
					return;
				}
			}
			else
			{
    
    
				int input2 = 0;
				do
				{
    
    
					menu_modify_option();
					scanf("%d",&input2);
					switch (input2)
					{
    
    
					case 1:
						printf("你要将原来的名字修改为\n");
						scanf("%s",ps->data[pos].name);
						system("cls");
						break;
					case 2:
						printf("你要将原来的年龄改为\n");
						scanf("%d",&ps->data[pos].age);
						system("cls");
						break;
					case 3:
						printf("你要将原来的性别改为\n");
						scanf("%s",ps->data[pos].sex);
						system("cls");
						break;
					case 4:
						printf("你要将原来的电话改为\n");
						scanf("%s",ps->data[pos].tele);
						system("cls");
						break;
					case 5:
						printf("你要将原来的地址改为\n");
						scanf("%s",ps->data[pos].address);
						system("cls");
						break;
					case 0:
						system("cls");
						printf("返回成功\n");
						break;
					default:
						system("cls");
						printf("输入错误,请重新输入\n");
						break;
					}
				} while (input2);
			}
			break;
		case 2:
			system("cls");
			printf("请输入你要修改人的电话号码\n");
			scanf("%s", tele);
			pos = search_tele(tele, ps);
			if (pos == -1)
			{
    
    
				printf("要修改的人不存在,请确定通讯录信息\n");
				int i = 0;
				printf("\n");
				printf("是否需要返回菜单确认信息 1.返回 2.重新寻找\n");
				scanf("%d", &i);
				if (i == 1)
				{
    
    
					return;
				}
			}
			else
			{
    
    
				int input2 = 0;
				do
				{
    
    
					menu_modify_option();
					scanf("%d", &input2);
					switch (input2)
					{
    
    
					case 1:
						printf("你要将原来的名字修改为\n");
						scanf("%s", ps->data[pos].name);
						system("cls");
						break;
					case 2:
						printf("你要将原来的年龄改为\n");
						scanf("%d", &ps->data[pos].age);
						system("cls");
						break;
					case 3:
						printf("你要将原来的性别改为\n");
						scanf("%s", ps->data[pos].sex);
						system("cls");
						break;
					case 4:
						printf("你要将原来的电话改为\n");
						scanf("%s", ps->data[pos].tele);
						system("cls");
						break;
					case 5:
						printf("你要将原来的地址改为\n");
						scanf("%s", ps->data[pos].address);
						system("cls");
						break;
					case 0:
						system("cls");
						printf("返回成功\n");
						break;
					default:
						system("cls");
						printf("输入错误,请重新输入\n");
						break;
					}
				} while (input2);
			}
			break;
		case 0:
			system("cls");
			printf("返回成功\n");
			break;
		default:
			system("cls");
			printf("选择错误,请重新选择\n");
			break;
		}

	} while (input);
}

int CmpByName(const void* e1, const void* e2)
{
    
    
	return strcmp((const char*)e1, (const char*)e2);
}

void Sortcontect(contect* ps)
{
    
    
	qsort(ps->data,ps->sz,sizeof(people), CmpByName);
	system("cls");
	printf("排序成功\n");
}

void Deletinformation(contect* p)
{
    
    
	free(p->data);
	p->data= NULL;
	p->sz = 0;
}

void Savecontect(contect*ps)
{
    
    
	FILE* pf = fopen("contect.data", "wb");
	if (pf == NULL)
	{
    
    
		printf("内存不足,保存失败\n");
		return;
	}
	for (int i = 0; i < ps->sz; i++)
	{
    
    
		fwrite(&(ps->data[i]),sizeof(people),1,pf);
	}
	fclose(pf);
	pf = NULL;
	printf("保存成功\n");
}

void LodeContact(contect* p)
{
    
    
	FILE* pf = fopen("contect.data", "rb");
	if (pf == NULL)
	{
    
    
		printf("加载通讯录失败\n");
		return;
	}
	people tmp = {
    
    0};
	while (fread(&tmp, sizeof(people), 1, pf))
	{
    
    
		CheckCapacity(p);
		p->data[p->sz] = tmp;
		p->sz++;
	}
	fclose(pf);
	pf = NULL;
}

7.小结

学校的c语言,图书管理系统,学生成绩系统,等等八九不离十。

我写这篇文章的目的是梳理我写这些题的过程与思路,所以代码可能有些冗余。

猜你喜欢

转载自blog.csdn.net/dodamce/article/details/115363250
今日推荐