手把手教写通讯录(详细讲解)

通讯录

  • 一、通讯录的整体规划
  • 二、函数主体
    • 1、代码
    • 2、实现原理
  • 三、菜单函数menu的实现
    • 1、代码
    • 2、实现原理
  • 四、宏定义的设置
    • 1、代码
    • 2、代表的意思
  • 五、接收联系人的信息变量与必要的计数变量
    • 1、代码
    • 2、代表的意思
  • 六、枚举变量的设置
    • 1、代码
    • 2、实现的意义
  • 七、初始化函数的定义
    • 1、函数在三个文件中的定义
    • 2、代码
    • 3、实现原理
    • 4、注意
  • 八、加载函数的定义
    • 1、代码
    • 2、实现原理
    • 3、注意
  • 九、检查是否需要扩容的函数的定义
    • 1、代码
    • 2、实现原理
    • 3、注意
  • 十、添加联系人的函数的定义
    • 1、代码
    • 2、实现原理
    • 3、注意
  • 十一、输出通讯录中信息的函数的定义
    • 1、代码
    • 2、实现原理
  • 十二、删除通讯录中人信息的函数的定义
    • 1、代码
    • 2、实现原理
    • 3、注意
  • 十三、以通讯录中人的姓名进行查找的函数的定义
    • 1、代码
    • 2、实现原理
  • 十四、查找通讯录中指定人的信息的函数的定义
    • 1、代码
    • 2、实现原理
  • 十五、修改通讯录中指定人的信息的函数的定义
    • 1、代码
    • 2、实现原理
  • 十六、对通讯录进行排序的函数的定义
    • 1、代码
    • 2、实现原理
    • 3、注意
  • 十七、保存通讯录数据的函数的定义
    • 1、代码
    • 2、实现原理
    • 3、注意
  • 十八、销毁通讯录的函数的定义
    • 1、代码
    • 2、实现原理
  • 十九、实现通讯录的所有代码
    • 1、main.c
    • 2、Contact.h
    • 3、Contact.c

一、通讯录的整体规划

  • 因为实现通讯录的代码比较多,如果把这些代码全部写在一个文件里面,就会显得代码比较杂乱、可读性较差。
  • 所以用三个文件来实现通讯录,分别是main.c、Contact.hContact.c
  • 其中,main.c是用来写函数的整体实现,比如主函数main的规划等等,而Contact.h则是用来放置函数的声明、头文件的包含与宏定义,Contact.c则是实现在Contact.h里声明的函数与定义部分需要使用到的函数。

二、函数主体

只是先说明main函数主体的构造,里面包含的函数在后面会一一说明

1、代码

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

void Setting()
{
	int input = 0;
	Contact con;
	InitContact(&con);
	do
	{
		menu();
		printf("请输入你的选择\n");
		scanf("%d", &input);
		switch (input)
		{
		case ADD:
			AddContact(&con);
			break;
		case DEL:
			DelContact(&con);
			break;
		case SEACH:
			SeachContact(&con);
			break;
		case MODIFY:
			ModifyContact(&con);
			break;
		case SORT:
			SortContact(&con);
			break;
		case PRINT:
			PrintContact(&con);
			break;
		case EXIT:
			SaveContact(&con);
			DestroyContact(&con);
			printf("退出通讯录\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (input);
}
int main()
{
	Setting();
	return 0;
}

2、实现原理

  • 首先,我们需要确定main函数的构成,用一个setting函数来实现通讯录的选择,在main函数内调用它。
  • 最开始,我们需要定义一个结构体变量Contact con,它包含通讯录中人的信息和通讯录的容量,再者,对这个通讯录进行初始化。
  • 因为通讯录最开始需要打印菜单和提示用户进行选择,所以,采用do while循环来达成开始时总先进行一次循环的目的。然后再构造一个menu函数来显示菜单的内容。
  • 再者,输出一条语句来提示用户选择菜单的内容,用一个变量input来接收用户选择的结果。并用此变量作为下方的switch循环和do while循环的判断条件。
  • 当用户输入的选择是0时,进行保存数据、销毁通讯录和输出退出提示语的操作,因为input=0,do while循环判断条件结果为假,循环结束;当用户输入的选择是1~6时,使用户对通讯录进行操作,因为input不是0,do while循环判断条件结果为真,继续循环;当用户输入的选择不是1 ~ 6也不是0时,输出输入错误的提示语,do while循环判断条件结果为真,继续循环。

三、菜单函数menu的实现

1、代码

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

2、实现原理

  • 使用printf函数进行输出,用 ‘*’ 作为边框,在中间添加选项,提示用户进行选择。
  • 其中,1~6分别表示添加联系人、删除联系人、查找联系人、修改联系人的信息、对通讯录中每个联系人的信息以姓名的方式进行排序、打印通讯录所有联系人的信息。而0选项则是退出对通讯录的操作。

四、宏定义的设置

1、代码

#define NAME_MAX 20
#define SEX_MAX 5
#define TELE_MAX 14
#define ADDR_MAX 20
#define DEFAULT_MAX 3

2、代表的意思

  • 代码中的宏定义从上到下代表的意思是:
  • 联系人姓名的最大字符数、性别的的最大字符数、电话号码的最大字符数、地址的最大字符数和通讯录初始时的最大容量

五、接收联系人的信息变量与必要的计数变量

1、代码

typedef struct Peoinfo
{
	char name[NAME_MAX];
	char sex[SEX_MAX];
	int age;
	char tele[TELE_MAX];
	char addr[ADDR_MAX];
}Peoinfo;

typedef struct Contact
{
	Peoinfo* data;
	int sz;
	int capacity;
}Contact;

2、代表的意思

  • 第一个结构体定义的是接收联系人信息的变量,分别接收联系人的姓名、性别、年龄、电话号码和地址。
  • 第二个结构体定义一个第一个结构体的指针、记录通讯录保存的人数和通讯录当前可以保存的最大人数。
  • 因为这些变量是整个程序都要使用到的,所以定义在Contact.h这个头文件中。

六、枚举变量的设置

1、代码

enum option
{
	EXIT,
	ADD,
	DEL,
	SEACH,
	MODIFY,
	SORT,
	PRINT
};

2、实现的意义

  • 由于我们在看代码时,如果直接在每个选项前丢一个数字,一旦时间久了或者是别人看这些代码,在没看菜单中的选项时,可能不知道这些数字代表什么意思。
  • 所以,用枚举的方式来对这些选项进行定义,第一个选项默认是从0开始的,与选择的选项一一对应,当我们看到这些东西时,我们能很容易的知道这些选项代表的是什么意思。

七、初始化函数的定义

1、函数在三个文件中的定义

  • main.c文件:InitContact(&con);

将包含接收联系人信息和计数变量的结构体变量的地址作为参数传给函数。

  • Contact.h文件:void InitContact(Contact* pc);

对函数进行声明,这使得我们在其他两个文件中都可以使用这个函数,如果对该结构体的内容不做改变,函数的形参前可以加上const。

  • 由于实现通讯录的函数的声明等等都差不多,下面就不再说明。

2、代码

void InitContact(Contact* pc)
{
	assert(pc);				//记得加上断言
	pc->capacity = DEFAULT_MAX;
	pc->data = (Peoinfo*)malloc(pc->capacity * sizeof(Peoinfo));
	pc->sz = 0;
	if (pc->data == NULL)				//当初始化失败时显示失败信息
	{
		perror("InitContact::malloc");
		return;
	}
	memset(pc->data, 0, pc->capacity * sizeof(Peoinfo));
	LoadContact(pc);
}

3、实现原理

  • assert函数对接收实参的指针进行判断,防止空指针的情况发生。
  • 对最大容量的计数变量capacity和计算已存储联系人数量的变量sz进行初始化,在堆上开辟一块内存空间用来存储联系人的信息。
  • 因为如果malloc开辟空间失败会返回空指针,所以当接收的指针为空指针时,输出错误信息。
  • 如果开辟成功,用memset将该空间初始化为0,然后调用加载通讯录的函数。

4、注意

  • memset不能写成memset(pc, 0, sizeof(pc->data));
  • 如果写成那样会使程序崩溃。

八、加载函数的定义

1、代码

void LoadContact(Contact* pc)
{
	FILE* pf = fopen("Contact.dat", "rb");
	if (pf == NULL)
	{
		perror("LoadContact::fopen");
		return;
	}
	Peoinfo tmp = { 0 };
	while (fread(&tmp, sizeof(Peoinfo), 1, pf))
	{
		CheckCapacity(pc);
		pc->data[pc->sz] = tmp;
		pc->sz++;
	}
	fclose(pf);
	pf = NULL;
}

2、实现原理

  • 用二进制的方式读取数据,检查空间是否不足而进行扩容,并将取得的数据进行赋值。

3、注意

  • 赋值完成后需要对sz进行自增,否则无法正确或正常赋值。
  • 记得加上检查是否需要扩容的那个函数的语句,否则会出现空间不够的情况。
  • 不能以for (int i = 0; i < pc->sz; ++i)作为循环,因为此时sz为0。
  • 在最后不要忘记关闭文件和把指针置空。

九、检查是否需要扩容的函数的定义

1、代码

void CheckCapacity(Contact* pc)
{
	if (pc->sz == pc->capacity)
	{
		Peoinfo* tmp = (Peoinfo*)realloc(pc->data, (pc->capacity + 2) * sizeof(Peoinfo));
		if (tmp != NULL)
		{
			pc->data = tmp;
			pc->capacity += 2;
			printf("扩容成功\n");
		}
		else
		{
			perror("CheckCapacity::realloc");		//显示扩容失败的信息
			return;
		}
	}
}

2、实现原理

  • capacity是最大容量,如果sz和它相等证明需要进行扩容了,否则空间不够无法继续对通讯录行操作。

3、注意

  • 需要用临时变量tmp接收而不能直接用data接收,否则当开辟内存失败时,指针会被置空导致无法找到之前的地址。
  • tmp为判断条件的分支判断语句需写在第一个if语句内,否则当跳出if语句,tmp变量将是未定义的变量。

十、添加联系人的函数的定义

1、代码

void AddContact(Contact* pc)
{
	assert(pc);
	CheckCapacity(pc);

	printf("请输入欲增加联系人的姓名:\n");
	scanf("%s", pc->data[pc->sz].name);
	printf("请输入欲增加联系人的性别:\n");
	scanf("%s", pc->data[pc->sz].sex);
	printf("请输入欲增加联系人的年龄:\n");
	scanf("%d", &(pc->data[pc->sz].age));			//年龄是int型,不要用%s打印
	printf("请输入欲增加联系人的电话号码:\n");
	scanf("%s", pc->data[pc->sz].tele);
	printf("请输入欲增加联系人的地址:\n");
	scanf("%s", pc->data[pc->sz].addr);
	pc->sz++;
	printf("添加联系人成功\n");			//输出提示语句
}

2、实现原理

  • 首先是用assert断言,防止指针为空的情况发生。
  • 因为添加联系人需要空间,而多次添加联系人会有空间不足的情况发生,所以用CheckCapacity函数进行判断是否需要扩容和进行扩容的操作。
  • 最后输出提示用户进行输入数据的信息,提示用户输入信息和对这些信息的接收,最后将计算保存信息人数的变量自增1和输出添加联系人成功的提示语。

3、注意

  • 因为接收字符串数组的数组名是它首元素的地址,所以对它们的接收不要取地址。
  • 而接收年龄的变量age是int型,需要对它进行取地址的操作,格式是%d不要搞错。

十一、输出通讯录中信息的函数的定义

1、代码

void PrintContact(const Contact* pc)			//只打印不修改,可加const
{
	assert(pc);
	printf("%-20s %-5s %-5s %-14s %-20s\n", "姓名", "性别", "年龄", "电话号码", "地址");
	for (int i = 0; i < pc->sz; ++i)
	{
		printf("%-20s %-5s %-5d %-14s %-20s\n", pc->data[i].name, pc->data[i].sex,
			pc->data[i].age, pc->data[i].tele, pc->data[i].addr);
	}
}

2、实现原理

  • 因为输出的格式是正数的话,它输出的信息是右对齐的。而如果要以我们熟悉的左对齐格式输出信息,就需要将格式中的数字改为负数
  • 最后输出信息用一个循环实现,通讯录中保存的人数有多少就进行多少次循环。

十二、删除通讯录中人信息的函数的定义

1、代码

void DelContact(Contact* pc)
{
	assert(pc);
	if (pc->sz == 0)
	{
		printf("通讯录为空,无法删除\n");
		return;
	}
	char name[NAME_MAX] = { 0 };		//可使用NAME_MAX,而不是数字
	printf("请输入欲删除人的姓名\n");
	scanf("%s", name);
	int pos = FindByName(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");
}

2、实现原理

  • sz等于0时,说明通讯录中没有任何数据,则输出通讯录为空的提示语并退出函数。
  • 删除联系人的信息首先是要在通讯录中找到这个人,用一个大小是NAME_MAX的字符数组接收用户输入的联系人姓名。
  • 再调用FindByName函数在通讯录中进行查找,如果找到了返回它的下标,如果没有则输出通讯录无此人的提示语并退出。
  • 最后用循环将被删除的那个人的位置后面的所有数据都前移一位,然后将sz自减1,并输出删除成功的提示语。

3、注意

  • 最后的循环中,data后无需加结构体成员变量名,直接对该元素进行赋值就行。

十三、以通讯录中人的姓名进行查找的函数的定义

1、代码

int FindByName(Contact* pc, char* name)
{
	assert(pc);				//记得加上断言
	for (int i = 0; i < pc->sz; ++i)
	{
		if (strcmp(pc->data[i].name, name) == 0)
			return i;			//找到返回下标
	}
	return -1;				//找不到返回-1
}

2、实现原理

  • strcmp函数对通讯录中每个数据的姓名成员和用户输入的欲删除人的姓名进行比较,如果姓名相同strcmp函数将返回0,然后就把这时候i的值(该数据的下标)返回,函数结束。
  • 而当遍历完全部数据后都没有与用户输入的欲删除人的姓名相同的元素,则返回 -1

十四、查找通讯录中指定人的信息的函数的定义

1、代码

void SeachContact(const Contact* pc)
{
	assert(pc);
	if (pc->sz == 0)
	{
		printf("通讯录为空,无法查找\n");
		return;
	}
	char name[NAME_MAX] = { 0 };		//可使用NAME_MAX,而不是数字
	printf("请输入欲查找人的姓名:\n");
	scanf("%s", name);
	int pos = FindByName(pc, name);
	if (pos == -1)
	{
		printf("通讯录中无此人的信息\n");
		return;
	}
	printf("%-20s %-5s %-5s %-14s %-20s\n", "姓名", "性别", "年龄", "电话号码", "地址");
	printf("%-20s %-5s %-5d %-14s %-20s\n", pc->data[pos].name, pc->data[pos].sex,
		pc->data[pos].age, pc->data[pos].tele, pc->data[pos].addr);
}

2、实现原理

  • 此函数与上方的删除函数差不多,只不过是最后在通讯录中查找到该联系人后,对它的信息进行打印。

十五、修改通讯录中指定人的信息的函数的定义

1、代码

void ModifyContact(Contact* pc)
{
	assert(pc);
	if (pc->sz == 0)
	{
		printf("通讯录为空,无法修改\n");
		return;
	}
	char name[NAME_MAX] = { 0 };
	printf("请输入欲修改人的姓名:\n");
	scanf("%s", name);
	int pos = FindByName(pc, name);
	if (pos == -1)
	{
		printf("通讯录中无此人的信息,无法修改\n");
		return;
	}
	printf("请输入修改后的联系人姓名:\n");
	scanf("%s", pc->data[pos].name);
	printf("请输入修改后的联系人性别:\n");
	scanf("%s", pc->data[pos].sex);
	printf("请输入修改后的联系人年龄:\n");
	scanf("%d", &(pc->data[pos].age));
	printf("请输入修改后的联系人电话号码:\n");
	scanf("%s", pc->data[pos].tele);
	printf("请输入修改后的联系人地址:\n");
	scanf("%s", pc->data[pos].addr);
}

2、实现原理

  • 此函数与上方的删除函数差不多,只不过是最后在通讯录中查找到该联系人后,输出提示用户进行输入的提示语,并用scanf函数接收数据,即对该位置的数据进行覆盖(修改)。

十六、对通讯录进行排序的函数的定义

1、代码

void SortContact(Contact* pc)
{
	assert(pc);
	if (pc->sz == 0)
	{
		printf("通讯录为空,无法排序\n");
		return;
	}
	Peoinfo tmp = { 0 };
	for (int i = 0; i < pc->sz; ++i)
	{
		for (int j = 0; j < pc->sz - 1 - i; ++j)
		{
			//if(strcmp(pc->data+j,pc->data+j+1)>0)			//比较的是名字,而不是整体,不能用这种
			if (strcmp(pc->data[j].name, pc->data[j + 1].name) > 0)
			{
				tmp = pc->data[j];
				pc->data[j] = pc->data[j + 1];
				pc->data[j + 1] = tmp;
			}
		}
	}
	printf("排序成功\n");
}

2、实现原理

  • sz等于0时,说明通讯录中没有任何数据,则输出通讯录为空的提示语并退出函数。
  • 用一个临时变量tmp实现两个元素数据的交换,然后直接用冒泡排序实现。

3、注意

  • 循环中if语句的判断条件不能写成strcmp(pc->data+j,pc->data+j+1)>0,因为条件只是用元素的姓名变量进行比较,而不是整体。并且,这种没有加成员变量的结构体的使用是错误的。

十七、保存通讯录数据的函数的定义

1、代码

void SaveContact(const Contact* pc)
{
	FILE* pf = fopen("Contact.dat", "wb");
	if (pf == NULL)
	{
		perror("SaveContact::fopen");
		return;
	}
	for (int i = 0; i < pc->sz; ++i)
	{
		//fwrite(pc->data[i], sizeof(Peoinfo), 1, pf);			//这种方式不可以
		fwrite(pc->data + i, sizeof(Peoinfo), 1, pf);
	}
	fclose(pf);
	pf = NULL;
}

2、实现原理

  • FILE类型的指针接收打开文件函数fopen的结果,fopen中第一个参数是欲写入的文件名,第二个参数是对该文件进行的操作wb为以二进制的方式进行写入,即写入到该文件中的数据是二进制形式的。
  • 如果pf为空指针说明打开文件失败,用perror函数输出错误信息,然后结束函数。
  • 用循环的方式将数据写入到文件中。
  • 最后用fclose函数关闭文件,然后将pf指针置空。

3、注意

  • 循环中写入文件的fwrite函数中的第一个参数不能是pc->data[i],因为pc->data[i]相当于*(pc->data + i),即它不是一个地址而是一个数据。
  • 再者,它后面没有跟结构体成员变量名,是错误的用法。
  • 如果以%s的格式对pc->data[i]进行打印,则程序会崩,以%0x进行打印则为31(不是结构体的大小)。

十八、销毁通讯录的函数的定义

1、代码

void DestroyContact(Contact* pc)
{
	free(pc->data);
	pc->data = NULL;
	pc->sz = 0;
	pc->capacity = 0;
	printf("销毁通讯录成功\n");
}

2、实现原理

  • 因为开始时是在堆上开辟的空间,所以在最后不需要用到这块空间时,我们需要对它进行释放,以避免内存泄露的问题发生。
  • 最后将指针置空,变量置零和输出销毁通讯录的提示语。

十九、实现通讯录的所有代码

1、main.c

#include"Contact.h"

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

void Setting()
{
	int input = 0;
	Contact con;
	InitContact(&con);
	do
	{
		menu();
		printf("请输入你的选择\n");
		scanf("%d", &input);
		switch (input)
		{
		case ADD:
			AddContact(&con);
			break;
		case DEL:
			DelContact(&con);
			break;
		case SEACH:
			SeachContact(&con);
			break;
		case MODIFY:
			ModifyContact(&con);
			break;
		case SORT:
			SortContact(&con);
			break;
		case PRINT:
			PrintContact(&con);
			break;
		case EXIT:
			SaveContact(&con);
			DestroyContact(&con);
			printf("退出通讯录\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (input);
}
int main()
{
	Setting();
	return 0;
}

2、Contact.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS

//头文件包含
#include
#include
#include
#include

//#define MAX 100			//通讯录的最大人数(静态版本需要)
#define NAME_MAX 20
#define SEX_MAX 5
#define TELE_MAX 14
#define ADDR_MAX 20

			//通讯录初始时的最大容量
#define DEFAULT_MAX 3
			//定义每个人的信息
typedef struct Peoinfo
{
	char name[NAME_MAX];
	char sex[SEX_MAX];
	int age;
	char tele[TELE_MAX];
	char addr[ADDR_MAX];
}Peoinfo;

typedef struct Contact
{
	Peoinfo* data;
	int sz;			//记录通讯录保存的人数
	int capacity;			//初始时的通讯录最大人数
}Contact;

		//枚举,使选择项更具可读性
enum option
{
	EXIT,
	ADD,
	DEL,
	SEACH,
	MODIFY,
	SORT,
	PRINT
};

			//初始化通讯录
void InitContact(Contact* pc);
			//增加联系人
void AddContact(Contact* pc);
			//输出通讯录中人的信息
void PrintContact(const Contact* pc);
			//删除通讯录中人的信息
void DelContact(Contact* pc);
			//查找通讯录中指定人的信息
void SeachContact(const Contact* pc);
			//修改通讯录中指定人的信息
void ModifyContact(Contact* pc);
			//销毁通讯录
void DestroyContact(Contact* pc);
			//保存通讯录数据
void SaveContact(const Contact* pc);
			//对通讯录进行排序
void SortContact(Contact* pc);

3、Contact.c

#include"Contact.h"

			//检查容量是否已满并扩容
void CheckCapacity(Contact* pc)
{
	if (pc->sz == pc->capacity)
	{
		Peoinfo* tmp = (Peoinfo*)realloc(pc->data, (pc->capacity + 2) * sizeof(Peoinfo));

		//分支判断语句需写在if语句内,否则当跳出if语句,tmp变量将是未定义的变量
		if (tmp != NULL)
		{
			pc->data = tmp;
			pc->capacity += 2;
			printf("扩容成功\n");
		}
		else
		{
			perror("CheckCapacity::realloc");		//显示扩容失败的信息
			return;
		}
	}
}

//			//动态版本
//			//初始化通讯录
//void InitContact(Contact* pc)
//{
//	assert(pc);				//记得加上断言
//	pc->capacity = DEFAULT_MAX;
//	pc->data = (Peoinfo*)malloc(pc->capacity * sizeof(Peoinfo));
//	//memset(pc, 0, sizeof(pc->data));
//	pc->sz = 0;
//	if (pc->data == NULL)				//当初始化失败时显示失败信息
//	{
//		perror("InitContact::malloc");
//		return;
//	}
//}

			//加载通讯录
void LoadContact(Contact* pc)
{
	FILE* pf = fopen("Contact.dat", "rb");
	if (pf == NULL)
	{
		perror("LoadContact::fopen");
		return;
	}
	Peoinfo tmp = { 0 };
	while (fread(&tmp, sizeof(Peoinfo), 1, pf))
	{
		CheckCapacity(pc);			//记得加上此语句,否则会出现空间不够的情况
		pc->data[pc->sz] = tmp;
		pc->sz++;
	}
	
	//while (fread(pc->data + pc->sz, sizeof(Peoinfo), 1, pf))
	//{
	//	CheckCapacity(pc);			//记得加上此语句,否则会出现空间不够的情况
	//	pc->sz++;
	//}

	//			//不可以用这种方式加载通讯录,因为此时sz为0
	//for (int i = 0; i < pc->sz; ++i)
	//{
	//	CheckCapacity(pc);
	//	fread(pc->data[i], sizeof(Peoinfo), 1, pf);
	//}
				//记得关闭文件和把指针置空
	fclose(pf);
	pf = NULL;
}
			//文件版本
void InitContact(Contact* pc)
{
	assert(pc);				//记得加上断言
	pc->capacity = DEFAULT_MAX;
	pc->data = (Peoinfo*)malloc(pc->capacity * sizeof(Peoinfo));
	//memset(pc, 0, sizeof(pc->data));			//错误写法,会导致程序崩溃
	pc->sz = 0;
	if (pc->data == NULL)				//当初始化失败时显示失败信息
	{
		perror("InitContact::malloc");
		return;
	}
	memset(pc->data, 0, pc->capacity * sizeof(Peoinfo));
	LoadContact(pc);
}

			//增加联系人
void AddContact(Contact* pc)
{
	assert(pc);
			//静态版本
	//if (pc->sz == MAX)			//当sz与MAX相等时就代表已经满了,不能用大于号来进行判断
	//{
	//	printf("通讯录已满,无法添加\n");
	//	return;
	//}
			//动态版本
	CheckCapacity(pc);

	printf("请输入欲增加联系人的姓名:\n");
	scanf("%s", pc->data[pc->sz].name);
	printf("请输入欲增加联系人的性别:\n");
	scanf("%s", pc->data[pc->sz].sex);
	printf("请输入欲增加联系人的年龄:\n");
	scanf("%d", &(pc->data[pc->sz].age));			//年龄是int型,不要用%s格式
	printf("请输入欲增加联系人的电话号码:\n");
	scanf("%s", pc->data[pc->sz].tele);
	printf("请输入欲增加联系人的地址:\n");
	scanf("%s", pc->data[pc->sz].addr);
	pc->sz++;
	printf("添加联系人成功\n");			//输出提示语句
}
			//输出通讯录中人的信息
void PrintContact(const Contact* pc)			//只打印不修改,可加const
{
	assert(pc);
	printf("%-20s %-5s %-5s %-14s %-20s\n", "姓名", "性别", "年龄", "电话号码", "地址");
	for (int i = 0; i < pc->sz; ++i)
	{
		printf("%-20s %-5s %-5d %-14s %-20s\n", pc->data[i].name, pc->data[i].sex,
			pc->data[i].age, pc->data[i].tele, pc->data[i].addr);
	}
}
			//以通讯录中人的姓名进行查找
int FindByName(Contact* pc, char* name)
{
	assert(pc);				//记得加上断言
	for (int i = 0; i < pc->sz; ++i)
	{
		if (strcmp(pc->data[i].name, name) == 0)
			return i;			//找到返回下标
	}
	return -1;				//找不到返回-1
}
			//删除通讯录中人的信息
void DelContact(Contact* pc)
{
	assert(pc);
	if (pc->sz == 0)
	{
		printf("通讯录为空,无法删除\n");
		return;
	}
	char name[NAME_MAX] = { 0 };		//可使用NAME_MAX,而不是数字
	printf("请输入欲删除人的姓名\n");
	scanf("%s", name);
	int pos = FindByName(pc, name);
	if (pos == -1)
	{
		printf("通讯录中无此人的信息,删除失败\n");
		return;
	}
	for (int i = pos; i < pc->sz - 1; ++i)
	{
		pc->data[i] = pc->data[i + 1];		//data后无需加结构体成员变量名,因为其是不可修改的左值
	}
	pc->sz--;
	printf("删除成功\n");
}
			//查找通讯录中指定人的信息
void SeachContact(const Contact* pc)
{
	assert(pc);
	if (pc->sz == 0)
	{
		printf("通讯录为空,无法查找\n");
		return;
	}
	char name[NAME_MAX] = { 0 };		//可使用NAME_MAX,而不是数字
	printf("请输入欲查找人的姓名:\n");
	scanf("%s", name);
	int pos = FindByName(pc, name);
	if (pos == -1)
	{
		printf("通讯录中无此人的信息\n");
		return;
	}
	printf("%-20s %-5s %-5s %-14s %-20s\n", "姓名", "性别", "年龄", "电话号码", "地址");
	printf("%-20s %-5s %-5d %-14s %-20s\n", pc->data[pos].name, pc->data[pos].sex,
		pc->data[pos].age, pc->data[pos].tele, pc->data[pos].addr);
}
			//修改通讯录中指定人的信息
void ModifyContact(Contact* pc)
{
	assert(pc);
	if (pc->sz == 0)
	{
		printf("通讯录为空,无法修改\n");
		return;
	}
	char name[NAME_MAX] = { 0 };
	printf("请输入欲修改人的姓名:\n");
	scanf("%s", name);
	int pos = FindByName(pc, name);
	if (pos == -1)
	{
		printf("通讯录中无此人的信息,无法修改\n");
		return;
	}
	printf("请输入修改后的联系人姓名:\n");
	scanf("%s", pc->data[pos].name);
	printf("请输入修改后的联系人性别:\n");
	scanf("%s", pc->data[pos].sex);
	printf("请输入修改后的联系人年龄:\n");
	scanf("%d", &(pc->data[pos].age));			//年龄是int型,不要用%s的格式
	printf("请输入修改后的联系人电话号码:\n");
	scanf("%s", pc->data[pos].tele);
	printf("请输入修改后的联系人地址:\n");
	scanf("%s", pc->data[pos].addr);
}
			//销毁通讯录
void DestroyContact(Contact* pc)
{
	free(pc->data);
	pc->data = NULL;
	pc->sz = 0;
	pc->capacity = 0;
	printf("销毁通讯录成功\n");
}
			//保存通讯录数据
void SaveContact(const Contact* pc)
{
	FILE* pf = fopen("Contact.dat", "wb");
	if (pf == NULL)
	{
		perror("SaveContact::fopen");
		return;
	}
	for (int i = 0; i < pc->sz; ++i)
	{
		//fwrite(pc->data[i], sizeof(Peoinfo), 1, pf);			//这种方式不可以
		fwrite(pc->data + i, sizeof(Peoinfo), 1, pf);
	}
	fclose(pf);
	pf = NULL;
}
			//对通讯录进行排序
void SortContact(Contact* pc)
{
	assert(pc);
	if (pc->sz == 0)
	{
		printf("通讯录为空,无法排序\n");
		return;
	}
	Peoinfo tmp = { 0 };
	for (int i = 0; i < pc->sz; ++i)
	{
		for (int j = 0; j < pc->sz - 1 - i; ++j)
		{
			//if(strcmp(pc->data+j,pc->data+j+1)>0)			//比较的是名字,而不是整体,不能用这种
			if (strcmp(pc->data[j].name, pc->data[j + 1].name) > 0)
			{
				tmp = pc->data[j];
				pc->data[j] = pc->data[j + 1];
				pc->data[j + 1] = tmp;
			}
		}
	}
	printf("排序成功\n");
}

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