C语言之通讯录的实现(静态版,动态版,文件版),C语言通讯录功能实现详解,静态版、动态版与文件版操作指南

马肤

温馨提示:这篇文章已超过470天没有更新,请注意相关的内容是否还可用!

摘要:,,本文介绍了C语言中通讯录的实现,包括静态版、动态版和文件版三种方式。静态版实现简单,但功能受限;动态版则通过动态内存分配实现通讯录的灵活管理。文件版则实现了通讯录数据的持久化存储和读取,提高了程序的实用性和便捷性。本文详细阐述了各种实现方式的原理、代码实现及注意事项,为读者提供了通讯录程序开发的参考。

个人主页(找往期文章包括但不限于本期文章中不懂的知识点): 我要学编程(ಥ_ಥ)-CSDN博客

目录

静态通讯录的实现逻辑 

test.c:通讯录的逻辑实现

Contact.h:函数的声明与头文件的包含

Contact.c:函数的实现 

通讯录源码: 

test.c:

Contact.c:

Contect.h:

动态版通讯录 

test.c:

Contact.h:

Contact.c: 

动态通讯录(文件版)

test.c: 

Contact.h: 

Contact.c: 


静态通讯录的实现逻辑 

test.c:通讯录的逻辑实现

我们今天就一起来用c语言写一个通讯录的小程序

这个通讯录可以实现存储100个联系人。

首先,我们进去这个通讯录,肯定得有一些基本的功能。如下图:

C语言之通讯录的实现(静态版,动态版,文件版),C语言通讯录功能实现详解,静态版、动态版与文件版操作指南 第1张

这个就和我们在前面写游戏的时候是一样的。

void menu()
{
	printf("******************************************\n");
	printf("****  1.增加联系人      2.删除联系人  ****\n");
	printf("****  3.查找联系人      4.修改联系人  ****\n");
	printf("****  5.显示联系人     6.排序联系人   ****\n");
	printf("****           0.退出通讯录           ****\n");
	printf("******************************************\n");
}

打印出界面之后,我们就得根据需求来进行选择。

void test()
{
	int input = 0;
	//下面是对通讯录进行的各种操作,所以我们得先有一个通讯录
    //创建一个通讯录,包含一个人的各种信息,是一个复杂对象,用结构体来描述
	Contact con;
	InitContact(&con);//初始化通讯录
	do
	{
		menu();//打印基本功能
		printf("请选择:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			AddContact(&con);//增加联系人的信息
			break;
		case 2:
			DelContact(&con);//删除联系人的信息
			break;
		case 3:
			SearchContact(&con);//查找联系人的信息
			break;
		case 4:
			ModifyContact(&con);//修改联系人的信息
			break;
		case 5:
			ShowContact(&con);//显示联系人的信息
			break;
		case 6:
			SortContact(&con);//排序联系人
			break;
		case 0:
            printf("退出通讯录\n");
			break;
		default:
            printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);
}

为了让别人能够更清楚我们代码的功能,我们最好能把那些case后面的数字,改成ADD这些能够让别人一下就能够看懂。我们就可以联想到联合体。自定义类型:联合和枚举-CSDN博客

上面这篇博客,有关于联合体的知识。

void test()
{
	int input = 0;
	//下面是对通讯录进行的各种操作,所以我们得先有一个通讯录
	Contact con;
	InitContact(&con);
	do
	{
		menu();
		printf("请选择:");
		scanf("%d", &input);
		switch (input)
		{
		case ADD:
			AddContact(&con);//增加联系人的信息
			break;
		case DEL:
			DelContact(&con);//删除联系人的信息
			break;
		case SEARCH:
			SearchContact(&con);//查找联系人的信息
			break;
		case MODIFY:
			ModifyContact(&con);//修改联系人的信息
			break;
		case SHOW:
			ShowContact(&con);//显示联系人的信息
			break;
		case SORT:
			SortContact(&con);//按名字排序联系人
			break;
		case EXIT:
            printf("退出通讯录\n");
			break;
		default:
            printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);
}

可以把test函数在main函数里调用。

#include "Contact.h"
int main()
{
	test();
	return 0;
}

写完这些基本的功能之后,就可以开始实现这些编辑通讯录的函数了。上面这些都是放在test.c函数(实现通讯录的基本逻辑)。

Contact.h:函数的声明与头文件的包含

这些函数的声明和头文件的声明,我们都可以放在一个Contact.h的头文件中。 

头文件的声明:

//头文件的声明
#include 
#include 
#include 
#include 

联合体的创建: 

//把选项一 一列举出来
enum OPPION
{
	EXIT,
	ADD,
	DEL,
	SEARCH,
	MODIFY,
	SHOW,
	SORT
};

通讯录准备: 

//为后期增加联系人做准备
#define MAX 100
#define MAX_NAME 20
#define MAX_SEX 5
#define MAX_TELE 12
#define MAX_ADDR 30
//类型的声明
typedef struct people
{
	char name[MAX_NAME];//姓名
	int age;//年龄
	char sex[MAX_SEX];//性别
	//电话(我们正常的电话是11位,超过了int的范围,所以用字符串比较合适)
	char tele[MAX_TELE];
	char addr[MAX_ADDR];//住址
}people;
//通讯录的创建
typedef struct Contact
{
	people data[MAX];
	int sz;//记录当前通讯录的人数
}Contact;

函数的声明:

//初始化通讯录的函数的声明
void InitContact(Contact* pc);
//增加联系人信息的函数的声明
void AddContact(Contact* pc);
//显示联系人的信息的函数的声明
void ShowContact(const Contact* pc);
//删除联系人信息的函数的声明
void DelContact(Contact* pc);
//查找指定联系人信息的函数的声明
void SearchContact(const Contact* pc);
//修改指定联系人的信息
void ModifyContact(Contact* pc);
//排序联系人
void SortContact(const Contact* pc);

Contact.c:函数的实现 

初始化通讯录函数: 

#include "Contact.h"//声明头文件
//初始化通讯录的函数
void InitContact(Contact* pc)
{
	assert(pc);
    //把pc->data的空间全部初始化为0
	memset(pc->data, 0, sizeof(pc->data));//循环也可以,但是太麻烦。
	pc->sz = 0;
}

增加联系人信息的函数: 

//增加联系人信息的函数
void AddContact(Contact* pc)
{
	assert(pc);
    //首先得判断这个通讯录是否已经满了
	if (pc->sz == MAX)
	{
		printf("通讯录已满,无法增加\n");
		//通讯录满了,就无需执行下面的语句了
		return;//因为是void。所以无需返回任何值
	}
	else
	{
		printf("请输入姓名:");
		scanf("%s", pc->data[pc->sz].name);
		printf("请输入年龄:");
		scanf("%d", &(pc->data[pc->sz].age));//age是int类型
		printf("请输入性别:");
		scanf("%s", pc->data[pc->sz].sex);
		printf("请输入电话:");
		scanf("%s", pc->data[pc->sz].tele);
		printf("请输入住址:");
		scanf("%s", pc->data[pc->sz].addr);
		pc->sz++;
		printf("成功增加联系人\n");
	}
}

显示联系人的信息的函数: 

//显示联系人的信息的函数
void ShowContact(const Contact* pc)
{
	assert(pc);
    //要有一定的排版(采用左对齐)
	printf("%-20s\t%-4s\t%-5s\t%-12s\t%-30s\n", "姓名", "年龄", "性别", "电话", "地址");
	for (int i = 0; i sz; i++)
	{
		printf("%-20s\t%-4d\t%-5s\t%-12s\t%-30s\n",
			pc->data[i].name,
			pc->data[i].age,
			pc->data[i].sex,
			pc->data[i].tele,
			pc->data[i].addr);
	}
}

 删除联系人信息的函数的声明:

//删除联系人信息的函数的声明
void DelContact(Contact* pc)
{
	assert(pc);
    //判断是否能删除
	if (pc->sz == 0)
	{
		printf("通讯录为空,无法删除\n");
		return;
	}
    //创建一个name数组来存放我们要寻找的联系人
	char name[MAX_NAME] = { 0 };
	//知道要删除谁
	printf("请输入要删除的人的名字:");
	scanf("%s", name);
	//找到要删除的人(字符串)
	int del = 0;
	int flag = 0;
    //遍历数组,一个一个的比较字符串
	for (int i = 0; i sz; i++)
	{
		if (strcmp(pc->data[i].name, name) == 0)
		{
			del = i;//记录当前的位置
			flag = 1;
	    	break;
		}
	}
	//开始删除(删除的方法就是把后面一个往前覆盖)
	if (flag == 0)
	{
		printf("要删除的联系人不存在\n");
		return;//后面的代码无需执行
	}
	else
	{
	    for (int i = del; i sz - 1; i++)
		{
			pc->data[i] = pc->data[i + 1];
		}
	}
	//删除最后一个元素时,虽然不能在循环中删除,但是pc->sz--了,致使访问不到最后一个元素了
	//因此,我们在打印时也不会打印出来
	pc->sz--;
	printf("删除成功\n");
}

其实我们会发现在这里寻找名字的时候,在后面查找联系人也会用到,不如我们直接分装成一个寻找名字的函数。

//查找联系人名字的函数
//这个函数我们只想在内部使用,没必要暴露给别人
static int FindByName(const Contact* pc, char name[])
{
	assert(pc);
	for (int i = 0; i sz; i++)
	{
		if (strcmp(pc->data[i].name, name) == 0)
		{
			return i;
		}
	}
	return -1;
}

那么上面的删除函数就可以改造成:

//删除联系人信息的函数的声明
void DelContact(Contact* pc)
{
	assert(pc);
	if (pc->sz == 0)
	{
		printf("通讯录为空,无法删除\n");
		return;
	}
	char name[MAX_NAME] = { 0 };
	//知道要删除谁
	printf("请输入要删除的人的名字:");
	scanf("%s", name);
    //找到要删除的人(字符串)
	int del = FindByName(pc, name);
	if (del == -1)
	{
		printf("要删除的人不存在\n");
		return;
	}
	for (int i = del; i sz - 1; i++)
	{
		pc->data[i] = pc->data[i + 1];
	}
	//删除最后一个元素时,虽然不能在循环中删除,但是pc->sz--了,致使访问不到最后一个元素了
	//因此,我们在打印时也不会打印出来
	pc->sz--;
	printf("删除成功\n");
}

查找指定联系人信息的函数: 

//查找指定联系人信息的函数
void SearchContact(const Contact* pc)
{
	assert(pc);
    //存放名字的数组
	char name[MAX_NAME];
	printf("请输入要查找的联系人:");
	scanf("%s", name);
	int pos = FindByName(pc, name);
	if (pos == -1)
	{
		printf("要查找的人不存在\n");
		return;
	}
	printf("%-20s\t%-4s\t%-5s\t%-12s\t%-30s\n", "姓名", "年龄", "性别", "电话", "地址");
	printf("%-20s\t%-4d\t%-5s\t%-12s\t%-30s\n",
		pc->data[pos].name,
		pc->data[pos].age,
		pc->data[pos].sex,
		pc->data[pos].tele,
		pc->data[pos].addr);
}

修改指定联系人的信息: 

void menu1()
{
	printf("*******************************\n");
	printf("****  1.姓名     2.年龄    ****\n");
	printf("****  3.性别     4.电话    ****\n");
	printf("****        5.住址         ****\n");
	printf("*******************************\n");
}
//修改指定联系人的信息
void ModifyContact(Contact* pc)
{
	assert(pc);
    //存放名字的数组
	char name[MAX_NAME];
	printf("请输入要修改的联系人:");
	scanf("%s", name);
	int pos = FindByName(pc, name);
	if (pos == -1)
	{
		printf("要修改的联系人不存在\n");
		return;
	}
	menu1();
	printf("请选择要修改的选项:");
	int n = 0;
	scanf("%d", &n);
	switch (n)
	{
	case 1:printf("请输入姓名:");
		scanf("%s", pc->data[pos].name);
		break;
	case 2:printf("请输入年龄:");
		scanf("%d", &(pc->data[pos].age));
		break;
	case 3:printf("请输入性别:");
		scanf("%s", pc->data[pos].sex);
		break;
	case 4:printf("请输入电话:");
		scanf("%s", pc->data[pos].tele);
		break;
	case 5:printf("请输入住址:");
		scanf("%s", pc->data[pos].addr);
		break;
	}
	printf("修改成功\n");
    //看看是否修改成功
	printf("%-20s\t%-4s\t%-5s\t%-12s\t%-30s\n", "姓名", "年龄", "性别", "电话", "地址");
	printf("%-20s\t%-4d\t%-5s\t%-12s\t%-30s\n",
		pc->data[pos].name,
		pc->data[pos].age,
		pc->data[pos].sex,
		pc->data[pos].tele,
		pc->data[pos].addr);
}
int cmp_peo_by_name(const void* e1, const void* e2)
{
	return strcmp(((people*)e1)->name, ((people*)e2)->name);
}
int cmp_peo_by_age(const void* e1, const void* e2)
{
	return ((people*)e1)->age - ((people*)e2)->age;
}
//排序联系人
void SortContact(const Contact* pc)
{
	printf("请选择想要排的序:\n");
	printf("********************************\n");
	printf("*****   1.按照名字排序    ******\n");
	printf("*****   2.按照年龄排序    ******\n");
	printf("********************************\n");
	int n = 0;
	printf("请选择:");
	scanf("%d", &n);
	qsort(pc->data, pc->sz, sizeof(pc->data[0]), cmp_peo_by_name);
	qsort(pc->data, pc->sz, sizeof(pc->data[0]), cmp_peo_by_age);
    //看看是否排序成功
	for (int i = 0; i sz; i++)
	{
		printf("%-20s\t%-4d\t%-5s\t%-12s\t%-30s\n",
			pc->data[i].name,
			pc->data[i].age,
			pc->data[i].sex,
			pc->data[i].tele,
			pc->data[i].addr);
	}
	printf("排序成功\n");
}

通讯录源码: 

test.c:

#include "Contact.h"
void menu()
{
	printf("******************************************\n");
	printf("****  1.增加联系人      2.删除联系人  ****\n");
	printf("****  3.查找联系人      4.修改联系人  ****\n");
	printf("****  5.显示联系人     6.排序联系人   ****\n");
	printf("****           0.退出通讯录           ****\n");
	printf("******************************************\n");
}
void test()
{
	int input = 0;
	//下面是对通讯录进行的各种操作,所以我们得先有一个通讯录
	Contact con;
	InitContact(&con);
	do
	{
		menu();
		printf("请选择:");
		scanf("%d", &input);
		switch (input)
		{
		case ADD:
			AddContact(&con);//增加联系人的信息
			break;
		case DEL:
			DelContact(&con);//删除联系人的信息
			break;
		case SEARCH:
			SearchContact(&con);//查找联系人的信息
			break;
		case MODIFY:
			ModifyContact(&con);//修改联系人的信息
			break;
		case SHOW:
			ShowContact(&con);//显示联系人的信息
			break;
		case SORT:
			SortContact(&con);//按名字排序联系人
			break;
		case EXIT:
            printf("退出通讯录\n");
			break;
		default:
            printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);
}
int main()
{
	test();
	return 0;
}

Contact.c:

#include "Contact.h"
//初始化通讯录的函数
void InitContact(Contact* pc)
{
	assert(pc);
	memset(pc->data, 0, sizeof(pc->data));//循环也可以
	pc->sz = 0;
}
//增加联系人信息的函数
void AddContact(Contact* pc)
{
	assert(pc);
	if (pc->sz == MAX)
	{
		printf("通讯录已满,无法增加\n");
		//通讯录满了,就无需执行下面的语句了
		return;//因为是void。所以无需返回任何值
	}
	else
	{
		printf("请输入姓名:");
		scanf("%s", pc->data[pc->sz].name);
		printf("请输入年龄:");
		scanf("%d", &(pc->data[pc->sz].age));
		printf("请输入性别:");
		scanf("%s", pc->data[pc->sz].sex);
		printf("请输入电话:");
		scanf("%s", pc->data[pc->sz].tele);
		printf("请输入住址:");
		scanf("%s", pc->data[pc->sz].addr);
		pc->sz++;
		printf("成功增加联系人\n");
	}
}
//显示联系人的信息的函数
void ShowContact(const Contact* pc)
{
	assert(pc);
	printf("%-20s\t%-4s\t%-5s\t%-12s\t%-30s\n", "姓名", "年龄", "性别", "电话", "地址");
	for (int i = 0; i sz; i++)
	{
		printf("%-20s\t%-4d\t%-5s\t%-12s\t%-30s\n",
			pc->data[i].name,
			pc->data[i].age,
			pc->data[i].sex,
			pc->data[i].tele,
			pc->data[i].addr);
	}
}
//查找联系人名字的函数
static int FindByName(const Contact* pc, char name[])
{
	assert(pc);
	for (int i = 0; i sz; i++)
	{
		if (strcmp(pc->data[i].name, name) == 0)
		{
			return i;
		}
	}
	return -1;
}
//删除联系人信息的函数的声明
void DelContact(Contact* pc)
{
	assert(pc);
	if (pc->sz == 0)
	{
		printf("通讯录为空,无法删除\n");
		return;
	}
	char name[MAX_NAME] = { 0 };
	//知道要删除谁
	printf("请输入要删除的人的名字:");
	scanf("%s", name);
	//找到要删除的人(字符串)
	int del = FindByName(pc, name);
	if (del == -1)
	{
		printf("要删除的人不存在\n");
		return;
	}
	for (int i = del; i sz - 1; i++)
	{
		pc->data[i] = pc->data[i + 1];
	}
	//删除最后一个元素时,虽然不能在循环中删除,但是pc->sz--了,致使访问不到最后一个元素了
	//因此,我们在打印时也不会打印出来
	pc->sz--;
	printf("删除成功\n");
}
//查找指定联系人信息的函数
void SearchContact(const Contact* pc)
{
	assert(pc);
	char name[MAX_NAME];
	printf("请输入要查找的联系人:");
	scanf("%s", name);
	int pos = FindByName(pc, name);
	if (pos == -1)
	{
		printf("要查找的人不存在\n");
		return;
	}
	printf("%-20s\t%-4s\t%-5s\t%-12s\t%-30s\n", "姓名", "年龄", "性别", "电话", "地址");
	printf("%-20s\t%-4d\t%-5s\t%-12s\t%-30s\n",
		pc->data[pos].name,
		pc->data[pos].age,
		pc->data[pos].sex,
		pc->data[pos].tele,
		pc->data[pos].addr);
}
void menu1()
{
	printf("*******************************\n");
	printf("****  1.姓名     2.年龄    ****\n");
	printf("****  3.性别     4.电话    ****\n");
	printf("****        5.住址         ****\n");
	printf("*******************************\n");
}
//修改指定联系人的信息
void ModifyContact(Contact* pc)
{
	assert(pc);
	char name[MAX_NAME];
	printf("请输入要修改的联系人:");
	scanf("%s", name);
	int pos = FindByName(pc, name);
	if (pos == -1)
	{
		printf("要修改的联系人不存在\n");
		return;
	}
	menu1();
	printf("请选择要修改的选项:");
	int n = 0;
	scanf("%d", &n);
	switch (n)
	{
	case 1:printf("请输入姓名:");
		scanf("%s", pc->data[pos].name);
		break;
	case 2:printf("请输入年龄:");
		scanf("%d", &(pc->data[pos].age));
		break;
	case 3:printf("请输入性别:");
		scanf("%s", pc->data[pos].sex);
		break;
	case 4:printf("请输入电话:");
		scanf("%s", pc->data[pos].tele);
		break;
	case 5:printf("请输入住址:");
		scanf("%s", pc->data[pos].addr);
		break;
	}
	printf("修改成功\n");
	printf("%-20s\t%-4s\t%-5s\t%-12s\t%-30s\n", "姓名", "年龄", "性别", "电话", "地址");
	printf("%-20s\t%-4d\t%-5s\t%-12s\t%-30s\n",
		pc->data[pos].name,
		pc->data[pos].age,
		pc->data[pos].sex,
		pc->data[pos].tele,
		pc->data[pos].addr);
}
int cmp_peo_by_name(const void* e1, const void* e2)
{
	return strcmp(((people*)e1)->name, ((people*)e2)->name);
}
int cmp_peo_by_age(const void* e1, const void* e2)
{
	return ((people*)e1)->age - ((people*)e2)->age;
}
void SortContact(const Contact* pc)
{
	printf("请选择想要排的序:\n");
	printf("********************************\n");
	printf("*****   1.按照名字排序    ******\n");
	printf("*****   2.按照年龄排序    ******\n");
	printf("********************************\n");
	int n = 0;
	printf("请选择:");
	scanf("%d", &n);
	qsort(pc->data, pc->sz, sizeof(pc->data[0]), cmp_peo_by_name);
	qsort(pc->data, pc->sz, sizeof(pc->data[0]), cmp_peo_by_age);
	for (int i = 0; i sz; i++)
	{
		printf("%-20s\t%-4d\t%-5s\t%-12s\t%-30s\n",
			pc->data[i].name,
			pc->data[i].age,
			pc->data[i].sex,
			pc->data[i].tele,
			pc->data[i].addr);
	}
	printf("排序成功\n");
}

Contect.h:

//头文件的声明
#include 
#include 
#include 
#include 
//为后期增加联系人做准备
#define MAX 100
#define MAX_NAME 20
#define MAX_SEX 5
#define MAX_TELE 12
#define MAX_ADDR 30
//把选项一 一列举出来
enum OPPION
{
	EXIT,
	ADD,
	DEL,
	SEARCH,
	MODIFY,
	SHOW,
	SORT
};
//类型的声明
typedef struct people
{
	char name[MAX_NAME];//姓名
	int age;//年龄
	char sex[MAX_SEX];//性别
	//电话(我们正常的电话是11位,超过了int的长度,所以用字符串比较合适)
	char tele[MAX_TELE];
	char addr[MAX_ADDR];//住址
}people;
//通讯录
typedef struct Contact
{
	people data[MAX];
	int sz;
}Contact;
//初始化通讯录的函数的声明
void InitContact(Contact* pc);
//增加联系人信息的函数的声明
void AddContact(Contact* pc);
//显示联系人的信息的函数的声明
void ShowContact(const Contact* pc);
//删除联系人信息的函数的声明
void DelContact(Contact* pc);
//查找指定联系人信息的函数的声明
void SearchContact(const Contact* pc);
//修改指定联系人的信息
void ModifyContact(Contact* pc);
//按名字排序联系人
void SortContact(const Contact* pc);

动态版通讯录 

有关动态内存开辟的知识点:动态内存管理-CSDN博客

上述是静态版的通讯录。

下面我们来实现一下动态版的通讯录。

目标:1. 可以实现储存通讯录人数不限。2. 默认可以放3个人的信息,如果不够,就每次增加2个人的信息(这样方便我们测试,也可以默认别的数,但数据太大,不好测试)。

首先,存放人信息的空间大小是不需要改变的,但是我们用的那个联系人数组要变成一个由malloc函数开辟的空间。其次,我们想要知道这个空间当前用了几个,还剩几个。记录用了几个,就可以用sz来记录,而还剩几个空间,就可以用count来记录一下。

//通讯录
typedef struct Contact
{
	people* data;//指向通讯录的那块空间
	int sz;//记录当前的联系人个数
	int count;//记录总共开辟了多少空间
}Contact;

既然通讯录里的数据变了,那么我们的初始化函数也得变化。

//初始化通讯录的函数
void InitContact(Contact* pc)
{
    assert(pc);
	//开辟一块空间给通讯录
	pc->data = (people*)malloc(3 * sizeof(people));
	if (pc->data == NULL)//空间开辟失败
	{
		perror("InitContact");
		return;
	}
	pc->sz = 0;
	pc->count = DEFAULT_SZ;//在头文件中定义一个宏,默认数据表示初始数据
}

容量变了,那我们增加联系人的函数也得发生变化。 

static int CheckCount(Contact* pc)//判断是否需要增加空间
{
	if (pc->sz == pc->count)
	{
		people *ptr = (people*)realloc(pc->data, (pc->count + INC_SZ) * sizeof(people));
		if (ptr == NULL)
		{
			perror("CheckCount");
			return 0;
		}
		pc->data = ptr;
		pc->count += INC_SZ;
        printf("增容成功\n");
		return 1;
	}
	return 1;
}
//增加联系人信息的函数
void AddContact(Contact* pc)
{
	assert(pc);
	if (CheckCount(&pc) == 0)//增加失败就不需要往下走了
	{
		return;
	}
	else
	{
		printf("请输入姓名:");
		scanf("%s", pc->data[pc->sz].name);
		printf("请输入年龄:");
		scanf("%d", &(pc->data[pc->sz].age));
		printf("请输入性别:");
		scanf("%s", pc->data[pc->sz].sex);
		printf("请输入电话:");
		scanf("%s", pc->data[pc->sz].tele);
		printf("请输入住址:");
		scanf("%s", pc->data[pc->sz].addr);
		pc->sz++;
		printf("成功增加联系人\n");
	}
}

删除,查找,显示,修改,排序这些就不需要更改了。

但我们还得在退出通讯录的时候,把动态开辟的内存给释放掉。 

void DestroyContact(Contact* pc)
{
	free(pc->data);
	pc->data = NULL;
	pc->count = 0;
	pc->sz = 0;
}

test.c:

#include "Contact.h"
void menu()
{
	printf("******************************************\n");
	printf("****  1.增加联系人      2.删除联系人  ****\n");
	printf("****  3.查找联系人      4.修改联系人  ****\n");
	printf("****  5.显示联系人     6.排序联系人   ****\n");
	printf("****           0.退出通讯录           ****\n");
	printf("******************************************\n");
}
void test()
{
	int input = 0;
	//下面是对通讯录进行的各种操作,所以我们得先有一个通讯录
	Contact con;
	InitContact(&con);
	do
	{
		menu();
		printf("请选择:");
		scanf("%d", &input);
		switch (input)
		{
		case ADD:
			AddContact(&con);//增加联系人的信息
			break;
		case DEL:
			DelContact(&con);//删除联系人的信息
			break;
		case SEARCH:
			SearchContact(&con);//查找联系人的信息
			break;
		case MODIFY:
			ModifyContact(&con);//修改联系人的信息
			break;
		case SHOW:
			ShowContact(&con);//显示联系人的信息
			break;
		case SORT:
			SortContact(&con);//按名字排序联系人
			break;
		case EXIT:
			DestroyContact(&con);//销毁通讯录
			printf("退出通讯录\n");
			break;
		default:printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);
}
int main()
{
	test();
	return 0;
}

Contact.h:

//头文件的声明
#include 
#include 
#include 
#include 
//为后期增加联系人做准备
#define MAX 100
#define MAX_NAME 20
#define MAX_SEX 5
#define MAX_TELE 12
#define MAX_ADDR 30
#define DEFAULT_SZ 3
#define INC_SZ 2
//把选项一 一列举出来
enum OPPION
{
	EXIT,
	ADD,
	DEL,
	SEARCH,
	MODIFY,
	SHOW,
	SORT
};
//类型的声明
typedef struct people
{
	char name[MAX_NAME];//姓名
	int age;//年龄
	char sex[MAX_SEX];//性别
	//电话(我们正常的电话是11位,超过了int的长度,所以用字符串比较合适)
	char tele[MAX_TELE];
	char addr[MAX_ADDR];//住址
}people;
//通讯录
typedef struct Contact
{
	people* data;
	int sz;
	int count;
}Contact;
//初始化通讯录的函数的声明
void InitContact(Contact* pc);
//增加联系人信息的函数的声明
void AddContact(Contact* pc);
//显示联系人的信息的函数的声明
void ShowContact(const Contact* pc);
//删除联系人信息的函数的声明
void DelContact(Contact* pc);
//查找指定联系人信息的函数的声明
void SearchContact(const Contact* pc);
//修改指定联系人的信息
void ModifyContact(Contact* pc);
//按名字排序联系人
void SortContact(const Contact* pc);
//销毁通讯录的函数
void DestroyContact(Contact* pc);

Contact.c: 

#include "Contact.h"
//初始化通讯录的函数
void InitContact(Contact* pc)
{
	assert(pc);
	//开辟一块空间给通讯录
	pc->data = (people*)malloc(3 * sizeof(people));
	if (pc->data == NULL)//空间开辟失败
	{
		perror("InitContact");
		return;
	}
	pc->sz = 0;
	pc->count = DEFAULT_SZ;
}
static int CheckCount(Contact* pc)//判断是否需要增加空间
{
	if (pc->sz == pc->count)
	{
		people *ptr = (people*)realloc(pc->data, (pc->count + INC_SZ) * sizeof(people));
		if (ptr == NULL)
		{
			perror("CheckCount");
			return 0;
		}
		pc->data = ptr;
		pc->count += INC_SZ;
		printf("增容成功\n");
		return 1;
	}
	return 1;
}
//增加联系人信息的函数
void AddContact(Contact* pc)
{
	assert(pc);
	if (CheckCount(pc) == 0)//增加失败就不需要往下走了
	{
		return;
	}
	else
	{
		printf("请输入姓名:");
		scanf("%s", pc->data[pc->sz].name);
		printf("请输入年龄:");
		scanf("%d", &(pc->data[pc->sz].age));
		printf("请输入性别:");
		scanf("%s", pc->data[pc->sz].sex);
		printf("请输入电话:");
		scanf("%s", pc->data[pc->sz].tele);
		printf("请输入住址:");
		scanf("%s", pc->data[pc->sz].addr);
		pc->sz++;
		printf("成功增加联系人\n");
	}
}
//显示联系人的信息的函数
void ShowContact(const Contact* pc)
{
	assert(pc);
	printf("%-20s\t%-4s\t%-5s\t%-12s\t%-30s\n", "姓名", "年龄", "性别", "电话", "地址");
	for (int i = 0; i sz; i++)
	{
		printf("%-20s\t%-4d\t%-5s\t%-12s\t%-30s\n",
			pc->data[i].name,
			pc->data[i].age,
			pc->data[i].sex,
			pc->data[i].tele,
			pc->data[i].addr);
	}
}
//查找联系人名字的函数
static int FindByName(const Contact* pc, char name[])
{
	assert(pc);
	for (int i = 0; i sz; i++)
	{
		if (strcmp(pc->data[i].name, name) == 0)
		{
			return i;
		}
	}
	return -1;
}
//删除联系人信息的函数的声明
void DelContact(Contact* pc)
{
	assert(pc);
	if (pc->sz == 0)
	{
		printf("通讯录为空,无法删除\n");
		return;
	}
	char name[MAX_NAME] = { 0 };
	//知道要删除谁
	printf("请输入要删除的人的名字:");
	scanf("%s", name);	
	//找到要删除的人(字符串)
	int del = FindByName(pc, name);
	if (del == -1)
	{
		printf("要删除的人不存在\n");
		return;
	}
	for (int i = del; i sz - 1; i++)
	{
		pc->data[i] = pc->data[i + 1];
	}
	//删除最后一个元素时,虽然不能在循环中删除,但是pc->sz--了,致使访问不到最后一个元素了
	//因此,我们在打印时也不会打印出来
	pc->sz--;
	printf("删除成功\n");
}
//查找指定联系人信息的函数
void SearchContact(const Contact* pc)
{
	assert(pc);
	char name[MAX_NAME];
	printf("请输入要查找的联系人:");
	scanf("%s", name);
	int pos = FindByName(pc, name);
	if (pos == -1)
	{
		printf("要查找的人不存在\n");
		return;
	}
	printf("%-20s\t%-4s\t%-5s\t%-12s\t%-30s\n", "姓名", "年龄", "性别", "电话", "地址");
	printf("%-20s\t%-4d\t%-5s\t%-12s\t%-30s\n",
		pc->data[pos].name,
		pc->data[pos].age,
		pc->data[pos].sex,
		pc->data[pos].tele,
		pc->data[pos].addr);
}
void menu1()
{
	printf("*******************************\n");
	printf("****  1.姓名     2.年龄    ****\n");
	printf("****  3.性别     4.电话    ****\n");
	printf("****        5.住址         ****\n");
	printf("*******************************\n");
}
//修改指定联系人的信息
void ModifyContact(Contact* pc)
{
	assert(pc);
	char name[MAX_NAME];
	printf("请输入要修改的联系人:");
	scanf("%s", name);
	int pos = FindByName(pc, name);
	if (pos == -1)
	{
		printf("要修改的联系人不存在\n");
		return;
	}
	menu1();
	printf("请选择要修改的选项:");
	int n = 0;
	scanf("%d", &n);
	switch (n)
	{
	case 1:printf("请输入姓名:");
		scanf("%s", pc->data[pos].name);
		break;
	case 2:printf("请输入年龄:");
		scanf("%d", &(pc->data[pos].age));
		break;
	case 3:printf("请输入性别:");
		scanf("%s", pc->data[pos].sex);
		break;
	case 4:printf("请输入电话:");
		scanf("%s", pc->data[pos].tele);
		break;
	case 5:printf("请输入住址:");
		scanf("%s", pc->data[pos].addr);
		break;
	}
	printf("修改成功\n");
	printf("%-20s\t%-4s\t%-5s\t%-12s\t%-30s\n", "姓名", "年龄", "性别", "电话", "地址");
	printf("%-20s\t%-4d\t%-5s\t%-12s\t%-30s\n",
		pc->data[pos].name,
		pc->data[pos].age,
		pc->data[pos].sex,
		pc->data[pos].tele,
		pc->data[pos].addr);
}
int cmp_peo_by_name(const void* e1, const void* e2)
{
	return strcmp(((people*)e1)->name, ((people*)e2)->name);
}
int cmp_peo_by_age(const void* e1, const void* e2)
{
	return ((people*)e1)->age - ((people*)e2)->age;
}
void SortContact(const Contact* pc)
{
	printf("请选择想要排的序:\n");
	printf("********************************\n");
	printf("*****   1.按照名字排序    ******\n");
	printf("*****   2.按照年龄排序    ******\n");
	printf("********************************\n");
	int n = 0;
	printf("请选择:");
	scanf("%d", &n);
	qsort(pc->data, pc->sz, sizeof(pc->data[0]), cmp_peo_by_name);
	qsort(pc->data, pc->sz, sizeof(pc->data[0]), cmp_peo_by_age);
	for (int i = 0; i sz; i++)
	{
		printf("%-20s\t%-4d\t%-5s\t%-12s\t%-30s\n",
			pc->data[i].name,
			pc->data[i].age,
			pc->data[i].sex,
			pc->data[i].tele,
			pc->data[i].addr);
	}
	printf("排序成功\n");
}
void DestroyContact(Contact* pc)
{
	free(pc->data);
	pc->data = NULL;
	pc->count = 0;
	pc->sz = 0;
}

动态通讯录(文件版)

 有关文件的读写的知识点:C语言之文件操作(万字详解)-CSDN博客

上面是动态版的通讯录,可以实现空间不受限制。但是如果我们想要知道我们上一次是存放什么呢?那就得用文件来保存上一次的信息。我们应该是在销毁前保存。

void SaveContact(Contact* pc)
{
	//打开文件
	FILE* pf = fopen("data.txt", "wb");
	if (pf == NULL)
	{
		perror("SaveContact");
		return;
	}
	//写数据
	for (int i = 0; i sz; i++)
	{
		fwrite(pc->data + i, sizeof(people), 1, pf);
	}
	//关闭文件
	fclose(pf);
	pf = NULL;
}

我们保存的信息是为了下一次打开的时候,可以直接查看到这些数据。那么我们在初始化通讯录的时候,就应该把数据放到通讯录里,如果我们要查看的话,就可以选择显示通讯录的信息即可。

void LoadContact(Contact* pc)
{
	//把文件中的信息加载到通讯录中
	FILE* pf = fopen("data.txt", "rb");
	if (pf == NULL)
	{
		perror("LoadContact");
		return;
	}
	//读文件
	people tmp = {0};
	while (fread(&tmp, sizeof(people), 1, pf))//读一个判断一个,当返回为0时,就说明已经读完了
	{
		if (CheckCount(pc) == 0)//存放的时候,判断是否需要增加空间
		{
			return;
		}
		pc->data[pc->sz] = tmp;
		pc->sz++;
	}
	//关闭文件
	fclose(pf);
	pf = NULL;
}
//初始化通讯录的函数
void InitContact(Contact* pc)
{
	assert(pc);
	//开辟一块空间给通讯录
	pc->data = (people*)malloc(3 * sizeof(people));
	if (pc->data == NULL)//空间开辟失败
	{
		perror("InitContact");
		return;
	}
	pc->sz = 0;
	pc->count = DEFAULT_SZ;
	//把文件中的信息加载到通讯录中
	LoadContact(pc);
}

test.c: 

#include "Contact.h"
void menu()
{
	printf("******************************************\n");
	printf("****  1.增加联系人      2.删除联系人  ****\n");
	printf("****  3.查找联系人      4.修改联系人  ****\n");
	printf("****  5.显示联系人     6.排序联系人   ****\n");
	printf("****           0.退出通讯录           ****\n");
	printf("******************************************\n");
}
void test()
{
	int input = 0;
	//下面是对通讯录进行的各种操作,所以我们得先有一个通讯录
	Contact con;
	InitContact(&con);
	do
	{
		menu();
		printf("请选择:");
		scanf("%d", &input);
		switch (input)
		{
		case ADD:
			AddContact(&con);//增加联系人的信息
			break;
		case DEL:
			DelContact(&con);//删除联系人的信息
			break;
		case SEARCH:
			SearchContact(&con);//查找联系人的信息
			break;
		case MODIFY:
			ModifyContact(&con);//修改联系人的信息
			break;
		case SHOW:
			ShowContact(&con);//显示联系人的信息
			break;
		case SORT:
			SortContact(&con);//按名字排序联系人
			break;
		case EXIT:
			SaveContact(&con);//把信息保存在文件里
			DestroyContact(&con);//销毁通讯录
			printf("退出通讯录\n");
			break;
		default:printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);
}
int main()
{
	test();
	return 0;
}

Contact.h: 

//头文件的声明
#include 
#include 
#include 
#include 
//为后期增加联系人做准备
#define MAX 100
#define MAX_NAME 20
#define MAX_SEX 5
#define MAX_TELE 12
#define MAX_ADDR 30
#define DEFAULT_SZ 3
#define INC_SZ 2
//把选项一 一列举出来
enum OPPION
{
	EXIT,
	ADD,
	DEL,
	SEARCH,
	MODIFY,
	SHOW,
	SORT
};
//类型的声明
typedef struct people
{
	char name[MAX_NAME];//姓名
	int age;//年龄
	char sex[MAX_SEX];//性别
	//电话(我们正常的电话是11位,超过了int的长度,所以用字符串比较合适)
	char tele[MAX_TELE];
	char addr[MAX_ADDR];//住址
}people;
//通讯录
typedef struct Contact
{
	people* data;
	int sz;
	int count;
}Contact;
//初始化通讯录的函数的声明
void InitContact(Contact* pc);
//增加联系人信息的函数的声明
void AddContact(Contact* pc);
//显示联系人的信息的函数的声明
void ShowContact(const Contact* pc);
//删除联系人信息的函数的声明
void DelContact(Contact* pc);
//查找指定联系人信息的函数的声明
void SearchContact(const Contact* pc);
//修改指定联系人的信息
void ModifyContact(Contact* pc);
//按名字排序联系人
void SortContact(const Contact* pc);
//销毁通讯录的函数
void DestroyContact(Contact* pc);
//加载通讯录的函数
void SaveContact(Contact* pc);

Contact.c: 

#include "Contact.h"
int CheckCount(Contact* pc);
void LoadContact(Contact* pc)
{
	//把文件中的信息加载到通讯录中
	FILE* pf = fopen("data.txt", "rb");
	if (pf == NULL)
	{
		perror("LoadContact");
		return;
	}
	//读文件
	people tmp = {0};
	while (fread(&tmp, sizeof(people), 1, pf))
	{
		if (CheckCount(pc) == 0)
		{
			return;
		}
		pc->data[pc->sz] = tmp;
		pc->sz++;
	}
	//关闭文件
	fclose(pf);
	pf = NULL;
}
//初始化通讯录的函数
void InitContact(Contact* pc)
{
	assert(pc);
	//开辟一块空间给通讯录
	pc->data = (people*)malloc(3 * sizeof(people));
	if (pc->data == NULL)//空间开辟失败
	{
		perror("InitContact");
		return;
	}
	pc->sz = 0;
	pc->count = DEFAULT_SZ;
	//把文件中的信息加载到通讯录中
	LoadContact(pc);
}
static int CheckCount(Contact* pc)//判断是否需要增加空间
{
	if (pc->sz == pc->count)
	{
		people *ptr = (people*)realloc(pc->data, (pc->count + INC_SZ) * sizeof(people));
		if (ptr == NULL)
		{
			perror("CheckCount");
			return 0;
		}
		pc->data = ptr;
		pc->count += INC_SZ;
		printf("增容成功\n");
		return 1;
	}
	return 1;
}
//增加联系人信息的函数
void AddContact(Contact* pc)
{
	assert(pc);
	if (CheckCount(pc) == 0)//增加失败就不需要往下走了
	{
		return;
	}
	else
	{
		printf("请输入姓名:");
		scanf("%s", pc->data[pc->sz].name);
		printf("请输入年龄:");
		scanf("%d", &(pc->data[pc->sz].age));
		printf("请输入性别:");
		scanf("%s", pc->data[pc->sz].sex);
		printf("请输入电话:");
		scanf("%s", pc->data[pc->sz].tele);
		printf("请输入住址:");
		scanf("%s", pc->data[pc->sz].addr);
		pc->sz++;
		printf("成功增加联系人\n");
	}
}
//显示联系人的信息的函数
void ShowContact(const Contact* pc)
{
	assert(pc);
	printf("%-20s\t%-4s\t%-5s\t%-12s\t%-30s\n", "姓名", "年龄", "性别", "电话", "地址");
	for (int i = 0; i sz; i++)
	{
		printf("%-20s\t%-4d\t%-5s\t%-12s\t%-30s\n",
			pc->data[i].name,
			pc->data[i].age,
			pc->data[i].sex,
			pc->data[i].tele,
			pc->data[i].addr);
	}
}
//查找联系人名字的函数
static int FindByName(const Contact* pc, char name[])
{
	assert(pc);
	for (int i = 0; i sz; i++)
	{
		if (strcmp(pc->data[i].name, name) == 0)
		{
			return i;
		}
	}
	return -1;
}
//删除联系人信息的函数的声明
void DelContact(Contact* pc)
{
	assert(pc);
	if (pc->sz == 0)
	{
		printf("通讯录为空,无法删除\n");
		return;
	}
	char name[MAX_NAME] = { 0 };
	//知道要删除谁
	printf("请输入要删除的人的名字:");
	scanf("%s", name);
	//找到要删除的人(字符串)
	int del = FindByName(pc, name);
	if (del == -1)
	{
		printf("要删除的人不存在\n");
		return;
	}
	for (int i = del; i sz - 1; i++)
	{
		pc->data[i] = pc->data[i + 1];
	}
	//删除最后一个元素时,虽然不能在循环中删除,但是pc->sz--了,致使访问不到最后一个元素了
	//因此,我们在打印时也不会打印出来
	pc->sz--;
	printf("删除成功\n");
}
//查找指定联系人信息的函数
void SearchContact(const Contact* pc)
{
	assert(pc);
	char name[MAX_NAME];
	printf("请输入要查找的联系人:");
	scanf("%s", name);
	int pos = FindByName(pc, name);
	if (pos == -1)
	{
		printf("要查找的人不存在\n");
		return;
	}
	printf("%-20s\t%-4s\t%-5s\t%-12s\t%-30s\n", "姓名", "年龄", "性别", "电话", "地址");
	printf("%-20s\t%-4d\t%-5s\t%-12s\t%-30s\n",
		pc->data[pos].name,
		pc->data[pos].age,
		pc->data[pos].sex,
		pc->data[pos].tele,
		pc->data[pos].addr);
}
void menu1()
{
	printf("*******************************\n");
	printf("****  1.姓名     2.年龄    ****\n");
	printf("****  3.性别     4.电话    ****\n");
	printf("****        5.住址         ****\n");
	printf("*******************************\n");
}
//修改指定联系人的信息
void ModifyContact(Contact* pc)
{
	assert(pc);
	char name[MAX_NAME];
	printf("请输入要修改的联系人:");
	scanf("%s", name);
	int pos = FindByName(pc, name);
	if (pos == -1)
	{
		printf("要修改的联系人不存在\n");
		return;
	}
	menu1();
	printf("请选择要修改的选项:");
	int n = 0;
	scanf("%d", &n);
	switch (n)
	{
	case 1:printf("请输入姓名:");
		scanf("%s", pc->data[pos].name);
		break;
	case 2:printf("请输入年龄:");
		scanf("%d", &(pc->data[pos].age));
		break;
	case 3:printf("请输入性别:");
		scanf("%s", pc->data[pos].sex);
		break;
	case 4:printf("请输入电话:");
		scanf("%s", pc->data[pos].tele);
		break;
	case 5:printf("请输入住址:");
		scanf("%s", pc->data[pos].addr);
		break;
	}
	printf("修改成功\n");
	printf("%-20s\t%-4s\t%-5s\t%-12s\t%-30s\n", "姓名", "年龄", "性别", "电话", "地址");
	printf("%-20s\t%-4d\t%-5s\t%-12s\t%-30s\n",
		pc->data[pos].name,
		pc->data[pos].age,
		pc->data[pos].sex,
		pc->data[pos].tele,
		pc->data[pos].addr);
}
int cmp_peo_by_name(const void* e1, const void* e2)
{
	return strcmp(((people*)e1)->name, ((people*)e2)->name);
}
int cmp_peo_by_age(const void* e1, const void* e2)
{
	return ((people*)e1)->age - ((people*)e2)->age;
}
void SortContact(const Contact* pc)
{
	printf("请选择想要排的序:\n");
	printf("********************************\n");
	printf("*****   1.按照名字排序    ******\n");
	printf("*****   2.按照年龄排序    ******\n");
	printf("********************************\n");
	int n = 0;
	printf("请选择:");
	scanf("%d", &n);
	qsort(pc->data, pc->sz, sizeof(pc->data[0]), cmp_peo_by_name);
	qsort(pc->data, pc->sz, sizeof(pc->data[0]), cmp_peo_by_age);
	for (int i = 0; i sz; i++)
	{
		printf("%-20s\t%-4d\t%-5s\t%-12s\t%-30s\n",
			pc->data[i].name,
			pc->data[i].age,
			pc->data[i].sex,
			pc->data[i].tele,
			pc->data[i].addr);
	}
	printf("排序成功\n");
}
void DestroyContact(Contact* pc)
{
	free(pc->data);
	pc->data = NULL;
	pc->count = 0;
	pc->sz = 0;
}
void SaveContact(Contact* pc)
{
	//打开文件
	FILE* pf = fopen("data.txt", "wb");
	if (pf == NULL)
	{
		perror("SaveContact");
		return;
	}
	//写数据
	for (int i = 0; i sz; i++)
	{
		fwrite(pc->data + i, sizeof(people), 1, pf);
	}
	//关闭文件
	fclose(pf);
	pf = NULL;
}

好啦!本期的通讯录的实现就到此结束啦!下一期,我们再一起学习吧! 


0
收藏0
文章版权声明:除非注明,否则均为VPS857原创文章,转载或复制请以超链接形式并注明出处。

相关阅读

  • 【研发日记】Matlab/Simulink自动生成代码(二)——五种选择结构实现方法,Matlab/Simulink自动生成代码的五种选择结构实现方法(二),Matlab/Simulink自动生成代码的五种选择结构实现方法详解(二)
  • 超级好用的C++实用库之跨平台实用方法,跨平台实用方法的C++实用库超好用指南,C++跨平台实用库使用指南,超好用实用方法集合,C++跨平台实用库超好用指南,方法与技巧集合
  • 【动态规划】斐波那契数列模型(C++),斐波那契数列模型(C++实现与动态规划解析),斐波那契数列模型解析与C++实现(动态规划)
  • 【C++】,string类底层的模拟实现,C++中string类的模拟底层实现探究
  • uniapp 小程序实现微信授权登录(前端和后端),Uniapp小程序实现微信授权登录全流程(前端后端全攻略),Uniapp小程序微信授权登录全流程攻略,前端后端全指南
  • Vue脚手架的安装(保姆级教程),Vue脚手架保姆级安装教程,Vue脚手架保姆级安装指南,Vue脚手架保姆级安装指南,从零开始教你如何安装Vue脚手架
  • 如何在树莓派 Raspberry Pi中本地部署一个web站点并实现无公网IP远程访问,树莓派上本地部署Web站点及无公网IP远程访问指南,树莓派部署Web站点及无公网IP远程访问指南,本地部署与远程访问实践,树莓派部署Web站点及无公网IP远程访问实践指南,树莓派部署Web站点及无公网IP远程访问实践指南,本地部署与远程访问详解,树莓派部署Web站点及无公网IP远程访问实践详解,本地部署与远程访问指南,树莓派部署Web站点及无公网IP远程访问实践详解,本地部署与远程访问指南。
  • vue2技术栈实现AI问答机器人功能(流式与非流式两种接口方法),Vue2技术栈实现AI问答机器人功能,流式与非流式接口方法探究,Vue2技术栈实现AI问答机器人功能,流式与非流式接口方法详解
  • 发表评论

    快捷回复:表情:
    评论列表 (暂无评论,0人围观)

    还没有评论,来说两句吧...

    目录[+]

    取消
    微信二维码
    微信二维码
    支付宝二维码