C语言之通讯录版本----从入门到精通

目录

  • 前言
  • 一、Test_Contact.c测试
    • 先设计菜单界面
    • 通讯录运行,功能选择
  • 二、Contact.h头文件
    • 关于Contact结构体的设计
  • 三、Contact.c源文件的功能实现
    • 初始化结构体变量
    • 增加联系人
    • 显示所有联系人
    • 查找联系人
      • 查找联系人,返回具体坐标
    • 修改联系人
    • 删除联系人
  • 四、源代码
    • 1.***静态版本***
      • 1.1Contact.h
      • 1.2Test_Contact.c
      • 1.3 Contact.c
    • 2.***动态版本***
      • 头文件
      • C文件
    • 3.***文件流版本***
      • 头文件
      • 源文件
      • 源文件---功能实现
  • 五、总结

前言

此番为 疏通关于用C语言实现通讯录三个版本(从静态到动态到可以文件保存的三个版本)的思路。
实现通讯录,需要进一步理解:自定义结构体、动态内存,文件流的知识体系

在开始构建思路,先明白通讯录要实现那些功能,然后再思考代码实现的逻辑

通讯录(初版):

  1. 增加联系人
  2. 删除联系人
  3. 查找联系人
  4. 修改联系人
  5. 显示所有联系人
  6. 排序

动态储存版本:
结构体的变化,结构体变量初始化的变化

文件流版本:
增加文件信息加载到变量的函数,增加保存数据致文件的函数

源文件:
头文件定义宏,全局自定义结构体,标准库等
.c文件,分为test测试代码和service.c文件

一、Test_Contact.c测试

先设计菜单界面

void menu()
{
	printf("******1.add      ******\n");
	printf("******2.del       ******\n");
	printf("******3.search  ******\n");
	printf("******4.modify ******\n");
	printf("******5.show    ******\n");
	printf("******6.sort      ******\n");
	printf("******0.exit      ******\n");
}

通讯录运行,功能选择

这里为了更具有使用和检阅性,可以用枚举来定义’add‘等功能关键字

enum
{
	EXIT,				//没有指定,默认以0开始
	ADD,			//1
	DEL,
	SEARCH,
	MODIFY,
	SHOW,
	SORT,
};

功能选择区也可以整体封装为一个函数,但初始化结构体变量应该放在功能选择前

int main()
{
	int input;
	Contact con;			//创建通讯录
	InitContact(&con);

	do
	{
		menu();
		printf("请输入操作选项:>");
		scanf("%d", &input);
		switch (input)
		{
		case ADD:
			//add();
			AddContact(&con);
			break;
		case DEL:
			//del();
			DeleteContact(&con);
			break;
		case SEARCH:
			//search();
			SearchContact(&con);
			break;
		case MODIFY:
			//modify();
			ModifyContact(&con);
			break;
		case SHOW:
			//show();
			ShowContact(&con);
			break;
		case SORT:
			//sort();按什么排序,qsort
			printf("待完善\n");
			break;
		case EXIT:
			printf("退出成功\n");
			break;
		default:
			printf("输入错误,重新选择!\n");
			break;
		}
	} while (input);
	return 0;
}

下面再用头文件来定义结构体

二、Contact.h头文件

关于头文件

#pragma once//这个目的是防止头文件被多次包含,而影响程序效率

关于Contact结构体的设计

首先,一个通讯录需要,记录联系人个数的变量、联系人的详细信息的变量。
那么联系人的详细信息也需要自定义结构体,联系人需要:name,age,sex,number,address

//类型的声明
typedef struct PeoInfo
{
	char name[Name_Size];
	int age;
	char sex[5];                                           //联想到bool类型
	char telenumber[15];										//这里能不能用int数组或者直接用int记录电话
	char address[20];
}PeoInfo;

typedef struct Contact
{
	PeoInfo date[MAX];			//这里PeoInfo  理解为类型,可以理解成   int date[100]  这样定义
	int sz;								//记录已有联系人数量
}Contact;

如上,在输入常量数字时,我们可以用宏定义代替

#pragma once

#include
#include
#include

#define Name_Size 20
#define  Sex_Size 5
#define Tele_Size 12
#define Add_Size 30

#define MAX 100



//类型的声明
typedef struct PeoInfo
{
	char name[Name_Size];
	int age;
	char sex[Sex_Size];                                           //联想到bool类型
	char telenumber[Tele_Size];										//这里能不能用int数组或者直接用int记录电话
	char address[Add_Size];
}PeoInfo;

在动态版本的设计时,需要将Contact中的PenInfo数组设计成PenInfo的指针来开辟动态空间(对于动态版本的,这也是主要修改的地方)
注意:动态版本的在退出通讯录之前,要进行内存释放 free()

三、Contact.c源文件的功能实现

初始化结构体变量

首先最应该做的就是定义初始化函数,再使用指针参数时,可以用assert函数来判断该参数是否为空指针来预防。
用memset函数进行初始化

//初始化通讯录
void InitContact(Contact* con)
{
	assert(con);             
	con->sz = 0;
	memset(con, 0, sizeof(PeoInfo));                        //memse函数是内存块复制函数,将PeoInfo类型数组的内存空间都变为0
}

增加联系人

再开始之前,应该先判断是否为空的通讯录,然后便是scanf函数进行输入,这里也可以设计while()来进行多次输入

//增加通讯录的联系人信息
void AddContact(Contact* con)
{
	//判断通讯录是否已经满了
	if (con->sz == MAX)
	{
		printf("通讯录已经满了\n");
		return;
	}

	//此处可以思考设计一次性选择加入多个还是一个
	printf("请输入姓名:>");
	scanf("%s", con->date[con->sz].name);
	printf("请输入年龄:>");
	scanf("%d", &(con->date[con->sz].age));						//这里注意了,结构体变量还是要取地址
	printf("请输入性别:>");
	scanf("%s", con->date[con->sz].sex);
	printf("请输入电话:>");
	scanf("%s", con->date[con->sz].telenumber);
	printf("请输入地址:>");
	scanf("%s", con->date[con->sz].address);

	con->sz++;
	printf("增加成功\n");
}

显示所有联系人

这里设计首行打印的格式,也可以用右对齐等%20s

//显示所有联系人的信息
void  ShowContact(const Contact *con)
{
	//判断空指针,再判断通讯录是否为空
	assert(con);
	if (con->sz == 0)
	{
		printf("通讯录为空,无联系人\n");
		return;
	}

	//打印的格式
	// 名字       年龄         性别          电话              地址
	//xxx          xx            xx           xxxxxxx        xxxxxx
	printf("%-20s%-5s%-12s%-20s%-30s\n", "名字", "年龄", "性别", "电话", "地址");

	for (int i = 0;i < con->sz;i++)
	{
		printf("%-20s", con->date[i].name);
		printf("%-5d", con->date[i].age);
		printf("%-12s", con->date[i].sex);
		printf("%-20s", con->date[i].telenumber);
		printf("%-30s", con->date[i].address);
		printf("\n");
	}
}

查找联系人

再构建查找,删除,修改联系人的思路,他们都有一个共同点,就是都需要知道需要查找或修改的这个联系人在数组中位置,所以设计这类多次使用的功能也许要单封装一个函数来实现

查找联系人,返回具体坐标

//因为modify函数和del函数和search函数都要查找到某个联系人的信息再进行操作
//定义一个查找联系人姓名的函数,返回一个查找到这个联系人所在数组的下标
//这里的static可以不加,加了只在此文档用
static int  FindByname(const Contact* con, char name[])     
{
	assert(con);
	if (con->sz == 0)
	{
		return -2;
	}
	int i ;//返回变量
	for (i = 0;i < con->sz;i++)
	{
		if (strcmp(con->date[i].name, name) == 0)
			return i;
	}
	return -1;
}
//查找某个联系人的信息
void SearchContact(const Contact* con)
{
	//还是一样先判断指针是否为空,再判断通讯录是否为空
	assert(con);
	if (con->sz == 0)
	{
		printf("通讯录为空,请重新选择\n");
		return;
	}
	char name[Name_Size];
	printf("请输入要查找的联系人:>");
	while (scanf("%s", &name) != EOF)
	{
		int tem = FindByname(con, name);
		if (tem >= 0)
		{
			//套用show函数的输出格式
			printf("%-20s%-5s%-12s%-20s%-30s\n", "名字", "年龄", "性别", "电话", "地址");
			printf("%-20s", con->date[tem].name);
			printf("%-5d", con->date[tem].age);
			printf("%-12s", con->date[tem].sex);
			printf("%-20s", con->date[tem].telenumber);
			printf("%-30s\n", con->date[tem].address);
			break;
		}
		else
		{
			printf("未查到相关联系人,请重新输入\n");
		}
	}
}

修改联系人

//修改某个联系人的信息
void ModifyContact(Contact* con)					//需要修改联系人的位置,或者相关信息,
{
	//先判断是否为空指针,在判断通讯录是否为空
	assert(con);
	char name[Name_Size];
	if (con->sz == 0)
	{
		printf("通讯录为空,没有可修改的联系人\n");
		return;
	}
	printf("请输入要修改联系人的姓名:>");
	scanf("%s", &name);
	int tem = FindByname(con, name);				
	if (tem >= 0)
	{
		printf("请输入修改内容\n");
		printf("请输入姓名:>");
		scanf("%s", con->date[tem].name);
		printf("请输入年龄:>");
		scanf("%d", &(con->date[tem].age));						//这里注意了,结构体变量还是要去地址
		printf("请输入性别:>");
		scanf("%s", con->date[tem].sex);
		printf("请输入电话:>");
		scanf("%s", con->date[tem].telenumber);
		printf("请输入地址:>");
		scanf("%s", con->date[tem].address);
		printf("修改成功\n");
	}
	else
		printf("未查到该联系人\n");
}

删除联系人

在删除联系人时,可以联想,一个int tem[100]数组,在将i+1的值赋予i的坐标来实现删除i坐标的数据

//删除某个联系人的信息
void DeleteContact(Contact* con)
{
	assert(con);
	if (con->sz == 0)
	{
		printf("通讯录为空,请重新选择\n");
		return;
	}

	char name[Name_Size];
	printf("请输入要删除的联系人的姓名:>");
	while (scanf("%s", &name) != EOF)
	{
		int tem = FindByname(con, name);
		if (tem >= 0)
		{
			for (int i = tem;i < con->sz;i++)											//这里是将数组为i下标的后一个date[i+1]赋值给date[i]来达到删除的效果
			{
				con->date[tem] = con->date[tem + 1];
			}

			con->sz--;						//注意删除后修改通讯录的大小
			printf("删除成功\n");
			break;
		}
		else
		{
			printf("未查到该联系人,请重新输入\n");
		}
	}
}

四、源代码

1.静态版本

1.1Contact.h

#pragma once

#include
#include
#include

#define Name_Size 20
#define  Sex_Size 5
#define Tele_Size 12
#define Add_Size 30

#define MAX 100



//类型的声明
typedef struct PeoInfo
{
	char name[Name_Size];
	int age;
	char sex[Sex_Size];                                           //联想到bool类型
	char telenumber[Tele_Size];										//这里能不能用int数组或者直接用int记录电话
	char address[Add_Size];
}PeoInfo;

typedef struct Contact
{
	PeoInfo date[MAX];			//这里PeoInfo  理解为类型,可以看成   int date[100]
	int sz;								//记录已有联系人数量
}Contact;

//初始化通讯录
void InitContact(Contact* con);

//增加通讯录的联系人信息
void AddContact(Contact* con);

//显示所有联系人的信息
void  ShowContact(const Contact* con);

//修改某个联系人的信息
void ModifyContact(Contact* con);

//删除某个联系人的信息
void DeleteContact(Contact* con);

//查找某个联系人的信息
void SearchContact(const Contact* con);

1.2Test_Contact.c

#define _CRT_SECURE_NO_WARNINGS 1

#include"contact.1.h"

enum
{
	EXIT,				//没有指定,默认以0开始
	ADD,			//1
	DEL,
	SEARCH,
	MODIFY,
	SHOW,
	SORT,
};

void menu()
{
	printf("******1.add      ******\n");
	printf("******2.del       ******\n");
	printf("******3.search  ******\n");
	printf("******4.modify ******\n");
	printf("******5.show    ******\n");
	printf("******6.sort      ******\n");
	printf("******0.exit      ******\n");
}

int main()
{
	int input;
	Contact con;			//创建通讯录
	InitContact(&con);

	do
	{
		menu();
		printf("请输入操作选项:>");
		scanf("%d", &input);
		switch (input)
		{
		case ADD:
			//add();
			AddContact(&con);
			break;
		case DEL:
			//del();
			DeleteContact(&con);
			break;
		case SEARCH:
			//search();
			SearchContact(&con);
			break;
		case MODIFY:
			//modify();
			ModifyContact(&con);
			break;
		case SHOW:
			//show();
			ShowContact(&con);
			break;
		case SORT:
			//sort();按什么排序,qsort
			printf("待完善\n");
			break;
		case EXIT:
			printf("退出成功\n");
			break;
		default:
			printf("输入错误,重新选择!\n");
			break;

		}
	} while (input);

	return 0;
}

1.3 Contact.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.1.h"

//初始化通讯录
void InitContact(Contact* con)
{
	assert(con);             
	con->sz = 0;
	memset(con, 0, sizeof(PeoInfo));                        //memse函数是内存块复制函数,将PeoInfo类型数组的内存空间都变为0
}

//增加通讯录的联系人信息
void AddContact(Contact* con)
{
	//判断通讯录是否已经满了
	if (con->sz == MAX)
	{
		printf("通讯录已经满了\n");
		return;
	}

	//此处可以思考设计一次性选择加入多个还是一个
	printf("请输入姓名:>");
	scanf("%s", con->date[con->sz].name);
	printf("请输入年龄:>");
	scanf("%d", &(con->date[con->sz].age));						//这里注意了,结构体变量还是要去地址
	printf("请输入性别:>");
	scanf("%s", con->date[con->sz].sex);
	printf("请输入电话:>");
	scanf("%s", con->date[con->sz].telenumber);
	printf("请输入地址:>");
	scanf("%s", con->date[con->sz].address);
	//scanf("%s\n", con->date->address);    犯了俩个错误

	con->sz++;
	printf("增加成功\n");
}

//显示所有联系人的信息
void  ShowContact(const Contact *con)
{
	//判断空指针,再判断通讯录是否为空
	assert(con);
	if (con->sz == 0)
	{
		printf("通讯录为空,无联系人\n");
		return;
	}

	//打印的格式
	// 名字       年龄         性别          电话              地址
	//xxx          xx            xx           xxxxxxx        xxxxxx
	printf("%-20s%-5s%-12s%-20s%-30s\n", "名字", "年龄", "性别", "电话", "地址");

	for (int i = 0;i < con->sz;i++)
	{
		printf("%-20s", con->date[i].name);
		printf("%-5d", con->date[i].age);
		printf("%-12s", con->date[i].sex);
		printf("%-20s", con->date[i].telenumber);
		printf("%-30s", con->date[i].address);
		printf("\n");
	}
}

//因为modify函数和del函数和search函数都要查找到某个联系人的信息再进行操作
//定义一个查找联系人姓名的函数,返回一个查找到这个联系人所在数组的下标
//这里的static可以不加,加了只在此文档用
static int  FindByname(const Contact* con, char name[])     
{
	assert(con);
	if (con->sz == 0)
	{
		return -2;
	}
	int i ;//返回变量
	for (i = 0;i < con->sz;i++)
	{
		if (strcmp(con->date[i].name, name) == 0)
			return i;
	}
	return -1;
}

//修改某个联系人的信息
void ModifyContact(Contact* con)					//需要修改联系人的位置,或者相关信息,
{
	//先判断是否为空指针,在判断通讯录是否为空
	assert(con);
	char name[Name_Size];
	if (con->sz == 0)
	{
		printf("通讯录为空,没有可修改的联系人\n");
		return;
	}
	printf("请输入要修改联系人的姓名:>");
	scanf("%s", &name);
	int tem = FindByname(con, name);				
	if (tem >= 0)
	{
		printf("请输入修改内容\n");
		printf("请输入姓名:>");
		scanf("%s", con->date[tem].name);
		printf("请输入年龄:>");
		scanf("%d", &(con->date[tem].age));						//这里注意了,结构体变量还是要去地址
		printf("请输入性别:>");
		scanf("%s", con->date[tem].sex);
		printf("请输入电话:>");
		scanf("%s", con->date[tem].telenumber);
		printf("请输入地址:>");
		scanf("%s", con->date[tem].address);
		printf("修改成功\n");
	}
	else
		printf("未查到该联系人\n");
}

//删除某个联系人的信息
void DeleteContact(Contact* con)
{
	assert(con);
	if (con->sz == 0)
	{
		printf("通讯录为空,请重新选择\n");
		return;
	}

	char name[Name_Size];
	printf("请输入要删除的联系人的姓名:>");
	while (scanf("%s", &name) != EOF)
	{
		int tem = FindByname(con, name);
		if (tem >= 0)
		{
			for (int i = tem;i < con->sz;i++)											//这里是将数组为i下标的后一个date[i+1]赋值给date[i]来达到删除的效果
			{
				con->date[tem] = con->date[tem + 1];
			}

			con->sz--;						//注意删除后修改通讯录的大小
			printf("删除成功\n");
			break;
		}
		else
		{
			printf("未查到该联系人,请重新输入\n");
		}
	}
}

//查找某个联系人的信息
void SearchContact(const Contact* con)
{
	//还是一样先判断指针是否为空,再判断通讯录是否为空
	assert(con);
	if (con->sz == 0)
	{
		printf("通讯录为空,请重新选择\n");
		return;
	}
	char name[Name_Size];
	printf("请输入要查找的联系人:>");
	while (scanf("%s", &name) != EOF)
	{
		int tem = FindByname(con, name);
		if (tem >= 0)
		{
			//套用show函数的输出格式
			printf("%-20s%-5s%-12s%-20s%-30s\n", "名字", "年龄", "性别", "电话", "地址");
			printf("%-20s", con->date[tem].name);
			printf("%-5d", con->date[tem].age);
			printf("%-12s", con->date[tem].sex);
			printf("%-20s", con->date[tem].telenumber);
			printf("%-30s\n", con->date[tem].address);
			break;
		}
		else
		{
			printf("未查到相关联系人,请重新输入\n");
		}
	}
}

2.动态版本

动态版本主要将结构体变量修改为 指针变量
capacity是指当前的动态空间

typedef struct Contact
{
	PeoInfo* date;		//这里PeoInfo  理解为类型,可以看成   int date[100]
	int sz;								//记录已有联系人数量
	int capacity;
}Contact;

初始化函数

void InitContact(Contact* con)
{
	assert(con);
	con->sz = 0;
	con->capacity = DEFAULT_SZ;  //这里的DEFAULT_SZ  为
	con->date = (PeoInfo *)calloc(DEFAULT_SZ, sizeof(PeoInfo));
	if (NULL== con->date )
	{
		perror("InitContact->calloc");          //此处可不用写,用来检测开辟内存失败,报错
		return;
	}
}

检测当前函数是否需要动态分配空间,关于realloc函数的介绍,可以参考

void Checkcapacity(Contact* con)
{
	PeoInfo* ptr = (PeoInfo*)realloc(con->date, (con->capacity + DEFAULT_capa) * sizeof(PeoInfo));
	if (ptr != NULL)
	{
		con->date = ptr;
		con->capacity += DEFAULT_capa;
		printf("已增容");
	}
	else
	{
		perror("AddContact->realloc");       //此处也可不用
		return;
	}
}

最后要注意释放空间

void DestroyContact(Contact* con)
{
	free(con->date);
	con->date = NULL;
	con->sz = 0;
	con->capacity = 0;
}

头文件

#pragma once

#include
#include
#include
#include

#define Name_Size 20
#define  Sex_Size 5
#define Tele_Size 12
#define Add_Size 30

#define DEFAULT_SZ 2
#define DEFAULT_capa 1

//类型的声明
typedef struct PeoInfo
{
	char name[Name_Size];
	int age;
	char sex[5];                                           //联想到bool类型
	char telenumber[15];										
	char address[20];
}PeoInfo;

//静态版本
//typedef struct Contact
//{
//	PeoInfo date[MAX];			//这里PeoInfo  理解为类型,可以看成   int date[100]
//	int sz;								//记录已有联系人数量
//}Contact;


typedef struct Contact
{
	PeoInfo* date;		
	int sz;								//记录已有联系人数量
	int capacity;
}Contact;

//初始化通讯录
void InitContact(Contact* con);

//增加通讯录的联系人信息
void AddContact(Contact* con);

//显示所有联系人的信息
void  ShowContact(const Contact* con);

//修改某个联系人的信息
void ModifyContact(Contact* con);

//删除某个联系人的信息
void DeleteContact(Contact* con);

//查找某个联系人的信息
void SearchContact(const Contact* con);

//释放动态内存空间
void DestroyContact(Contact* con);

C文件

#define _CRT_SECURE_NO_WARNINGS 1

#include"contact.2.h"

enum
{
	EXIT,				//没有指定,默认以0开始
	ADD,			//1
	DEL,
	SEARCH,
	MODIFY,
	SHOW,
	SORT,
};

void menu()
{
	printf("******1.add      ******\n");
	printf("******2.del       ******\n");
	printf("******3.search  ******\n");
	printf("******4.modify ******\n");
	printf("******5.show    ******\n");
	printf("******6.sort      ******\n");
	printf("******0.exit      ******\n");
}

int main()
{
	int input;
	Contact con;			//创建通讯录
	InitContact(&con);

	do
	{
		menu();
		printf("请输入操作选项:>");
		scanf("%d", &input);
		switch (input)
		{
		case ADD:
			//add();
			AddContact(&con);
			break;
		case DEL:
			//del();
			DeleteContact(&con);
			break;
		case SEARCH:
			//search();
			SearchContact(&con);
			break;
		case MODIFY:
			//modify();
			ModifyContact(&con);
			break;
		case SHOW:
			//show();
			ShowContact(&con);
			break;
		case SORT:
			//sort();按什么排序,qsort
			printf("待完善\n");
			break;
		case EXIT:
			printf("退出成功\n");
			DestroyContact(&con);
			break;
		default:
			printf("输入错误,重新选择!\n");
			break;

		}
	} while (input);

	return 0;
}
#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.2.h"

//静态初始化通讯录
//void InitContact(Contact* con)
//{
//	assert(con);             
//	con->sz = 0;
//	memset(con, 0, sizeof(PeoInfo));                        //memse函数是内存块复制函数,将PeoInfo类型数组的内存空间都变为0
//}

void InitContact(Contact* con)
{
	assert(con);
	con->sz = 0;
	con->capacity = DEFAULT_SZ;
	con->date = (PeoInfo *)calloc(DEFAULT_SZ, sizeof(PeoInfo));
	if (NULL== con->date )
	{
		perror("InitContact->calloc");          //此处可不用,用来检测开辟内存失败,报错
		return;
	}
}

void Checkcapacity(Contact* con);

//增加通讯录的联系人信息
void AddContact(Contact* con)
{
	assert(con);
	//判断当前通讯录大小是否需要扩容
	if (con->sz == con->capacity)
	{
		Checkcapacity(con);
	}
	printf("请输入姓名:>");
	scanf("%s", con->date[con->sz].name);
	printf("请输入年龄:>");
	scanf("%d", &(con->date[con->sz].age));						
	printf("请输入性别:>");
	scanf("%s", con->date[con->sz].sex);
	printf("请输入电话:>");
	scanf("%s", con->date[con->sz].telenumber);
	printf("请输入地址:>");
	scanf("%s", con->date[con->sz].address);


	con->sz++;
	printf("增加成功\n");
}

//检查当前通讯录是否需要增容
void Checkcapacity(Contact* con)
{
	PeoInfo* ptr = (PeoInfo*)realloc(con->date, (con->capacity + DEFAULT_capa) * sizeof(PeoInfo));
	if (ptr != NULL)
	{
		con->date = ptr;
		con->capacity += DEFAULT_capa;
		printf("已增容");
	}
	else
	{
		perror("AddContact->realloc");       //此处也可不用
		return;
	}
}

void DestroyContact(Contact* con)
{
	free(con->date);
	con->date = NULL;
	con->sz = 0;
	con->capacity = 0;
}

//显示所有联系人的信息
void  ShowContact(const Contact *con)
{
	//判断空指针,再判断通讯录是否为空
	assert(con);
	if (con->sz == 0)
	{
		printf("通讯录为空,无联系人\n");
		return;
	}

	//打印的格式
	// 名字       年龄         性别          电话              地址
	//xxx          xx            xx           xxxxxxx        xxxxxx
	printf("%-20s%-5s%-12s%-20s%-30s\n", "名字", "年龄", "性别", "电话", "地址");

	for (int i = 0;i < con->sz;i++)
	{
		printf("%-20s", con->date[i].name);
		printf("%-5d", con->date[i].age);
		printf("%-12s", con->date[i].sex);
		printf("%-20s", con->date[i].telenumber);
		printf("%-30s", con->date[i].address);
		printf("\n");
	}
}
static int  FindByname(const Contact* con, char name[])     
{
	assert(con);
	if (con->sz == 0)
	{
		return -2;
	}
	int i ;//返回变量
	for (i = 0;i < con->sz;i++)
	{
		if (strcmp(con->date[i].name, name) == 0)
			return i;
	}
	return -1;
}

//修改某个联系人的信息
void ModifyContact(Contact* con)					
{
	//先判断是否为空指针,在判断通讯录是否为空
	assert(con);
	char name[Name_Size];
	if (con->sz == 0)
	{
		printf("通讯录为空,没有可修改的联系人\n");
		return;
	}
	printf("请输入要修改联系人的姓名:>");
	scanf("%s", &name);
	int tem = FindByname(con, name);				
	if (tem >= 0)
	{
		printf("请输入修改内容\n");
		printf("请输入姓名:>");
		scanf("%s", con->date[tem].name);
		printf("请输入年龄:>");
		scanf("%d", &(con->date[tem].age));						
		printf("请输入性别:>");
		scanf("%s", con->date[tem].sex);
		printf("请输入电话:>");
		scanf("%s", con->date[tem].telenumber);
		printf("请输入地址:>");
		scanf("%s", con->date[tem].address);
		printf("修改成功\n");
	}
	else
		printf("未查到该联系人\n");
}

//删除某个联系人的信息
void DeleteContact(Contact* con)
{
	assert(con);
	if (con->sz == 0)
	{
		printf("通讯录为空,请重新选择\n");
		return;
	}

	char name[Name_Size];
	printf("请输入要删除的联系人的姓名:>");
	while (scanf("%s", &name) != EOF)
	{
		int tem = FindByname(con, name);
		if (tem >= 0)
		{
			for (int i = tem;i < con->sz;i++)											
			{
				con->date[tem] = con->date[tem + 1];
			}

			con->sz--;						
			printf("删除成功\n");
			break;
		}
		else
		{
			printf("未查到该联系人,请重新输入\n");
		}
	}
}

//查找某个联系人的信息
void SearchContact(const Contact* con)
{
	//还是一样先判断指针是否为空,再判断通讯录是否为空
	assert(con);
	if (con->sz == 0)
	{
		printf("通讯录为空,请重新选择\n");
		return;
	}
	char name[Name_Size];
	printf("请输入要查找的联系人:>");
	while (scanf("%s", &name) != EOF)
	{
		int tem = FindByname(con, name);
		if (tem >= 0)
		{
			printf("%-20s%-5s%-12s%-20s%-30s\n", "名字", "年龄", "性别", "电话", "地址");
			printf("%-20s", con->date[tem].name);
			printf("%-5d", con->date[tem].age);
			printf("%-12s", con->date[tem].sex);
			printf("%-20s", con->date[tem].telenumber);
			printf("%-30s\n", con->date[tem].address);
			break;
		}
		else
		{
			printf("未查到相关联系人,请重新输入\n");
		}
	}
}

3.文件流版本

文件流版本主要是,将数据从文件加载到变量,然后退出时,再将变量数据加载到文件中,同时要注意既然用的文件流,当然要在此目录下,建立同后缀的文档如:
C语言之通讯录版本----从入门到精通_第1张图片

LoadContact函数,可以参考菜鸟教程的文件文件操作
这里用到的fread和fwrite函数是二进制输入输出流函数

void LoadContact(Contact* con)
{
	FILE* pf = fopen("contact.txt", "rb");
	if (pf == NULL)
	{
		perror("LoadContact->FILE");
		return;
	}
	PeoInfo tem = { 0 };     //定义一个联系人的变量用来读取文件的信息,作为文件信息缓冲区,在赋值给通讯录con里
	while (fread(&tem, sizeof(PeoInfo), 1, pf))        //可以查询关于二进制输入函数fread,返回值为读取数,成功便大于零
	{
		//这里要注意当文件中的联系人信息数量大于当前初始化通讯录的容量时,通讯录是需要扩容才能装下
		Checkcapacity(con);
		con->date[con->sz] = tem;             //将tem的值赋予con的date域中
		con->sz++;
	}
	fclose(pf);
	pf = NULL;
}

perror函数是报告出错的地方,便于调试

//退出通讯录,保存文件信息
void SaveContact(Contact* con)
{
	FILE* pf = fopen("contact.txt", "wb");
	//判断文件指针是否开辟
	if (pf == NULL)
	{
		perror("SaveContact->FILE");
		return;
	}
	for (int i = 0;i < con->sz;i++)
	{
		fwrite(&(con->date[i]), sizeof(PeoInfo), 1, pf);          //这里也可以用con->date+i
		//fwrite(con->date+i, sizeof(PeoInfo), 1, pf);          
	}
	fclose(pf);
	pf = NULL;

头文件

#pragma once

#include
#include
#include
#include

#define Name_Size 20
#define  Sex_Size 5
#define Tele_Size 12
#define Add_Size 30

#define DEFAULT_SZ 2
#define DEFAULT_capa 1



//类型的声明
typedef struct PeoInfo
{
	char name[Name_Size];
	int age;
	char sex[5];                                           //联想到bool类型
	char telenumber[15];										//这里能不能用int数组或者直接用int记录电话
	char address[20];
}PeoInfo;

//静态版本
//typedef struct Contact
//{
//	PeoInfo date[MAX];			//这里PeoInfo  理解为类型,可以看成   int date[100]
//	int sz;								//记录已有联系人数量
//}Contact;


typedef struct Contact
{
	PeoInfo* date;		//这里PeoInfo  理解为类型,可以看成   int date[100]
	int sz;								//记录已有联系人数量
	int capacity;
}Contact;

//初始化通讯录
void InitContact(Contact* con);

//增加通讯录的联系人信息
void AddContact(Contact* con);

//显示所有联系人的信息
void  ShowContact(const Contact* con);

//修改某个联系人的信息
void ModifyContact(Contact* con);

//删除某个联系人的信息
void DeleteContact(Contact* con);

//查找某个联系人的信息
void SearchContact(const Contact* con);

//释放动态内存空间
void DestroyContact(Contact* con);

//退出通讯录,保存数据到文件中(保存信息)
void SaveContact(Contact* con);

源文件

#define _CRT_SECURE_NO_WARNINGS 1

#include"contact.2.h"

enum
{
	EXIT,				//没有指定,默认以0开始
	ADD,			//1
	DEL,
	SEARCH,
	MODIFY,
	SHOW,
	SORT,
};

void menu()
{
	printf("******1.add      ******\n");
	printf("******2.del       ******\n");
	printf("******3.search  ******\n");
	printf("******4.modify ******\n");
	printf("******5.show    ******\n");
	printf("******6.sort      ******\n");
	printf("******0.exit      ******\n");
}

int main()
{
	int input;
	Contact con;			//创建通讯录
	InitContact(&con);

	do
	{
		menu();
		printf("请输入操作选项:>");
		scanf("%d", &input);
		switch (input)
		{
		case ADD:
			//add();
			AddContact(&con);
			break;
		case DEL:
			//del();
			DeleteContact(&con);
			break;
		case SEARCH:
			//search();
			SearchContact(&con);
			break;
		case MODIFY:
			//modify();
			ModifyContact(&con);
			break;
		case SHOW:
			//show();
			ShowContact(&con);
			break;
		case SORT:
			//sort();按什么排序,qsort
			printf("待完善\n");
			break;
		case EXIT:
			printf("退出成功\n");
			//SaveContact(&con);
			SaveContact(&con);
			DestroyContact(&con);
			break;
		default:
			printf("输入错误,重新选择!\n");
			break;

		}
	} while (input);

	return 0;
}

源文件—功能实现

#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.2.h"

//静态初始化通讯录
//void InitContact(Contact* con)
//{
//	assert(con);             
//	con->sz = 0;
//	memset(con, 0, sizeof(PeoInfo));                        //memse函数是内存块复制函数,将PeoInfo类型数组的内存空间都变为0
//}

void Checkcapacity(Contact* con);

//加载文件数据
void LoadContact(Contact* con)
{
	FILE* pf = fopen("contact.txt", "rb");
	if (pf == NULL)
	{
		perror("LoadContact->FILE");
		return;
	}
	PeoInfo tem = { 0 };     //定义一个联系人的变量用来读取文件的信息,作为文件信息缓冲区,在赋值给通讯录con里
	while (fread(&tem, sizeof(PeoInfo), 1, pf))        //可以查询关于二进制输入函数fread,返回值为读取数,成功便大于零
	{
		//这里要注意当文件中的联系人信息数量大于当前初始化通讯录的容量时,通讯录是需要扩容才能装下
		Checkcapacity(con);
		con->date[con->sz] = tem;             //将tem的值赋予con的date域中
		con->sz++;
	}
	fclose(pf);
	pf = NULL;
}

void InitContact(Contact* con)
{
	assert(con);
	con->sz = 0;
	con->capacity = DEFAULT_SZ;
	con->date = (PeoInfo *)calloc(DEFAULT_SZ, sizeof(PeoInfo));
	if (NULL== con->date )
	{
		perror("InitContact->calloc");          //此处可不用,用来检测开辟内存失败,报错
		return;
	}
	LoadContact(con);
}

//增加通讯录的联系人信息
void AddContact(Contact* con)
{
	assert(con);
	//判断当前通讯录大小是否需要扩容
	Checkcapacity(con);

	//此处可以思考设计一次性选择加入多个还是一个
	printf("请输入姓名:>");
	scanf("%s", con->date[con->sz].name);
	printf("请输入年龄:>");
	scanf("%d", &(con->date[con->sz].age));						//这里注意了,结构体变量还是要去地址
	printf("请输入性别:>");
	scanf("%s", con->date[con->sz].sex);
	printf("请输入电话:>");
	scanf("%s", con->date[con->sz].telenumber);
	printf("请输入地址:>");
	scanf("%s", con->date[con->sz].address);
	//scanf("%s\n", con->date->address);    犯了俩个错误

	con->sz++;
	printf("增加成功\n");
}

//检查当前通讯录是否需要增容
void Checkcapacity(Contact* con)
{
	if (con->sz == con->capacity)
	{
		PeoInfo* ptr = (PeoInfo*)realloc(con->date, (con->capacity + DEFAULT_capa) * sizeof(PeoInfo));
		if (ptr != NULL)
		{
			con->date = ptr;
			con->capacity += DEFAULT_capa;
			printf("已增容");
		}
		else
		{
			perror("AddContact->realloc");       //此处也可不用
			return;
		}
	}
}

//动态内存释放,通讯录的动态内存释放
void DestroyContact(Contact* con)
{
	free(con->date);
	con->date = NULL;
	con->sz = 0;
	con->capacity = 0;
}

//显示所有联系人的信息
void  ShowContact(const Contact *con)
{
	//判断空指针,再判断通讯录是否为空
	assert(con);
	if (con->sz == 0)
	{
		printf("通讯录为空,无联系人\n");
		return;
	}

	//打印的格式
	// 名字       年龄         性别          电话              地址
	//xxx          xx            xx           xxxxxxx        xxxxxx
	printf("%-20s%-5s%-12s%-20s%-30s\n", "名字", "年龄", "性别", "电话", "地址");

	for (int i = 0;i < con->sz;i++)
	{
		printf("%-20s", con->date[i].name);
		printf("%-5d", con->date[i].age);
		printf("%-12s", con->date[i].sex);
		printf("%-20s", con->date[i].telenumber);
		printf("%-30s", con->date[i].address);
		printf("\n");
	}
}

//因为modify函数和del函数和search函数都要查找到某个联系人的信息再进行操作
//定义一个查找联系人姓名的函数,返回一个查找到这个联系人所在数组的下标
//这里的static可以不加,加了只在此文档用
static int  FindByname(const Contact* con, char name[])     
{
	assert(con);
	if (con->sz == 0)
	{
		return -2;
	}
	int i ;//返回变量
	for (i = 0;i < con->sz;i++)
	{
		if (strcmp(con->date[i].name, name) == 0)
			return i;
	}
	return -1;
}

//修改某个联系人的信息
void ModifyContact(Contact* con)					//需要修改联系人的位置,或者相关信息,
{
	//先判断是否为空指针,在判断通讯录是否为空
	assert(con);
	char name[Name_Size];
	if (con->sz == 0)
	{
		printf("通讯录为空,没有可修改的联系人\n");
		return;
	}
	printf("请输入要修改联系人的姓名:>");
	scanf("%s", &name);
	int tem = FindByname(con, name);				
	if (tem >= 0)
	{
		printf("请输入修改内容\n");
		printf("请输入姓名:>");
		scanf("%s", con->date[tem].name);
		printf("请输入年龄:>");
		scanf("%d", &(con->date[tem].age));						//这里注意了,结构体变量还是要去地址
		printf("请输入性别:>");
		scanf("%s", con->date[tem].sex);
		printf("请输入电话:>");
		scanf("%s", con->date[tem].telenumber);
		printf("请输入地址:>");
		scanf("%s", con->date[tem].address);
		printf("修改成功\n");
	}
	else
		printf("未查到该联系人\n");
}

//删除某个联系人的信息
void DeleteContact(Contact* con)
{
	assert(con);
	if (con->sz == 0)
	{
		printf("通讯录为空,请重新选择\n");
		return;
	}

	char name[Name_Size];
	printf("请输入要删除的联系人的姓名:>");
	while (scanf("%s", &name) != EOF)
	{
		int tem = FindByname(con, name);
		if (tem >= 0)
		{
			for (int i = tem;i < con->sz;i++)											//这里是将数组为i下标的后一个date[i+1]赋值给date[i]来达到删除的效果
			{
				con->date[tem] = con->date[tem + 1];
			}

			con->sz--;						//注意删除后修改通讯录的大小
			printf("删除成功\n");
			break;
		}
		else
		{
			printf("未查到该联系人,请重新输入\n");
		}
	}
}

//查找某个联系人的信息
void SearchContact(const Contact* con)
{
	//还是一样先判断指针是否为空,再判断通讯录是否为空
	assert(con);
	if (con->sz == 0)
	{
		printf("通讯录为空,请重新选择\n");
		return;
	}
	char name[Name_Size];
	printf("请输入要查找的联系人:>");
	while (scanf("%s", &name) != EOF)
	{
		int tem = FindByname(con, name);
		if (tem >= 0)
		{
			//套用show函数的输出格式
			printf("%-20s%-5s%-12s%-20s%-30s\n", "名字", "年龄", "性别", "电话", "地址");
			printf("%-20s", con->date[tem].name);
			printf("%-5d", con->date[tem].age);
			printf("%-12s", con->date[tem].sex);
			printf("%-20s", con->date[tem].telenumber);
			printf("%-30s\n", con->date[tem].address);
			break;
		}
		else
		{
			printf("未查到相关联系人,请重新输入\n");
		}
	}
}

//退出通讯录,保存文件信息
void SaveContact(Contact* con)
{
	FILE* pf = fopen("contact.txt", "wb");
	//判断文件指针是否开辟
	if (pf == NULL)
	{
		perror("SaveContact->FILE");
		return;
	}
	for (int i = 0;i < con->sz;i++)
	{
		fwrite(&(con->date[i]), sizeof(PeoInfo), 1, pf);          //这里也可以用con->date+i
		//fwrite(con->date+i, sizeof(PeoInfo), 1, pf);          
	}
	fclose(pf);
	pf = NULL;
}

五、总结

总体来说,基本功能都已实现,关于排序,可以用qsort函数来实现,更高级的通讯录系统,可以用到图形化工具来展示,还有easyx等,美化界面,最后将整个项目打包成一个.exe的可执行程序,由此也可以联想到之后的多线程聊天功能。总之,一步步继续沉淀,从无到有,从缺少到完善。

你可能感兴趣的:(c语言,算法)