C语言 — 通讯录模拟实现

目录

1.创建contact.h,contact.c,test.c三个文件

1.1 contact.h文件

1.需要包含头文件;

2.#define定义的常量;

3.#pragma指令的包含;

4.结构体的创建;

5.函数的声明;

c 复制代码
//防止头文件多次包含
#pragma once

//防止函数报不安全警告
#pragma warning(disable:4996)

//头文件的包含

#include<stdio.h>

//#define定义的常量

#define MAX 50 //假设最大容量是50个
#define MAX_NAME 20//名字存放的大小
#define MAX_SEX 6//性别存放的大小
#define MAX_TELE 12//类型方式存放的大小
#define MAX_ADDR 20//地址存放的大小

//创建一个存放人信息的结构体
typedef struct PeoInf
{
	char name[MAX_NAME];//名字
	int age;//年龄
	char sex[6];
	char tele[MAX_TELE];//联系方式
	char addr[MAX_ADDR];//地址

}PeoInf;//类型重定义为PeoInf

//创建一个通讯录结构体
typedef struct Contact
{
	int count;//记录当前存放的个数
	PeoInf data[MAX];//结构体数组

}Contact;//类型重定义为Contact

1.2 contact.c文件实现函数的定义

c 复制代码
#include "contact.h"//包含自定义头文件

1.3 test.c文件

1.实现menu菜单函数;

2.使用枚举完成替换;

3.使用switch分支实现功能的选择;

c 复制代码
#include "contact.h"//包含自定义头文件

//菜单:增删查改,展示,排序,退出
void menu()
{
	printf("************************************\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");
}

//枚举常量替换
enum
{
	EXIT,//0
	ADD,//1
	DELETE,//2
	SEARCH,//3
	MODIFY,//4
	SHOW,//5
	SORT//6
};

int main()
{
	int input = 0;
	do 
	{
		menu();
		printf("请选择:>");
		(void)scanf("%d", &input);
		switch (input)
		{
		case ADD:	
			break;
		case DELETE:
			break;
		case SEARCH:
			break;
		case MODIFY:
			break;
		case SHOW:
			break;
		case SORT:
			break;
		case EXIT:
		    printf("退出程序中...\n");
			break;
		default:
			printf("请重新选择!\n");
		}
	} while (input);
	return 0;
}

2.InitidContact初始化函数

在test.c文件中创建一个Contact结构体变量 con,并在该文件中调用InitidContact函数。

c 复制代码
int main()
{
	int input = 0;//功能选择
	Contact con;//创建通讯录的变量

    //初始化函数
	InitidContact(&con);//指针传参

	//多次选择
	do 
	{
		menu();
		printf("请选择:>");
		(void)scanf("%d", &input);
		switch (input)
		{
		case ADD:	
			break;
		case DELETE:
			break;
		case SEARCH:
			break;
		case MODIFY:
			break;
		case SHOW:
			break;
		case SORT:
			break;
		case EXIT:
			printf("退出程序中...\n");
			break;
		default:
			printf("请重新选择!\n");
		}
	} while (input);

	return 0;
}

在contact.c文件事项该函数的定义,需要使用assert函数和memset函数。

c 复制代码
#include "contact.h"//包含自定义头文件

void InitidContact(Contact* con)
{
	assert(con);//保证con不为空指针

	//初始化:使用memset函数
	memset(con,0, MAX * sizeof(Contact));
}

contact.h文件需要包含assert和memset函数需要的头文件和初始化函数的定义。

c 复制代码
//防止头文件多次包含
#pragma once

//防止函数报不安全警告
#pragma warning(disable:4996)

//头文件的包含

#include<stdio.h>//标准输入输出需要的头文件
#include<assert.h>//断言函数需要的头文件
#include<string.h>//memset函数和字符串函数需要的文件

//#define定义的常量

#define MAX 50 //假设最大容量是50个
#define MAX_NAME 20
#define MAX_SEX 6
#define MAX_TELE 12
#define MAX_ADDR 20

//创建一个存放人信息的结构体
typedef struct PeoInf
{
	char name[MAX_NAME];//名字
	int age;//年龄
	char sex[6];//性别
	char tele[MAX_TELE];//联系方式
	char addr[MAX_ADDR];//地址

}PeoInf;//类型重定义为PeoInf

//创建一个通讯录结构体
typedef struct Contact
{
	int count;//记录当前存放的个数
	PeoInf data[MAX];//结构体数组

}Contact;//类型重定义为Contact

//函数的声明

//初始化函数
void InitidContact(Contact* con);

此时可以按F10启动调试查询能否初始化数据,可以打开监视窗口观察。

3.Add函数

在contact.h文件中包含Add函数的声明。

c 复制代码
//Add函数
void Add(Contact* con);

在contact.c文件实现文件的定义

c 复制代码
//Add函数的定义
void Add(Contact* con)
{
	assert(con);

	//判断当前存放的个数是否满了
	if (con->count == MAX)
	{
		//此时不能存放,终止增加
		printf("当前容量已满,不可创建,退出中.......\n");
		return;
	}
	//此时容量未满,可以增加
	//找到当前数组的位置
	printf("请输入名字:>");
	scnaf("%s", con->data[con->count].name);//字符串数组名标识地址不需要&
	printf("请输入年龄:>");
	scnaf("%s", &(con->data[con->count].age));//age是变量需要&
	printf("请输入性别:>");
	scnaf("%s", con->data[con->count].sex);
	printf("请输入联系方式:>");
	scnaf("%s", con->data[con->count].tele);
	printf("请输入地址:>");
	scnaf("%s", con->data[con->count].addr);

	//此时增加成功,将count++
	con->count++;
	//提示一下,增加成功
	printf("输入的信息已经保存\n");
}

在test.c文件调用Add函数。

c 复制代码
	do 
	{
		menu();
		printf("请选择:>");
		(void)scanf("%d", &input);
		switch (input)
		{
		case ADD:	
			Add(&con);//调用Add函数
			break;
		case DELETE:
			break;
		case SEARCH:
			break;
		case MODIFY:
			break;
		case SHOW:
			break;
		case SORT:
			break;
		case EXIT:
			printf("退出程序中...\n");
			break;
		default:
			printf("请重新选择!\n");
		}
	} while (input);

4.Show函数

实现Add函数后需要将Add含增加的信息展示,使用Show函数输出对应的信息。

contact.h文件实现函数声明。

c 复制代码
//Show函数
void Show(Contact* con);

contact.c文件实现函数的定义

c 复制代码
//Show函数的定义
void Show(Contact* con)
{
	assert(con);//con不为空指针

	//判断count是否为0
	if (con->count == 0)
	{
		printf("没有信息,不可展示!\n");
		return;//结束Show函数
	}

	//打印标题行:-表示左对齐,\t是间隔8个水平制表位
	printf("%-20s%-5s\t%-6s\t%-12s\t%s\n","名字","年龄","性别","联系方式","地址");

	//使用for循环遍历输出打印
	int i = 0;//从下标为0的元素开始
	for (i = 0; i < con->count; i++)
	{
		printf("%-20s%-5d\t%-6s\t%-12s\t%s\n", con->data[i].name,
			         con->data[i].age, con->data[i].sex,
			        con->data[i].tele, con->data[i].addr);
			
	}
}

test.c文件实现Show函数的调用

c 复制代码
	do 
	{
		menu();
		printf("请选择:>");
		(void)scanf("%d", &input);
		switch (input)
		{
		case ADD:	
			Add(&con);//调用Add函数
			break;
		case DELETE:
			break;
		case SEARCH:
			break;
		case MODIFY:
			break;
		case SHOW:
			Show(&con);//调用Show函数
			break;
		case SORT:
			break;
		case EXIT:
			printf("退出程序中...\n");
			break;
		default:
			printf("请重新选择!\n");
		}
	} while (input);

按Ctrl + F5开始调试不执行,测试当前的增加和展示.

5.Delete函数

Delete函数是将指定用户的信息进行删除,需要先输入指定的删除对象,实现一个FindName函数查询该用户是是否存在,如果存在将指定用户删除后进行覆盖,进入不存在输出提示信息。

contact.h文件实现函数的声明和头文件的包含

c 复制代码
#include<string.h>//memset函数和strcmp函数的头文件
void contact(Contact* con);//函数声明

contact.c文件实现函数的定义。

c 复制代码
int FindName(Contact*con,const char* tem)
{
	assert(tem);//此时con已经在Delet数组中调用,不需要判断con

	//使用数组遍历查找
	int i = 0;
	for (i = 0; i < con->count; i++)
	{
		//判断名字是否相等,使用strcmp函数
		//头文件是#include<string.h>
		if(strcmp(con->data[i].name,tem) == 0)
		   return i;//表示找到,返回下标
	}
	return -1;//遍历数组后还找不到返回-1
}

//Delete函数的定义
void Delete(Contact* con)
{
	assert(con);//判断con不为空指针

	char tem[20] = { 0 };
	printf("请输入需要删除的对象:>");
	scanf("%s", tem);

	//1.需要找到删除的对象,需要实现一个查找函数;
	int r = FindName(con, tem);

	//2.判断是否找到
	//如果找到了进行覆盖
	if (r != -1)//找到了,通过后一个信息覆盖前一个信息完成删除
	{	//使用for循环遍历数组
		//从下标为r的数组元素开始覆盖
		for (; r < con->count - 1; r++)//count - 1表示数组倒数第二个元素
		{//count - 1表示数组倒数第二个元素,防止数组越界
		//memcpy函数的头文件是#include<string.h>
			memcpy(con->data + r, con->data + r + 1, sizeof(PeoInf));
			//memcpy是内存块拷贝函数,第一个参数是下标为r的元素的起始地址
			//第二个元素是r + 1下标元素的起始地址,第三个参数是拷贝元素的
			//大小,刚刚好是一个人信息结构体大小。
		}
	}
	//找不到,输出原因,并退出函数
	else
	{
		printf("输入的用户信息不存在,请稍后尝试!\n");
		return;
	}
	
	//3.count--,此时的用户信息删除了一个,用户信息减一
	con->count--;

	//4.提示一下删除成功
	printf("输入的用户信息删除成功!\n");
}

test.c文件调用Delete函数。

c 复制代码
//多次选择
do 
{
	menu();
	printf("请选择:>");
	(void)scanf("%d", &input);
	switch (input)
	{
	case ADD:	
		Add(&con);//调用Add函数
		break;
	case DELETE:
		Delete(&con);//调用删除函数
		break;
	case SEARCH:
		break;
	case MODIFY:
		break;
	case SHOW:
		Show(&con);//调用Show函数
		break;
	case SORT:
		break;
	case EXIT:
		printf("退出程序中...\n");
		break;
	default:
		printf("请重新选择!\n");
	}
} while (input);

按Ctrl + F5启动调试测试是否能够删除。

选择删除功能,删除李四的信息,然后调用Show函数.

6.Search函数

Search函数的作用是查找指定用户,使用已经实现的FindName函数,如果存在指定用户,将用户信息输出展示,如果指定用户不存在,输出提示信息。

contact.h文件实现Search函数的声明。

c 复制代码
//Search函数的声明
void Search(Contact* con);

contact.c文件实现Search函数的定义。

c 复制代码
void Search(Contact* con)
{
	assert(con);//判断con不为NULL

	//前面的Delete函数已经实现了查找函数
	//此处可以调用该函数判断是否存在该用户信息
	//如果存在,将该用户信息输入即可

	//1.输入需要查找的对象
	char tem[20] = { 0 };
	printf("请输入需要查找的对象:>");
	scanf("%s", tem);
	//2.查找是否存在
	int r = FindName(con, tem);
	//存在输入打印
	if (r != -1)
	{
		//找到了,提示一下
		printf("输入的用户存在,正在加载中....\n");
		//使用Show函数的格式输出
		//打印标题行:-表示左对齐,\t是间隔8个水平制表位
		printf("%-20s%-5s\t%-6s\t%-12s\t%s\n", "名字", "年龄", "性别", "联系方式", "地址");
		//指定格式输出
		printf("%-20s%-5d\t%-6s\t%-12s\t%s\n", con->data[r].name,
			con->data[r].age, con->data[r].sex,
			con->data[r].tele, con->data[r].addr);
	}
	//不存在,提示一下,并结束函数
	else
	{
		printf("输入的用户不存在!\n");
		return ;//结束函数
	}
}

test.c实现函数的调用

c 复制代码
//多次选择
do 
{
	menu();
	printf("请选择:>");
	(void)scanf("%d", &input);
	switch (input)
	{
	case ADD:	
		Add(&con);//调用Add函数
		break;
	case DELETE:
		Delete(&con);//调用删除函数
		break;
	case SEARCH:
		Search(&con);//调用查找函数
		break;
	case MODIFY:
		break;
	case SHOW:
		Show(&con);//调用Show函数
		break;
	case SORT:
		break;
	case EXIT:
		printf("退出程序中...\n");
		break;
	default:
		printf("请重新选择!\n");
	}
} while (input);

按Ctrl + F5启动调试测试Search函数。

7.Modify函数

Modify函数是将指定的用户进行修改,使用FindName函数进行查找,如果用户存在,对用户信息重新录入修改,如果不存在,输出提示信息。

contatc.h文件实现Modify函数的声明.

c 复制代码
//Modify函数的声明
void Modify(Contact* con);

contact.c文件实现函数的定义.

c 复制代码
//Modify函数的定义
void Modify(Contact* con)
{
	assert(con);//保证con不为NULL;

	//1.查找到成员信息,还是使用查找函数
	char tem[20] = { 0 };
	printf("请输入需要修改的对象:>");
	scanf("%s", tem);
	//2.查找是否存在
	int r = FindName(con, tem);
	//如果存在,因为是修改,可以将原因信息重新录入即可
	if (r != -1)
	{
		//直接使用Add函数的代码
		printf("请输入名字:>");
		scanf("%s", con->data[r].name);//字符串数组名标识地址不需要&
		printf("请输入年龄:>");
		scanf("%d", &(con->data[r].age));//age是变量需要&
		printf("请输入性别:>");
		scanf("%s", con->data[r].sex);
		printf("请输入联系方式:>");
		scanf("%s", con->data[r].tele);
		printf("请输入地址:>");
		scanf("%s", con->data[r].addr);

		//修改成功后提示一下
		printf("用户信息已经成功修改!\n");
	}
	//没有找到,提示一下
	else
	{
		printf("输入的用户信息不存在!\n");
		return;//结束程序
	}
}

test.c文件实现函数的调用。

c 复制代码
//多次选择
do 
{
	menu();
	printf("请选择:>");
	(void)scanf("%d", &input);
	switch (input)
	{
	case ADD:	
		Add(&con);//调用Add函数
		break;
	case DELETE:
		Delete(&con);//调用删除函数
		break;
	case SEARCH:
		Search(&con);//调用查找函数
		break;
	case MODIFY:
		Modify(&con);//调用修改函数
		break;
	case SHOW:
		Show(&con);//调用Show函数
		break;
	case SORT:
		break;
	case EXIT:
		printf("退出程序中...\n");
		break;
	default:
		printf("请重新选择!\n");
	}
} while (input);

按Ctrl + F5测试修改函数。

8.Sort函数

Sort函数是按照指定顺序排序,以下是按照名字排序,使用qsort函数完成具体排序,使用qsort函数需要实现一个函数指针,并包含指定头文件。

contact.h文件实现Sort函数的声明

c 复制代码
#include<stdlib.h>//qsort函数需要的头文件
//Sort函数的声明
void Sort(Contact* con);

contact.c文件实现函数的定义。

c 复制代码
//Sort函数的定义
int cmp_by_name(const void* e1,const void* e2)
{
	assert(e1 && e2);//e1和e2不为NULL
	//比较名字,实现strcmp函数
	//将比较结果自己返回
	return strcmp(((PeoInf*)e1)->name,((PeoInf*)e2)->name);
}
void Sort(Contact* con)
{
	assert(con);//判断con不为NULL

	//假设按照名字排列,使用qsort函数实现排序
	//qsort函数的头文件是#inlcude<stdlib.h>
	//qsor函数需要实现一个函数指针
	qsort(con->data, con->count, sizeof(PeoInf), cmp_by_name);
	//提示一下排序成功
	printf("用户信息已经成功排序!\n");
}

test.c文件实现函数的调用

c 复制代码
//多次选择
do 
{
	menu();
	printf("请选择:>");
	(void)scanf("%d", &input);
	switch (input)
	{
	case ADD:	
		Add(&con);//调用Add函数
		break;
	case DELETE:
		Delete(&con);//调用删除函数
		break;
	case SEARCH:
		Search(&con);//调用查找函数
		break;
	case MODIFY:
		Modify(&con);//调用修改函数
		break;
	case SHOW:
		Show(&con);//调用Show函数
		break;
	case SORT:
		Sort(&con);//调用Sort函数
		break;
	case EXIT:
		printf("退出程序中...\n");
		break;
	default:
		printf("请重新选择!\n");
	}
} while (input);

按Ctrl + F5测试Sort函数。


9.修改为动态版本

此时的通讯录最大只能存放MAX个用户的信息,可以有些用户不需要这么多的空间,可能导致空间的浪费,有些用户需要更大的空间,导致空间不足;因此需要将大小根据用户的需求进行调整,可以实现动态内存管理的函数即时调整空间大小。

contact文件修改Contact结构体的内容,增加一个capacity(容量)变量,并将data数组修改为结构体指针类型,节省空间大小,并重新定义初始容量大小,和扩容大小,并进行销毁函数的声明。

c 复制代码
#define INT_EXPANSION 5//扩容大小
//动态版本
typedef struct Contact
{
	int count;//记录当前存放的个数
	int capacity;//记录当前容量
	PeoInf* data;//结构体指针

}Contact;//类型重定义为Contact
//销毁通讯录
void DestroyContact(Contact* con);

contact.c文件需要修改初始化函数,Add函数,增加一个扩容函数和销毁函数。

c 复制代码
//动态版本
void InitidContact(Contact* con)
{
	assert(con);//保证con不为空指针

	//初始化:使用memset函数
	con->count = 0;//开始信息为0
	con->capacity = INT_EXPANSION;//表示当前容量是5
	PeoInf*pfData = (PeoInf*)calloc(INT_EXPANSION, sizeof(PeoInf));
	if (pfData == NULL)//判断是否开辟空间失败
	{
		perror("InitidContact");//输出错误信息
		return;//结束扩容
	}
	//赋值
	con->data = pfData;//data可以指向pf指向的空间
	
}

//销毁通讯录
void DestroyContact(Contact* con)
{
	assert(con);//判断con不为NULL
	//回收空间,设置空指针
	free(con->data);
	con->data = NULL;
}

//扩容函数,static修饰只能在当前文件使用
static void Expansion(Contact* con)
{
	//扩容
	PeoInf* pfData = (PeoInf*)realloc(con->data, 
	                 (con->capacity + INT_EXPANSION) * sizeof(PeoInf));
	//
	if (pfData == NULL)
	{
		perror("Expansion::");//提示扩容失败的原因
		return;//结束扩容函数
	}
	con->data = pfData;//data数组可以指向调整后开辟的空间。
	con->capacity += INT_EXPANSION;//记录下当前的容量
	//提示一下扩容成功
	printf("扩容成功,当前容量为:%d\n", con->capacity);
}
void Add(Contact* con)
{
	assert(con);

	//考虑是否需要扩容
	if (con->capacity == con->count)
		Expansion(con);
	
	//此时容量未满,可以增加
	//找到当前数组的位置
	printf("请输入名字:>");
	scanf("%s", con->data[con->count].name);//字符串数组名标识地址不需要&
	printf("请输入年龄:>");
	scanf("%d", &(con->data[con->count].age));//age是变量需要&
	printf("请输入性别:>");
	scanf("%s", con->data[con->count].sex);
	printf("请输入联系方式:>");
	scanf("%s", con->data[con->count].tele);
	printf("请输入地址:>");
	scanf("%s", con->data[con->count].addr);

	//此时增加成功,将count++
	con->count++;
	//提示一下,增加成功
	printf("输入的信息已经保存\n");
}

test.c文件在退出程序时调用DestroyContact函数。

c 复制代码
	//多次选择
	do 
	{
		menu();
		printf("请选择:>");
		(void)scanf("%d", &input);
		switch (input)
		{
		case ADD:	
			Add(&con);//调用Add函数
			break;
		case DELETE:
			Delete(&con);//调用删除函数
			break;
		case SEARCH:
			Search(&con);//调用查找函数
			break;
		case MODIFY:
			Modify(&con);//调用修改函数
			break;
		case SHOW:
			Show(&con);//调用Show函数
			break;
		case SORT:
			Sort(&con);//调用Sort函数
			break;
		case EXIT:
			DestroyContact(&con);//销毁通讯录
			printf("退出程序中...\n");
			break;
		default:
			printf("请重新选择!\n");
		}
	} while (input);

按Ctrl + F5启动调试,测试是否能使用动态的版本。

10.修改为文件版本

文件版本是为了改进动态版本程序退出后数据销毁的问题,可以在程序退出时将已经保存的数据存放于一个data.txt的文件中,当程序下一次运行时将data.txt文件的信息在初始化阶段加载到data数组中,

需要创建一个Save函数保存用户信息,创建一个LoadContact函数将信息加载到data数组中。

contact.h文件对Save函数和LoadContact函数进行声明。

c 复制代码
//Save函数的声明
void Save(Contact* con);

//LoadContact函数的声明
void LoadContact(Contact* con);

contact.c文件需要对Save函数和LoadContact函数进行函数定义。

c 复制代码
//LoadContact函数的定义
void LoadContact(Contact* con)
{
	//以二进制读取方式打开文件
	FILE* pfRead = fopen("data.txt", "rb");
	if (pfRead == NULL)
	{
		perror("LoadContact");//输出错误信息
		return;//结束函数
	}

	//使用
	PeoInf tem = { 0 };//创建一个结构体变量
	while (fread(&tem,sizeof(PeoInf),1,pfRead) == 1)
	{
		Expansion(con);//检测是否需要扩容
		//因为文件的内容的大小可能比初始化的可以存储的空间多
		//因此需要考虑扩容
		con->data[con->count] = tem;//将读取到的信息赋给data数组的元素
		con->count++;//用户信息+1
	}

	//关闭
	fclose(pfRead);
	pfRead = NULL;
}

//Save函数的定义
void Save(Contact* con)
{
	assert(con);//判断不为NULL
	//打开文件
	FILE* pfWrite = fopen("data.txt", "wb");
	//判断
	if (pfWrite == NULL)
	{
		printf("Save");//输出错误信息
		return;//结束函数
	}

	//使用for遍历数组
	for (int i = 0; i < con->count; i++)
	{
	  fwrite(con->data+i,sizeof(PeoInf),1,pfWrite);
    }
	//关闭
	fclose(pfWrite);
	pfWrite = NULL;
}

test.c文件在程序退出卡调用Save函数,保存信息。

c 复制代码
	//多次选择
	do 
	{
		menu();
		printf("请选择:>");
		(void)scanf("%d", &input);
		switch (input)
		{
		case ADD:	
			Add(&con);//调用Add函数
			break;
		case DELETE:
			Delete(&con);//调用删除函数
			break;
		case SEARCH:
			Search(&con);//调用查找函数
			break;
		case MODIFY:
			Modify(&con);//调用修改函数
			break;
		case SHOW:
			Show(&con);//调用Show函数
			break;
		case SORT:
			Sort(&con);//调用Sort函数
			break;
		case EXIT:
			Save(&con);//保存信息
			DestroyContact(&con);//销毁通讯录
			printf("退出程序中...\n");
			break;
		default:
			printf("请重新选择!\n");
		}
	} while (input);
	

按Ctrl + F5测试是否能使用Save函数。

打开data.txt文件观察是否存放信息,可以观察到已经将用户信息存放。

将程序关闭,重新按Ctrl + F5测试LoadContact函数是否能使用,程序运行后使用Show功能展示。

此时可以使用文件的版本保存和加载有效信息,但是此时的程序还是存在很多问题,效率还是比较低的,高效的使用应该使用数据库管理用户信息,这里就不再进行改进,有兴趣的学习数据库后进行版本的改进,推荐数据库的学习链接:数据库学习

11.程序代码展示

11.1.contact.h文件

c 复制代码
//防止头文件多次包含
#pragma once

//防止函数报不安全警告
#pragma warning(disable:4996)

//头文件的包含

#include<stdio.h>//标准输入输出需要的头文件
#include<assert.h>//断言函数需要的头文件
#include<string.h>//memset函数和字符串函数需要的文件
#include<stdlib.h>//qsort函数和realloc函数需要的头文件


//#define定义的常量

//#define MAX 50 //假设最大容量是50个
#define MAX_NAME 20
#define MAX_SEX 6
#define MAX_TELE 12
#define MAX_ADDR 20
#define INT_EXPANSION 5//扩容大小

//创建一个存放人信息的结构体
typedef struct PeoInf
{
	char name[MAX_NAME];//名字
	int age;//年龄
	char sex[6];
	char tele[MAX_TELE];//联系方式
	char addr[MAX_ADDR];//地址

}PeoInf;//类型重定义为PeoInf

//创建一个通讯录结构体

//静态版本
//typedef struct Contact
//{
//	int count;//记录当前存放的个数
//	PeoInf data[MAX];//结构体数组
//
//}Contact;//类型重定义为Contact

//动态版本
typedef struct Contact
{
	int count;//记录当前存放的个数
	int capacity;//记录当前容量
	PeoInf* data;//结构体指针

}Contact;//类型重定义为Contact

//函数的声明

//初始化函数
void InitidContact(Contact* con);

//销毁通讯录
void DestroyContact(Contact* con);


//Add函数
void Add(Contact* con);

//Show函数
void Show(Contact* con);

//Delete函数
void Delete(Contact* con);

//Search函数的声明
void Search(Contact* con);

//Modify函数的声明
void Modify(Contact* con);

//Sort函数的声明
void Sort(Contact* con);

//Save函数的声明
void Save(Contact* con);

//LoadContact函数的声明
void LoadContact(Contact* con);

11.2.contact.c文件

c 复制代码
在这里插入代码片

#include "contact.h"//包含自定义头文件

//初始化函数的定义

//静态版本
//void InitidContact(Contact* con)
//{
//	assert(con);//保证con不为空指针
//
//	//初始化:使用memset函数
//	con->count = 0;
//	memset(con->data,0, sizeof(con ->data));
//}

//扩容函数,static修饰只能在当前文件使用
static void Expansion(Contact* con)
{
	//扩容
	if (con->capacity == con->count)
	{
		PeoInf* pfData = (PeoInf*)realloc(con->data, (con->capacity + INT_EXPANSION) * sizeof(PeoInf));
		//
		if (pfData == NULL)
		{
			perror("Expansion::");//提示扩容失败的原因
			return;//结束扩容函数
		}
		con->data = pfData;//data数组可以指向调整后开辟的空间。
		con->capacity += INT_EXPANSION;//记录下当前的容量
		//提示一下扩容成功
		printf("扩容成功,当前容量为:%d\n", con->capacity);
	}
}

//LoadContact函数的定义
void LoadContact(Contact* con)
{
	//以二进制读取方式打开文件
	FILE* pfRead = fopen("data.txt", "rb");
	if (pfRead == NULL)
	{
		perror("LoadContact");//输出错误信息
		return;//结束函数
	}

	//使用
	PeoInf tem = { 0 };//创建一个结构体变量
	while (fread(&tem,sizeof(PeoInf),1,pfRead) == 1)
	{
		Expansion(con);//检测是否需要扩容
		//因为文件的内容的大小可能比初始化的可以存储的空间多
		//因此需要考虑扩容
		con->data[con->count] = tem;//将读取到的信息赋给data数组的元素
		con->count++;//用户信息+1
	}

	//关闭
	fclose(pfRead);
	pfRead = NULL;
	
}


//动态版本
void InitidContact(Contact* con)
{
	assert(con);//保证con不为空指针
	

	//初始化:使用memset函数
	con->count = 0;//开始信息为0
	con->capacity = INT_EXPANSION;//表示当前容量是5
	PeoInf*pfData = (PeoInf*)calloc(INT_EXPANSION, sizeof(PeoInf));
	if (pfData == NULL)
	{
		perror("InitidContact");//输出错误信息
		return;//结束扩容
	}
	//赋值
	con->data = pfData;//data可以指向pf指向的空间

	LoadContact(con);
	
}

//销毁通讯录
void DestroyContact(Contact* con)
{
	assert(con);//判断con不为NULL
	//回收空间,设置空指针
	free(con->data);
	con->data = NULL;
}


//Add函数的定义

// 静态版本
//void Add(Contact* con)
//{
//	assert(con);
//
//	//判断当前存放的个数是否满了
//	if (con->count == MAX)
//	{
//		//此时不能存放,终止增加
//		printf("当前容量已满,不可创建,退出中.......\n");
//		return;
//	}
//	//此时容量未满,可以增加
//	//找到当前数组的位置
//	printf("请输入名字:>");
//	scanf("%s", con->data[con->count].name);//字符串数组名标识地址不需要&
//	printf("请输入年龄:>");
//	scanf("%d", &(con->data[con->count].age));//age是变量需要&
//	printf("请输入性别:>");
//	scanf("%s", con->data[con->count].sex);
//	printf("请输入联系方式:>");
//	scanf("%s", con->data[con->count].tele);
//	printf("请输入地址:>");
//	scanf("%s", con->data[con->count].addr);
//
//	//此时增加成功,将count++
//	con->count++;
//	//提示一下,增加成功
//	printf("输入的信息已经保存\n");
//}

//动态版本

void Add(Contact* con)
{
	assert(con);

	//考虑是否需要扩容
	if (con->capacity == con->count)
		Expansion(con);
	
	//此时容量未满,可以增加
	//找到当前数组的位置
	printf("请输入名字:>");
	scanf("%s", con->data[con->count].name);//字符串数组名标识地址不需要&
	printf("请输入年龄:>");
	scanf("%d", &(con->data[con->count].age));//age是变量需要&
	printf("请输入性别:>");
	scanf("%s", con->data[con->count].sex);
	printf("请输入联系方式:>");
	scanf("%s", con->data[con->count].tele);
	printf("请输入地址:>");
	scanf("%s", con->data[con->count].addr);

	//此时增加成功,将count++
	con->count++;
	//提示一下,增加成功
	printf("输入的信息已经保存\n");
}

//Show函数的定义
void Show(Contact* con)
{
	assert(con);//con不为空指针

	//判断count是否为0
	if (con->count == 0)
	{
		printf("没有信息,不可展示!\n");
		return;//结束Show函数
	}

	//打印标题行:-表示左对齐,\t是间隔8个水平制表位
	printf("%-20s%-5s\t%-6s\t%-12s\t%s\n","名字","年龄","性别","联系方式","地址");

	//使用for循环遍历输出打印
	int i = 0;//从下标为0的元素开始
	for (i = 0; i < con->count; i++)
	{
		printf("%-20s%-5d\t%-6s\t%-12s\t%s\n", con->data[i].name,
			         con->data[i].age, con->data[i].sex,
			        con->data[i].tele, con->data[i].addr);
			
	}
}

int FindName(Contact*con,const char* tem)
{
	assert(tem);//此时con已经在Delet数组中调用,不需要判断con

	//使用数组遍历查找
	int i = 0;
	for (i = 0; i < con->count; i++)
	{
		//判断名字是否相等,使用strcmp函数
		if(strcmp(con->data[i].name,tem) == 0)
		   return i;//表示找到,返回下标
	}
	return -1;//遍历数组后还找不到返回-1
}

//Delete函数的定义
void Delete(Contact* con)
{
	assert(con);//判断con不为空指针

	char tem[20] = { 0 };
	printf("请输入需要删除的对象:>");
	scanf("%s", tem);

	//1.需要找到删除的对象,需要实现一个查找函数;
	int r = FindName(con, tem);

	//2.判断是否找到
	//如果找到了进行覆盖
	if (r != -1)//找到了,通过后一个信息覆盖前一个信息完成删除
	{	//使用for循环遍历数组
		//从下标为r的数组元素开始覆盖
		for (; r < con->count - 1; r++)//count - 1表示数组倒数第二个元素
		{                            //防止数组越界
			memcpy(con->data + r, con->data + r + 1, sizeof(PeoInf));
			//memcpy是内存块拷贝函数,第一个参数是下标为r的元素的起始地址
			//第二个元素是r + 1下标元素的起始地址,第三个参数是拷贝元素的
			//大小,刚刚好是一个人信息结构体大小。
		}
	}
	//找不到,输出原因,并退出函数
	else
	{
		printf("输入的用户信息不存在,请稍后尝试!\n");
		return;
	}
	
	//3.count--,此时的用户信息删除了一个,用户信息减一
	con->count--;

	//4.提示一下删除成功
	printf("输入的用户信息删除成功!\n");
}

void Search(Contact* con)
{
	assert(con);//判断con不为NULL

	//前面的Delete函数已经实现了查找函数
	//此处可以调用该函数判断是否存在该用户信息
	//如果存在,将该用户信息输入即可

	//1.输入需要查找的对象
	char tem[20] = { 0 };
	printf("请输入需要查找的对象:>");
	scanf("%s", tem);
	//2.查找是否存在
	int r = FindName(con, tem);
	//存在输入打印
	if (r != -1)
	{
		//找到了,提示一下
		printf("输入的用户存在,正在加载中....\n");
		//使用Show函数的格式输出
		//打印标题行:-表示左对齐,\t是间隔8个水平制表位
		printf("%-20s%-5s\t%-6s\t%-12s\t%s\n", "名字", "年龄", "性别", "联系方式", "地址");
		//指定格式输出
		printf("%-20s%-5d\t%-6s\t%-12s\t%s\n", con->data[r].name,
			con->data[r].age, con->data[r].sex,
			con->data[r].tele, con->data[r].addr);
	}
	//不存在,提示一下,并结束函数
	else
	{
		printf("输入的用户不存在!\n");
		return;//结束函数
	}
}

//Modify函数的定义
void Modify(Contact* con)
{
	assert(con);//保证con不为NULL;

	//1.查找到成员信息,还是使用查找函数
	char tem[20] = { 0 };
	printf("请输入需要修改的对象:>");
	scanf("%s", tem);
	//2.查找是否存在
	int r = FindName(con, tem);
	//如果存在,因为是修改,可以将原因信息重新录入即可
	if (r != -1)
	{
		//直接使用Add函数的代码
		printf("请输入名字:>");
		scanf("%s", con->data[r].name);//字符串数组名标识地址不需要&
		printf("请输入年龄:>");
		scanf("%d", &(con->data[r].age));//age是变量需要&
		printf("请输入性别:>");
		scanf("%s", con->data[r].sex);
		printf("请输入联系方式:>");
		scanf("%s", con->data[r].tele);
		printf("请输入地址:>");
		scanf("%s", con->data[r].addr);

		//修改成功后提示一下
		printf("用户信息已经成功修改!\n");
	}
	//没有找到,提示一下
	else
	{
		printf("输入的用户信息不存在!\n");
		return;//结束程序
	}
}


//Sort函数的定义
int cmp_by_name(const void* e1,const void* e2)
{
	assert(e1 && e2);//e1和e2不为NULL
	//比较名字,实现strcmp函数
	//将比较结果自己返回
	return strcmp(((PeoInf*)e1)->name,((PeoInf*)e2)->name);
}
void Sort(Contact* con)
{
	assert(con);//判断con不为NULL

	//假设按照名字排列,使用qsort函数实现排序
	//qsort函数的头文件是#inlcude<stdlib.h>
	//qsor函数需要实现一个函数指针
	qsort(con->data, con->count, sizeof(PeoInf), cmp_by_name);
	//提示一下排序成功
	printf("用户信息已经成功排序!\n");
}

//Save函数的定义
void Save(Contact* con)
{
	assert(con);//判断不为NULL
	//打开文件
	FILE* pfWrite = fopen("data.txt", "wb");
	//判断
	if (pfWrite == NULL)
	{
		printf("Save");//输出错误信息
		return;//结束函数
	}

	//使用for遍历数组
	for (int i = 0; i < con->count; i++)
	{
	  fwrite(con->data+i,sizeof(PeoInf),1,pfWrite);
    }
	//关闭
	fclose(pfWrite);
	pfWrite = NULL;
}

11.3.test.c文件

c 复制代码
#include "contact.h"//包含自定义头文件

//菜单
void menu()
{
	printf("************************************\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");
}

//枚举常量
enum
{
	EXIT,//0
	ADD,//1
	DELETE,//2
	SEARCH,//3
	MODIFY,//4
	SHOW,//5
	SORT//6
};

int main()
{
	int input = 0;//功能选择
	Contact con;//创建通讯录的变量

    //初始化函数
	InitidContact(&con);//指针传参

	//多次选择
	do 
	{
		menu();
		printf("请选择:>");
		(void)scanf("%d", &input);
		switch (input)
		{
		case ADD:	
			Add(&con);//调用Add函数
			break;
		case DELETE:
			Delete(&con);//调用删除函数
			break;
		case SEARCH:
			Search(&con);//调用查找函数
			break;
		case MODIFY:
			Modify(&con);//调用修改函数
			break;
		case SHOW:
			Show(&con);//调用Show函数
			break;
		case SORT:
			Sort(&con);//调用Sort函数
			break;
		case EXIT:
			Save(&con);//保存信息
			DestroyContact(&con);//销毁通讯录
			printf("退出程序中...\n");
			break;
		default:
			printf("请重新选择!\n");
		}
	} while (input);

	return 0;
}
相关推荐
小猫咪怎么会有坏心思呢3 分钟前
华为OD机考-找座位-逻辑分析(JAVA 2025B卷)
java·开发语言·华为od
泪光29296 分钟前
洛谷自己创建的一个小比赛【c++】
开发语言·c++
比特森林探险记19 分钟前
GO 入门小项目-博客-结合Gin Gorm
开发语言·golang·gin
傻傻虎虎36 分钟前
【QT】自动更新库QSimpleUpdater使用实例封装
开发语言·qt
加油冲丫36 分钟前
Java过滤器的基本概念
java·开发语言·后端·servlet
Once_day39 分钟前
代码训练LeetCode(34)文本左右对齐
算法·leetcode·c
RR133540 分钟前
一个小错误:Content-Type ‘text/plain;charset=UTF-8‘ is not supported 的粗心
开发语言·前端·javascript
is081542 分钟前
C语言运行时
c语言·开发语言
tony3651 小时前
强化学习 A2C算法
人工智能·算法
q567315231 小时前
Go语言高并发爬虫程序源码
开发语言·爬虫·golang