C语言程序设计:可存储的通讯录

  • Post author:
  • Post category:其他


前言:

本章没有使用链表!


C语言从入门到精通(鹏哥带你C语言从入门到精通,谭浩强C语言教程C语言程序设计C语言修仙C语言考研计算机二级专升本C语言期末突软考二级C语言考研C语言C语言)_哔哩哔哩_bilibili

本文很长,看不懂可以去B站看鹏哥的,讲的非常好,但细节需要自己完善,加油💪💪

通讯录的每个联系人是由


名字、性别、年龄、电话、住址

等等

再由每个联系人组成一张列表完成通讯录


注:文中都是拆分的,文尾才是源代码!!

一、基本框架

完成框架:

因为要多次操作,所以需要循环控制操作方法

通讯录的操作方法有很多(

增删查改最基础

)

1.增加 2.删除 3.查找 4.修改 5.排序 6.打印

所以在头文件中用枚举常量很合适,因为枚举常量可以对应输入数值

然后就是主函数的框架部分

void menu()
{
	//菜单
	printf("*****************************\n");
	printf("****      通讯录v1.0    ****\n");
	printf("****   1.增加   2.删除   ****\n");
	printf("****   3.查找   4.修改   ****\n");
	printf("****   5.排序   6.打印   ****\n");
	printf("****        0.退出       ****\n");
	printf("*****************************\n");
}

int main()
{
	int input = 0;
	do
	{
		menu();					//菜单
		printf("请输入:>");
		scanf("%d", &input);
        getchar();				//吸收换行(\n)
		switch (input)
		{
		case Add:				//增加
			printf("增加\n");
			break;
		case Del:				//删除
			printf("删除\n");
			break;
		case Find:				//...
			printf("查找\n");
			break;
		case Change:
			printf("修改\n");
			break;
		case Sort:
			printf("排序\n");
			break;
		case Print:
			printf("打印\n");
			break;
		case Exit:
			printf("退出\n");
			break;
		default:
			printf("输入错误\n");
			break;
		}
	} while (input);
}

完成主要框架后,可以进行封装函数了

首先建立联系人的结构体,方便寻找成员,然后枚举几个最大值方便以后维护

然后建立

通讯录

结构体来存储

联系人



联系人个数

因为我不知道你有多少个联系人,所以采用动态开辟内存

来构造通讯录所占空间,因此通讯录中还需要

存储一个变量

来确定当前通讯录的容量以便于

扩容

(1) 封装第一个函数:初始化通讯录(记得在头文件声明)

因为要动态开辟内存,所以首先需要

初始化通讯录函数

来进行开辟空间

记得引用 <string.h>头文件

然后就可以声明通讯录并且初始化了。。

(2)封装增加联系人函数

因为是动态开辟内存,所以可能会存满需要扩容的情况,所以

先把编写

扩容函数

,用于判断是否需要扩容的情况,再考虑增加

联系人。realloc 函数可以重新开辟一块空间并且可以将原空间的值

拷贝到新开辟的空间,然后释放原空间,所以重新开辟空间用这个很合适。

接着封装增加联系人,增加一个继续增加的判断条件,这一部分整体还是很简单的。


(3)封装打印函数

有了增加方法,那就把打印函数写出来,就可以进行查看是否写入成功

尝试输入一个联系人

尝试输入四个联系人

看来,开辟空间成功了但是这个通讯录的格式很丑,全是右对齐的,

所以我全给搞成左对齐了(其实就是把”%20s”改成”%-20s”)

(4)封装删除联系人函数

删除联系人,首先你

得有联系人

,所以需要判断通讯录是否还有联系人

然后你还得

找到你想删掉的联系人

,然后将他删掉。

我加了一个判断防止不小心按到的情况。

因为需要找到联系人,但是后边修改、查找都需要寻找联系人

所以封装一个寻找联系人的函数,返回值为该联系人的下标

接着封装删除联系人函数

//删除联系人
void DeleteData(list* pc)
{
	if (pc->sz == 0)						//当通讯录没有任何联系人时,直接返回
	{
		printf("还没有联系人呢!\n");
		return;
	}
	
	char check = 0;							//判断是否误操作
	printf("确定要进行删除操作吗(N取消):>");
	scanf("%c", &check);
	CheckCapcity(pc);
	if (check != 'n' && check != 'N')
	{
		char name[MAX_NAME] = { 0 };		//获得删除的人名
		printf("请输入要删除的联系人:>");
		scanf("%s", name);
		int pos = FindContact(pc, name);	//接收返回值
		if (pos == -1)
		{
			printf("通讯录中没有这位!\n");	//检查是否存在该联系人
			return;
		}

		for (int i = pos; i < pc->sz -1; i++)	//最后一位不需要移位,防止越界访问
		{
			pc->data[i] = pc->data[i + 1];		//让后面一位覆盖前面一位
		}
		pc->sz--;								//让最后一位联系人不能访问
		printf("删除成功\n");
	}
	else
	{
		printf("取消删除\n");
		return;
	}

}

这是目前的通讯录,现在要把

“我是例外”

删除 看看能不能成功

删除成功了,那试试能不能删除

“王五”

,毕竟通讯录里可没有王五

可行!

(5)封装查找联系人函数

查找函数前面写了,那直接找到并打印出来即可(这个最简单)

因为前面都写过了,复制过来稍微修改即可


这是当前通讯录的人员,我现在想找到

“2”

,就试试

可以找到,那看看能不能找到

“4”

看来出了一点点小问题 ,但问题不大,修改后继续。。

(6)封装修改联系人函数

要修改联系人前需要先找到要修改的联系人,然后再选择修改方式

再进行修改

//修改联系人
void ChangeData(list* pc)
{
	char check = 0;
	printf("确定要修改联系人吗(N取消):>");
	scanf("%c", &check);
	if (check == 'n' || check == 'N')
	{
		printf("取消修改\n");
		return;
	}

	//寻找联系人
	char name[MAX_NAME] = { 0 };
	printf("请输入要修改的联系人名字:>");
	scanf("%s", name);
	int pos = FindContact(pc, name);
	if (pos == -1)
	{
		printf("通讯录中没有这位\n");
		return;
	}
	//修改联系人
	int ch = 0;		//修改标志位
	printf("请输入修改类别:1.姓名 2.性别 3.年龄 4.电话 5.地址:>");
	scanf("%d", &ch);
	if (ch == 1)
	{
		printf("请输入修改后的姓名:>");
		scanf("%s", pc->data[pos].name);
	}
	else if (ch == 2)
	{
		printf("请输入修改后的性别:>");
		scanf("%s", pc->data[pos].sex);
	}
	else if (ch == 3)
	{
		printf("请输入修改后的年龄:>");
		scanf("%d",&(pc->data[pos].age));		//注意 取地址(&)
	}
	else if (ch == 4)
	{
		printf("请输入修改后的电话:>");
		scanf("%s", pc->data[pos].phone);
	}
	else if (ch == 5)
	{
		printf("请输入修改后的地址:>");
		scanf("%s", pc->data[pos].address);
	}
	else
	{
		printf("输入错误!\n");
		return;
	}
	printf("修改成功\n");

}

试试行不行

不小心把张三性别写成女了,看看能不能修改

看来可以修改

(7)封装排序联系人函数

排序可以按照名字排序,也可以按照性别排序,也可以按照年龄排序

也可以按照电话排序,和按照地址排序。

但这有点多(我懒),所以借助<stdlib.h>头文件中的qsort函数来排序吧

选自:

C 库函数 – qsort() | 菜鸟教程 (runoob.com)

其中重要的就是compar(两个元素间的比较函数),其实就是用一个函数

传两个参数,返回他们的差值,观察差值是大于零还是等于零或者小于零

选择:

qsort – C++ Reference (cplusplus.com)

所以我们需要先写出5种不同的函数指针,用于比较不同的结构体成员

接着封装排序联系人函数

看看能不能行

目前通讯录,接着按名字排序


可以看到李四跑上去了,因为strcmp是根据首字母的ASCII码值排序的。

李四的拼音首字母是“l”,王五的拼音首字母是“w”,l是小于w的,所以李四在上面

接着按性别排序

可以看到李四跑到底下去了,是因为男的拼音首字母 “n” 是小于女的拼音首字母也是“n”

所以比较第二个字母nan中的“a”、nv中的“v”,显然a小于v

所以李四在后面。接着排序年龄

比较整形就没什么可以说的了,那个小,那个在前面

接着比较电话

注意不要把电话当作整形,这里设置的是字符串,所以还是按照strcmp的比较方式比较。

接着比较地址

还是按照strcmp的比较方式比较 。

到此基本完成了通讯录,剩下一些细节修修补补

比如删除联系人到多少的时候,重新开辟空间,节省空间浪费

又比如在修改联系人的时候,提示选中的是那个联系人

或者修饰一下提示语,让它更显眼。

然后。。

就是关闭时保存数据,下次启动时继续对数据操作,这才是通讯录

二、完善框架

(1)维护内存防止浪费

首先在退出程序时,释放动态开辟的内存空间,防止内存泄漏

所以写一个退出通讯录的函数

然后是删除联系人过多时,重新开辟空间

其实就是把CheckCapcity(扩容函数)函数稍微修改一下

多加一个条件,当容量-数量>8,也就是容量空余了8个,就重新开辟一块

空间,当然也可以增加一些宏变量方便以后维护

(2)细节修饰

先将要修改的人提示出来然后修饰

先从网上拷一点制表符

/*
┌ ─ ┐

│   │

└ ─ ┘
*/

然后将每一个有取消、没有等提示语句加上,例如

        printf("┌────────┐\n");
		printf("│取消退出│\n");
		printf("└────────┘\n");

        printf("┌───────────────┐\n");
		printf("│还没有联系人呢!│\n");
		printf("└───────────────┘\n");

(3)退出后保存

现在程序还是运行在内存上,程序退出后,系统就会回收内存空间。

所以想要保存数据,就需要将数据转移到硬盘上去。

所以在要退出前,将数据保存到文件中,在下次初始化时读出来即可。

写入操作

读取操作

以下为gif演示

三、代码展示

源代码展示:

1.Contact.h 文件

#define _CRT_SECURE_NO_WARNINGS 1
#pragma once

/******头文件定义*******/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

/******头文件定义*******/

/********全局变量*******/
enum Contact_tem
{
	MAX_NAME = 20,			//最大姓名字符数
	MAX_SEX = 10,			//最大性别字符数
	MAX_PHONE = 15,		//最大电话字符数
	MAX_ADDRESS = 20		//最大地址字符数
};


enum Input
{
	Exit,		//退出 值0
	Add,		//增加 值1
	Del,		//删除 值2
	Find,		//查找 值3
	Change,		//修改 值4
	Sort,		//排序 值5
	Print		//打印 值6
};


/********全局变量*******/

/*******结构体定义*******/
typedef struct Contact
{
	char name[MAX_NAME];		//姓名
	char sex[MAX_SEX];			//性别
	int age;					//年龄
	char phone[MAX_PHONE];		//电话
	char address[MAX_ADDRESS];	//地址
}Contact;

typedef struct PhoneBook
{
	Contact* data;				//联系人
	int sz;						//联系人个数
	int Capcity;				//当前容量
}list;

/*******结构体定义*******/

/*******函数声明********/

//初始化通讯录
void InitData(list* pc);

//增加联系人
void AddData(list* pc);

//打印
void PrintData(list* pc);

//删除联系人
void DeleteData(list* pc);

//查找联系人
void FindData(list* pc);

//修改联系人
void ChangeData(list* pc);

//排序联系人
void SortData(list* pc);

//退出通讯录
void ExitData(list* pc);

//保存数据
void SaveData(list* pc);
/*******函数声明********/






2.Contact.c 文件

#include"Contact.h"

/**************备用函数区****************/

//扩容
void CheckCapcity(list* pc)
{
	Contact* ptr = NULL;						//用于接收重新获得的空间
	if (pc->Capcity == pc->sz)
	{
		ptr = (Contact*)realloc(pc->data,(pc->Capcity + 2) * sizeof(Contact));	//重新开辟空间
		if (ptr == NULL)
		{
			perror("CheckCapcity");				//开辟失败时报错并退出
			return;
		}
		pc->data = ptr;			//没有错误时将新开辟的空间地址赋予pc->data
		pc->Capcity += 2;		//重新计算当前容量
	}
	if (pc->Capcity - pc->sz > 8)				//删除扩容
	{
		ptr = (Contact*)realloc(pc->data, (pc->sz + 2) * sizeof(Contact));		//重新开辟空间为pc->sz+2 个联系人的空间
		if (ptr == NULL)
		{
			perror("CheckCapcity");
			return;
		}
		pc->data = ptr;
		pc->Capcity = pc->sz + 2;			//重新开辟内存容量为pc->sz+2个
	}
	
}

//查找联系人       通讯录      需要寻找的联系人
int FindContact(list* pc, char* str)
{
	for (int i = 0; i < pc->sz; i++)			//遍历通讯录
	{
		if (!(strcmp(pc->data[i].name, str)))	//两个字符串对比使用strcmp函数
		{
			return i;							//存在该联系人返回i
		}
		
	}
	return -1;									//不存在返回-1

}

//结构体排序 -- 姓名
int cmp_by_name(const void* e1, const void* e2)
{
	//void*指针不能解引用操作,所以需要转换成需要的类型才能继续
	return strcmp(((Contact*)e1)->name, ((Contact*)e2)->name);		//字符串的比较方式需要用strcmp函数实现
}
//结构体排序 -- 性别
int cmp_by_sex(const void* e1, const void* e2)
{
	//void*指针不能解引用操作,所以需要转换成需要的类型才能继续
	return strcmp(((Contact*)e1)->sex, ((Contact*)e2)->sex);
}
//结构体排序 -- 年龄
int cmp_by_age(const void* e1, const void* e2)
{
	//void*指针不能解引用操作,所以需要转换成需要的类型才能继续
	return ((Contact*)e1)->age - ((Contact*)e2)->age;		//整形相减即可
}
//结构体排序 -- 电话
int cmp_by_phone(const void* e1, const void* e2)
{
	//void*指针不能解引用操作,所以需要转换成需要的类型才能继续
	return strcmp(((Contact*)e1)->phone, ((Contact*)e2)->phone);
}
//结构体排序 -- 地址
int cmp_by_address(const void* e1, const void* e2)
{
	//void*指针不能解引用操作,所以需要转换成需要的类型才能继续
	return strcmp(((Contact*)e1)->address, ((Contact*)e2)->address);
}



/**************备用函数区****************/

/*
┌ ─ ┐

│   │

└ ─ ┘
*/



/**************主要函数区****************/
//初始化通讯录
void InitData(list* pc)
{
	pc->sz = 0;												//初始化时没有联系人
	pc->Capcity = 3;										//初始容量为3
	pc->data = (Contact*)malloc(sizeof(Contact) * 3);		//开辟一块内存为3个联系人大小的通讯录
	if (pc->data == NULL)									//开辟失败返回并报错
	{
		perror("InitData");
		return;
	}

	FILE* pf = fopen("text.dat", "a+");						//将文件以可读可写的方式打开,若文件不存在,则新建一个文件
	if (pf == NULL)
	{
		perror("fopen");									//当文件打开失败时报错并退出
		return;
	}
	Contact tem = { 0 };									//定义一个变量来变相读取fread的返回值\
	//		  接收变量   变量大小     接收个数  文件流	fread每读取一次就会返回剩下多少个元素
	while (fread(&tem, sizeof(Contact), 1, pf))
	{
		CheckCapcity(pc);									//判断是否需要扩容
		pc->data[pc->sz] = tem;								//将原数据覆盖
		pc->sz++;											//元素个数增加
	}
	fclose(pf);												//关闭文件
}

/*
		printf("┌────────┐\n");
		printf("│取消增加│\n");
		printf("└────────┘\n");
*/


void AddData(list* pc)
{
	char check = 0;							//判断是否继续添加
	printf("确定要添加联系人吗(N取消):>");
	scanf("%c", &check);
	if (check == 'n' || check == 'N')
	{
		//取消添加
		printf("┌────────┐\n");
		printf("│取消增加│\n");
		printf("└────────┘\n");
		return;
	}
	//继续添加
	CheckCapcity(pc);					//判断扩容
	printf("请输入姓名:>");
	scanf("%s", pc->data[pc->sz].name);		//姓名
	printf("请输入性别:>");
	scanf("%s", pc->data[pc->sz].sex);			//性别
	printf("请输入年龄:>");
	scanf("%d", &(pc->data[pc->sz].age));		//年龄
	printf("请输入电话:>");
	scanf("%s", pc->data[pc->sz].phone);		//电话
	printf("请输入住址:>");
	scanf("%s", pc->data[pc->sz].address);		//地址
	pc->sz++;
	
}

void PrintData(list* pc)
{
	printf("%-20s %-10s %-6s %-15s %-20s\n", "姓名", "性别", "年龄", "电话", "地址");	//分类
	printf("-------------------------------------------------------------------------\n");
	for (int i = 0; i < pc->sz; i++)
	{
		printf("%-20s %-10s %-6d %-15s %-20s\n",	
			pc->data[i].name,		//姓名
			pc->data[i].sex,		//性别
			pc->data[i].age,		//年龄
			pc->data[i].phone,		//电话
			pc->data[i].address		//地址
		);
		printf("-------------------------------------------------------------------------\n");
	}
}

//删除联系人
void DeleteData(list* pc)
{
	if (pc->sz == 0)						//当通讯录没有任何联系人时,直接返回
	{
		printf("┌───────────────┐\n");
		printf("│还没有联系人呢!│\n");
		printf("└───────────────┘\n");
		return;
	}
	
	char check = 0;							//判断是否误操作
	printf("确定要进行删除操作吗(N取消):>");
	scanf("%c", &check);
	CheckCapcity(pc);
	if (check != 'n' && check != 'N')
	{
		char name[MAX_NAME] = { 0 };		//获得删除的人名
		printf("请输入要删除的联系人:>");
		scanf("%s", name);
		int pos = FindContact(pc, name);	//接收返回值
		if (pos == -1)
		{
			printf("┌──────────────────┐\n");
			printf("│通讯录中没有这位!│\n");	//检查是否存在该联系人
			printf("└──────────────────┘\n");
			return;
		}

		for (int i = pos; i < pc->sz -1; i++)	//最后一位不需要移位,防止越界访问
		{
			pc->data[i] = pc->data[i + 1];		//让后面一位覆盖前面一位
		}
		pc->sz--;								//让最后一位联系人不能访问
		printf("┌────────┐\n");
		printf("│删除成功│\n");
		printf("└────────┘\n");
	}
	else
	{
		printf("┌────────┐\n");
		printf("│取消删除│\n");
		printf("└────────┘\n");
		return;
	}

}

//查找联系人
void FindData(list* pc)
{
	char check = 0;
	printf("确定要查找联系人吗(N取消):>");
	scanf("%c", &check);
	if (check == 'n' || check == 'N')
	{
		printf("┌────────┐\n");
		printf("│取消查找│\n");
		printf("└────────┘\n");
		return;
	}

	//寻找联系人
	char name[MAX_NAME] = { 0 };
	printf("请输入要查找的联系人:>");
	scanf("%s", name);
	int pos = FindContact(pc,name);
	if (pos == -1)
	{
		printf("┌──────────────────┐\n");
		printf("│通讯录中没有这位!│\n");	//检查是否存在该联系人
		printf("└──────────────────┘\n");
		return;
	}
	//打印联系人
	printf("%-20s %-10s %-6s %-15s %-20s\n", "姓名", "性别", "年龄", "电话", "地址");	//分类
	printf("-------------------------------------------------------------------------\n");
	printf("%-20s %-10s %-6d %-15s %-20s\n",
		pc->data[pos].name,		//姓名
		pc->data[pos].sex,		//性别
		pc->data[pos].age,		//年龄
		pc->data[pos].phone,		//电话
		pc->data[pos].address		//地址
	);
	printf("-------------------------------------------------------------------------\n");

}

//修改联系人
void ChangeData(list* pc)
{
	char check = 0;
	printf("确定要修改联系人吗(N取消):>");
	scanf("%c", &check);
	if (check == 'n' || check == 'N')
	{
		printf("┌────────┐\n");
		printf("│取消修改│\n");
		printf("└────────┘\n");
		return;
	}

	//寻找联系人
	char name[MAX_NAME] = { 0 };
	printf("请输入要修改的联系人名字:>");
	scanf("%s", name);
	int pos = FindContact(pc, name);
	if (pos == -1)
	{
		printf("┌──────────────────┐\n");
		printf("│通讯录中没有这位!│\n");	//检查是否存在该联系人
		printf("└──────────────────┘\n");
		return;
	}
	printf("当前选中:  < %s >\n", pc->data[pos].name);
	//修改联系人
	int ch = 0;		//修改标志位
	printf("请输入修改类别:1.姓名 2.性别 3.年龄 4.电话 5.地址:>");
	scanf("%d", &ch);
	if (ch == 1)
	{
		printf("请输入修改后的姓名:>");
		scanf("%s", pc->data[pos].name);
	}
	else if (ch == 2)
	{
		printf("请输入修改后的性别:>");
		scanf("%s", pc->data[pos].sex);
	}
	else if (ch == 3)
	{
		printf("请输入修改后的年龄:>");
		scanf("%d",&(pc->data[pos].age));		//注意 取地址(&)
	}
	else if (ch == 4)
	{
		printf("请输入修改后的电话:>");
		scanf("%s", pc->data[pos].phone);
	}
	else if (ch == 5)
	{
		printf("请输入修改后的地址:>");
		scanf("%s", pc->data[pos].address);
	}
	else
	{
		printf("┌────────┐\n");
		printf("│输入错误│\n");
		printf("└────────┘\n");
		return;
	}
	printf("┌────────┐\n");
	printf("│修改成功│\n");
	printf("└────────┘\n");

}


//排序联系人
void SortData(list* pc)
{
	char check = 0;
	printf("确定要排序吗(N取消):>");
	scanf("%c", &check);
	if (check == 'n' || check == 'N')
	{
		printf("┌────────┐\n");
		printf("│取消排序│\n");
		printf("└────────┘\n");
		return;
	}
	//排序
	//函数指针数组,将每一个函数指针组成数组,有需要请自行百度
	int (*pf[6])(const void*, const void*) =
	{ NULL,cmp_by_name,cmp_by_sex,cmp_by_age,cmp_by_phone,cmp_by_address};
	int tmp = 0;
	printf("请选择排序方式:1.姓名 2.性别 3.年龄 4.电话 5.地址:>");
	scanf("%d", &tmp);

	//    排序位置	元素个数		单个元素字节数			元素对比方式
	qsort(pc->data, pc->sz, sizeof(pc->data[0]), pf[tmp]);	//排序函数

	printf("┌────────┐\n");
	printf("│排序成功│\n");
	printf("└────────┘\n");

}

void ExitData(list* pc)
{
	char check = 0;
	printf("确定要退出吗(N取消):>");
	scanf("%c", &check);
	if (check == 'N' || check == 'n')
	{
		printf("┌────────┐\n");
		printf("│取消退出│\n");
		printf("└────────┘\n");
		return;
	}
	free(pc->data);			//释放内存
	printf("┌────┐\n");
	printf("│退出│\n");
	printf("└────┘\n");

}

//保存数据
void SaveData(list* pc)
{
	FILE* pf = fopen("text.dat", "w");			//以只写的形式打开文件,如果文件不存在,创建一个新的文件
	if (pf == NULL)
	{
		perror("fopen");						//打开失败时报错
		return;
	}
	//		写入数据		单个数据大小	  写入多少	文件流
	fwrite(pc->data, sizeof(Contact), pc->sz, pf);


	fclose(pf);									//关闭文件
}
/**************主要函数区****************/



3.text.c 文件

#include"Contact.h"

void menu()
{
	//菜单
	printf("*****************************\n");
	printf("****      通讯录v1.0    ****\n");
	printf("****   1.增加   2.删除   ****\n");
	printf("****   3.查找   4.修改   ****\n");
	printf("****   5.排序   6.打印   ****\n");
	printf("****        0.退出       ****\n");
	printf("*****************************\n");
}

int main()
{
	int input = 0;
	list con;			//定义通讯录
	InitData(&con);		//初始化通讯录
	do
	{
		menu();					//菜单
		printf("请输入:>");
		scanf("%d", &input);
		getchar();				//吸收换行(\n)
		switch (input)
		{
		case Add:				//增加
			AddData(&con);
			break;
		case Del:				//删除
			DeleteData(&con);
			break;
		case Find:				//...
			FindData(&con);
			break;
		case Change:
			ChangeData(&con);
			break;
		case Sort:
			SortData(&con);
			break;
		case Print:
			PrintData(&con);
			break;
		case Exit:
			SaveData(&con);
			ExitData(&con);
			break;
		default:
			printf("输入错误\n");
			break;
		}
	} while (input);
}

如有错误,敬请指出,多谢🙏



版权声明:本文为sz1z_20_hsh原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。