笔记21-3 (C语言进阶 通讯录)

目录

要求

思路

前期的准备

1.设置类型存储通讯录内的数据

2.规划流程

实现

创建通讯录

初始化通讯录部分 

用户选择部分

ADDContact函数部分(增加联系人)

PRINTContact函数部分(打印联系人信息)

FindByName函数部分(通过名字查找对象)

DELContact函数部分(删除联系人的信息)

SEARCHContact函数部分(查找指定联系人)

MODIFYContact函数部分(修改指定联系人)

SORTContact函数部分(排序联系人信息)

优化

1.如果出现重名的联系人该怎么办?

2.把数据移动到堆上

3.保存数据

源代码

test.c

contact.h

contact.c


 

本笔记参考B站up鹏哥C语言


要求

1. 通讯录能够存放1000个人的信息

        每个人的信息:

        名字+年龄+性别+电话+地址

2. 增加人的信息

3. 删除指定人的信息

4. 修改指定人的信息

5. 查找指定人的信息

6. 排序通讯录的信息

思路

前期的准备

1.设置类型存储通讯录内的数据

contact.h 内部(此处代码仅供参考)

#define MAX_NAME 20
#define MAX_SEX 10
#define MAX_TELE 12
#define MAX_ADDR 30

//类型的定义
typedef struct PeoInfo
{
	char name[MAX_NAME];
	char sex[MAX_SEX];
	int age;
	char tele[MAX_TELE];
	char addr[MAX_ADDR];
}PeoInfo;

通过定义常量的方式方便后期的调试。

typedef 定义函数,方便之后使用。

2.规划流程

实现

创建通讯录

需要:

  1. 创建通讯录本体
  2. 需要知道通讯录中当前总共有几个元素(设置一个变量sz负责计数)
PeoInfo data[MAX];
int sz = 0;

但是在封装函数时会发现,如果使用sz计数,在传参时每次都要传入两个参数。这时候就会思考是否可以合并 PeoInfosz 这两个部分?

所以再封装一个结构体(MAX已提前定义):

//通讯录
typedef struct Contact
{
	PeoInfo data[MAX];//存储添加进来的人的信息
	int sz;//记录当前通讯录中有效信息的个数
}Contact;

此时也改变 test.c 内的结构体

Contact内部

初始化通讯录部分 

认为,刚刚创建好的通讯录内部应该没有数据,所以通讯录内部的 结构体PeoInfo 和 整型sz 应该被初始化。

首先在 test.c 内部写入函数 InitContact

InitContact(&con);
//选择传入 con 的地址,可以改动 con 内部的内容

再在 contact.h 内部声明函数

void InitContact(Contact* pc);

最后在 contact.c 内部首先该函数(需要包含头文件 contact.h

memset() - 内存设置函数,可以设置一块连续的内存。把指定空间的每个字节的内容初始化成指定的值(以字节为单位)。

void InitContact(Contact* pc)
{
	pc->sz = 0;
	memset(pc->data, 0, sizeof(pc->data));
}
//这种写法适用于较为复杂的初始化。

为什么可以直接使用 sizeof 计算字节个数?

分析:指针 pc->data 找到的是 data 这个数组的数组名,相当于将数组名单独放入 sizeof 内部,此时计算的是data这个数组的总大小。

用户选择部分

通过枚举类型的使用方便分支语句的选择,逻辑清晰

enum Option
{
	EXIT,
	ADD,
	DEL,
	SEARCH,
	MODIFY,
	SORT,
	PRI
};

可对应switch语句(用户选择)

switch (input)
{
    case ADD://增加信息
	break;
    case DEL://删除信息
	break;
    case SEARCH://查找信息
	break;
    case MODIFY://修改信息
	break;
    case SORT://排序
	break;
    case PRINT://打印信息
	break;
    case EXIT://退出
        break;
    default:
        break;
}

ADDContact函数部分(增加联系人)

写入函数(test.c)→ 声明函数(contact.h) → 实现函数(contact.c

分析:

  • 当通讯录内没有信息时, sz = 0 ,此时把联系人的信息放入 data[0] 内部;
  • 如果有一个联系人,则 sz = 1 ,此时把联系人的信息放入 data[1] 内部;
  • ……

由此可以发现新增加的联系人的信息应该被放入 data[sz] 的内部(放入后 sz++)

另一方面,当通讯录满了之后就不可以放入数据了。

void ADDContact(Contact* pc)
{
	//处理通讯录满了的情况
	if (pc->sz == MAX)
	{
		printf("通讯录已满,无法添加\n");
		return;
	}

	//增加一个人的信息
	printf("请输入名字:");
	scanf("%s", pc->data[pc->sz].name);//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");
}

PRINTContact函数部分(打印联系人信息)

首先在 test.c 内部写入函数 PRINTCotact

PRINTContact(&con);//传入地址

这里选择传入地址,不是为了更改 con 内部的内容,而是因为结构体传地址效率会更高。

声明函数(contact.h) 

void PRINTContact(const Contact* pc);

因为并改变 con 内容,所以使用 const 修饰变量。

实现函数(contact.c

要求

  1. 先打印标题
    //先设计好要打印的标题格式(左对齐,长度不够的部分会用空格替代)
    printf("%-20s\t%-5s\t%-5s\t%-12s\t%-20s\n", "名字", "年龄", "性别", "电话", "地址");
  2. 再打印数据
void PRINTContact(const Contact* pc)
{
	int i = 0;
	//打印标题
	printf("%-20s\t%-5s\t%-5s\t%-12s\t%-20s\n", "名字", "年龄", "性别", "电话", "地址");
	//打印数据
	for ( i = 0; i < pc->sz; i++)
	{
		printf("%-20s\t%-5d\t%-5s\t%-12s\t%-20s\n",
			pc->data[i].name,
			pc->data[i].age,
			pc->data[i].sex,
			pc->data[i].tele, 
			pc->data[i].addr);
	}
}

FindByName函数部分(通过名字查找对象)

DELContact函数、SEARCHContact函数 和 MODIFYContact函数 都需要查找对象,所以统一使用函数FindByName进行查找。

需要:

  • 如果找到 - 返回查找到的对象的下标;
  • 如果没找到 - 返回 -1

该函数在 contact.c 内部被使用

int pos = FindByName(pc, name);//传入通讯录和要查找的对象的名字

所以也在 contact.c 内部创建:

static int FindByName(Contact* pc, char name[])//此处name是字符串
{
	int i = 0;
	for ( i = 0; i < pc->sz; i++)
	{
		if (strcmp(pc->data[i].name, name) == 0)//找到了
			return i;
	}
	return -1;//找不到
}

依旧是遍历通过通讯录的方式找到目标。同时,因为其他源文件内不需要使用该函数,所以加上 static 保护函数。

DELContact函数部分(删除联系人的信息)

写入函数(test.c)→ 声明函数(contact.h) → 实现函数(contact.c

 分析:

  1. 通讯录内部的信息为空时,不用删除。
  2. 要删除对象,首先对象需要存在,所以先要查找。接下来:

如果对象不存在,不用删除;

如果对象存在,执行删除操作。

要删除对象,只需要把要删除的对象后面的所有对象往前移动一个元素就可以了。如:

void DELContact(Contact* pc)
{
	char name[MAX_NAME];

	//检测通讯录是否为空
	if (pc->sz == 0)
	{
		printf("通讯录为空,无需删除\n");
		return;
	}

	printf("请输入要删除联系人的名字:");
	scanf("%s", name);

	//1.查找删除的对象
	//判断:对象是否存在
	int pos = FindByName(pc, name);
	if (pos == -1)
	{
		printf("要删除的联系人不存在\n");
		return;
	}

	//2.对象存在,执行删除操作
	int i = 0;
	for (i = pos; i < pc->sz - 1; i++)
	{
		pc->data[i] = pc->data[i + 1];
	}
	pc->sz--;
	printf("删除成功\n");
}

为什么此处的 for (i = pos; i < pc->sz - 1; i++) 内部是 sz - 1 呢?

但是当 i + 1 取到5时,访问的是这个数组的第6个元素,删除对象后元素都往前移动一个元素,本就无法访问下标为 i + 1 的那个元素。

SEARCHContact函数部分(查找指定联系人)

写入函数(test.c)→ 声明函数(contact.h) → 实现函数(contact.c

大部分代码和前面有重复,这里直接上代码

void SEARCHContact(Contact* pc)
{
	char name[MAX_NAME] = { 0 };
	printf("请输入要查找的联系人的名字:");
	scanf("%s", name);

	int pos = FindByName(pc, name);
	if (pos == -1)
	{
		printf("要查找的人不存在\n");
		return;
	}

	else
	{
		printf("%-20s\t%-5d\t%-5s\t%-12s\t%-20s\n",
			pc->data[pos].name,
			pc->data[pos].age,
			pc->data[pos].sex,
			pc->data[pos].tele,
			pc->data[pos].addr);
                        //打印部分和之前实现的PRINTContact相类似,但是没有循环
	}
}

MODIFYContact函数部分(修改指定联系人)

修改信息需要传输地址,在 test.c

MODIFYContact(&con);//传入地址

声明函数  contact.h

void MODIFYContact(Contact* pc);//与之前的函数类似

实现函数  contact.c

void MODIFYContact(Contact* pc)
{
	char name[MAX_NAME] = { 0 };
	printf("请输入要修改的联系人的名字:");
	scanf("%s", name);

	int pos = FindByName(pc, name);
	if (pos == -1)
	{
		printf("要查找的人不存在\n");
		return;
	}

	else
	{
		printf("请输入名字:");
		scanf("%s", pc->data[pos].name);//name是一个数组,故不用取地址
		printf("请输入年龄:");
		scanf("%d", &(pc->data[pos].age));
		printf("请输入性别:");
		scanf("%s", pc->data[pos].sex);
		printf("请输入电话:");
		scanf("%s", pc->data[pos].tele);
		printf("请输入地址:");
		scanf("%s", pc->data[pos].addr);
                printf("修改成功\n");
	}
}

可以看出,这一部分代码和之前的代码有很多重复的部分。

SORTContact函数部分(排序联系人信息)

写入函数(test.c)→ 声明函数(contact.h) → 实现函数(contact.c

复习 strcmp函数 的返回值

int strcmp ( const char * str1, const char * str2 );
  • str1 指向的字符串的对应字符的ASCII值 < str2 指向的字符串的时,返回一个小于0的值;
  • str1 指向的字符串和 str2 指向的字符串一致时,返回 0 ;
  • str1 指向的字符串的对应字符的ASCII值 > str2 指向的字符串的时,返回一个大于0的值。

首先是检测通讯录是否为空。

其次,通过冒泡排序把姓名下ASCII值较小的联系人排到前面。

void SORTContact(Contact* pc)
{
	if (pc->data[0].name == 0)
	{
		printf("通讯录为空,无法排序\n");
		return;
	}

	int i = 0;
	int j = 0;
	for (i = 0; i < pc->sz; i++)
	{
		for (j = 0; j < pc->sz - 1; j++)
		{
			PeoInfo tmp[] = { 0 };
			if (strcmp(pc->data[j].name, pc->data[j + 1].name) > 0)
			{
				tmp[0] = pc->data[j];
				pc->data[j] = pc->data[j + 1];
				pc->data[j + 1] = tmp[0];
			}
		}
	}
	printf("排序成功\n");
}

优化

1.如果出现重名的联系人该怎么办?

一种解决方法:

多列出一列行号,通过行号来区分不同的联系人。会需要处理重名的联系人的函数只有DELContact函数和MODIFYContact函数,这里只举例DELContact函数

  • 如果需要删除多个联系人,就需要循环了,因为进入时就需要执行一次,所以使用了do循环。
  • 按照上面的想法,就需要多打印一行行号,这时会发现需求的功能和SEARCHContact函数类似,所以直接改造SEARCHContact函数。
  • 因为之前定义的变量pos刚好可以指向行号,所以使用该变量书写行号。

这就是第一个问题,如何改变变量pos

pos = FindByName(pc, name, pos);
//……
pos++;

这里从函数FindByName入手,因为这样可以筛出每一个同名的联系人。

FindByName函数只改变了循环条件

for (; i < pc->sz; i++)

接下来要在SEARCHContact函数内部增加一行打印行号,同时改变循环结束的条件。

void SEARCHContact(Contact* pc, char name[])
{
	int pos = 0;
	pos = FindByName(pc, name, pos);

	if (pos == -1)
	{
		//打印内容
		return;
	}

	else
	{
		//打印内容

		while (1)
		{
                                //打印内容
				pos++;
			}
			else
				return;
		}
	}
}

改为else语句退出。

  • DELContact函数需要的就是按照用户输入的行号来删除联系人信息。

这里还有一个问题:如果用户只要求删除一个联系人,这种情况要怎么办?

上代码:

do
{		
//……


    pos = FindByName(pc, name, 0);
    if (pos == -1)//检测是否还有要求的联系人信息存在
    {
	printf("修改完毕\n");
	return;
    }

    printf("是否继续修改(Y/N):");
    scanf("%c", &ch);
    getchar();
    printf("退出成功\n");
} while (ch != 'N');

代码分为两部分:

1.检测联系人信息是否依然存在;

2.询问客户是否依旧修改。

2.把数据移动到堆上

为什么要把数据移动到堆上呢?

  • 因为可能一次性开辟了过大的空间,这些空间可能无法被用完:一开始用户可能只有10个联系人,之后才会慢慢增加,可能最后增加到100个联系人就是极限了。这种情况下空间就被浪费了。
  • 而另一边,可能1000个位置不够用,这怎么办?

这就需要动态内存开辟:当需要的时候再增加空间,使得使用更加高效。

设定:

  1. 通讯录初始化后,能存放3个人的信息;
  2. 当空间存放满后,增加2个信息;
  3. 之后每次增加2个。

写成代码:

//通讯录 - 动态版本
typedef struct Contact
{
	PeoInfo* data;//指向动态申请的空间,用来存放联系人信息
	int sz;//记录当前通讯录中有效信息的个数
	int capacity;//记录当前通讯录的最大容量
}Contact;

此时的 InitContact(&con); 只是创建了一个变量而已,其中的动态空间还没有开辟,其他参数也还没有。现在我们需:

  • 在堆区上给 data 申请一块连续的空间;
  • sz = 0;
  • capacity 初始化为当前的最大容量。

为了方便,多定义两个常量:

#define DEFAULY_SZ 3 - 默认存储信息数量
#define INC_SZ 2 - 默认每次增加存储空间大小

初始化函数的修改:

void InitContact(Contact* pc)
{
	pc->data = (PeoInfo*)malloc(DEFAULY_SZ*sizeof(PeoInfo));
	if (pc->data == NULL)//如果空间开辟失败
	{
		perror("InitContact");
		return;
	}
	pc->sz = 0;//初始化后默认为0
	pc->capacity = DEFAULY_SZ;
}

初始化后,得到:

现在思考那些函数会涉及通讯录满的情况?

  1. ADD - 增加存储空间
  2. EXIT - 退出时需要回收空间

ADDContact函数

if (pc->sz == MAX)
{
    printf("通讯录已满,无法添加\n");
    return;
}

现在不是通讯录已满,而是到达最大存储容量时需要增加容量:

//考虑增容
if (pc->sz == pc->capacity)
{
    PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(PeoInfo));
    if (ptr != NULL)
    {
	pc->data = ptr;
	pc->capacity += INC_SZ;
    }
    else
    {
	perror("ADDContact");
	printf("增加联系人失败\n");
	return;
    }
}

退出部分 - 增加:销毁通讯录(DESTORYConstact函数)

还是 写入函数(test.c)→ 声明函数(contact.h) → 实现函数(contact.c三部分

contact.c 内部实现函数:

//销毁通讯录
void DESTORYConstact(Contact* pc)
{
	free(pc->data);
	pc->data = NULL;
	pc->capacity = 0;
	pc->sz = 0;
}

ps:就算没有销毁通讯录,在程序结束时,也会把空间还给操作系统。

3.保存数据

  • 当通讯录退出时,把信息写入文件;
  • 当通讯录初始化时,加载文件的信息到通讯录中。

首先,在退出时需要保存通讯录,于是在 test.c 中,用户选择退出时增加一个 SaveContact函数

case EXIT://退出
	//保存信息到文件
	SaveContact(&con);
	//销毁通讯录
	DESTORYConstact(&con);
	printf("退出通讯录\n");
	break;

contact.h 中声明:

void SaveContact(Contact* pc);

contact.c 中实现

在存储数据方面,一行一行把数据放入 test.dat 中。

值得注意的是这里使用的是 fwrite函数 - 按照二进制的方式写入数据。

void SaveContact(Contact* pc)
{
    //以“写入”的方式代开文件
	FILE* pf = fopen("contact.dat", "w");
	if (pf == NULL)
	{
		perror("SaveContact");
		return;
	}

	//写文件
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		fwrite(pc->data + i, sizeof(PeoInfo), 1, pf);
	}

	//关闭文件
	fclose(pf);
	pf = NULL;
}

随便写点数据,看看结果:

因为是以二进制的方式写入,所以会存在看不懂的数据。

注意

现在还没有读取文件内部的信息,如果此时再次运行程序,会发现通讯录内部依旧是空的。所以增加写入数据的步骤(在初始化中)。

现在要求初始化 - InitContact函数 不只是创建通讯录,还应该可以加载文件(封装新的函数 - LoadContact)。

LoadContact函数可声明,也可以不声明,因为只在 contact.c 中使用。但为了后期调试方便,笔者还是进行了声明。

注意1 - fread函数的使用方式

在 fread函数 返回值的描述中,存在着这样一句话:返回成功读取的元素的总个数。

  • 如果放入8个元素,而每次读取5个元素,则 fread函数 第一次会返回 5,第二次却会返回 3

通过返回值,我们可以确定 fread函数 是否读完了所指向的文件。

注意2 - 如果通过读取数据在放入通讯录的方式,通讯录可能会出现溢出的现象。

所以要检测容量。发现增加容量被多次使用,考虑使用函数实现 - CheckContact

void CheckContact(Contact* pc)
{
	if (pc->sz == pc->capacity)
	{
		PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(PeoInfo));
		if (ptr != NULL)
		{
			pc->data = ptr;
			pc->capacity += INC_SZ;
			printf("增容成功\n");
		}
		else
		{
			perror("ADDContact");
			printf("增加联系人失败\n");
			return;
		}
	}
}

LoadContact函数

void LoadContact(Contact* pc)
{
	//以“读”的方式打开文件
	FILE* pf = fopen("contact.dat", "r");
	if (pf == NULL)
	{
		perror("LoadContact");
		return;
	}

	//读文件
	PeoInfo tmp = { 0 };
	while (fread(&tmp, sizeof(PeoInfo), 1, pf))
	{
		//增加容量
		CheckContact(pc);

		//放入
		pc->data[pc->sz] = tmp;
		pc->sz++;
	}

	//关闭文件
	fclose(pf);
	pf = NULL;
}

源代码

test.c

//1. 通讯录能够存放1000个人的信息
//		每个人的信息:
//		名字 + 年龄 + 性别 + 电话 + 地址
//2. 增加人的信息
//3. 删除指定人的信息
//4. 修改指定人的信息
//5. 查找指定人的信息
//6. 排序通讯录的信息

#include<stdio.h>
#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");
}

enum Option
{
	EXIT,
	ADD,
	DEL,
	SEARCH,
	MODIFY,
	SORT,
	PRINT
};

int main()
{
	int input = 0;
	char name[MAX_NAME] = { 0 };

	//创建通讯录
	Contact con;
	
	//初始化通讯录的函数 - 静态版本
	//InitContact(&con);//传入 con 的地址

	//初始化通讯录 - 动态版本
	InitContact(&con);

	do
	{
		menu();
		printf("请选择:");
		scanf("%d", &input);

		switch (input)
		{
		case ADD://增加信息
			ADDContact(&con);//传入地址
			break;
		case DEL://删除信息
			DELContact(&con);
			break;

		case SEARCH://查找信息
			printf("请输入要查找的联系人的名字:");
			scanf("%s", name);
			SEARCHContact(&con, name);
			break;

		case MODIFY://修改信息
			printf("请输入要修改的联系人的名字:");
			scanf("%s", name);
			MODIFYContact(&con, name);
			break;

		case SORT://排序
			SORTContact(&con);
			break;
		case PRINT://打印信息
			PRINTContact(&con);
			break;
		case EXIT://退出
			//保存信息到文件
			SaveContact(&con);
			//销毁通讯录
			DESTORYConstact(&con);
			printf("退出通讯录\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}

	} while (input);
	return 0;
}

contact.h


#include<stdio.h>
#include<string.h>
#include<stdlib.h>

#define MAX_NAME 20
#define MAX_SEX 10
#define MAX_TELE 12
#define MAX_ADDR 30

#define MAX 1000

#define DEFAULY_SZ 3
#define INC_SZ 2

//类型的定义
typedef struct PeoInfo
{
	char name[MAX_NAME];
	char sex[MAX_SEX];
	int age;
	char tele[MAX_TELE];
	char addr[MAX_ADDR];
}PeoInfo;

通讯录 - 静态版本
//typedef struct Contact
//{
//	PeoInfo data[MAX];//存储添加进来的人的信息
//	int sz;//记录当前通讯录中有效信息的个数
//}Contact;

//通讯录 - 动态版本
typedef struct Contact
{
	PeoInfo* data;//指向动态申请的空间,用来存放联系人信息
	int sz;//记录当前通讯录中有效信息的个数
	int capacity;//记录当前通讯录的最大容量
}Contact;

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

//增加联系人
void ADDContact(Contact* pc);

//打印联系人信息
void PRINTContact(const Contact* pc);

//删除联系人的信息
void DELContact(Contact* pc);

//查找指定联系人
void SEARCHContact(Contact* pc, char name[]);

//修改指定联系人信息
void MODIFYContact(Contact* pc, char name[]);

//排序联系人信息
void SORTContact(Contact* pc);

//销毁通讯录
void DESTORYConstact(Contact* pc);

//保存通讯录信息到文件
void SaveContact(Contact* pc);

//加载文件内容到通讯录中
void LoadContact(Contact* pc);

//增加容量
void CheckContact(Contact* pc);

contact.c

#define _CRT_SECURE_NO_WARNINGS
#include"contact.h"

初始化 - 静态版本
//void InitContact(Contact* pc)
//{
//	pc->sz = 0;
//
//	//memset() - 内存设置函数,可以设置一块连续的内存
//	memset(pc->data, 0, sizeof(pc->data));
//}

//销毁通讯录
void DESTORYConstact(Contact* pc)
{
	free(pc->data);
	pc->data = NULL;
	pc->capacity = 0;
	pc->sz = 0;
}

//保存数据
void SaveContact(Contact* pc)
{
	FILE* pf = fopen("contact.dat", "w");
	if (pf == NULL)
	{
		perror("SaveContact");
		return;
	}

	//写文件
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		fwrite(pc->data + i, sizeof(PeoInfo), 1, pf);
	}

	//关闭文件
	fclose(pf);
	pf = NULL;
}

//加载数据
void LoadContact(Contact* pc)
{
	//以“读”的方式打开文件
	FILE* pf = fopen("contact.dat", "r");
	if (pf == NULL)
	{
		perror("LoadContact");
		return;
	}

	//读文件
	PeoInfo tmp = { 0 };
	while (fread(&tmp, sizeof(PeoInfo), 1, pf))
	{
		//增加容量
		CheckContact(pc);

		//放入
		pc->data[pc->sz] = tmp;
		pc->sz++;
	}

	//关闭文件
	fclose(pf);
	pf = NULL;
}

//初始化 - 动态版本
void InitContact(Contact* pc)
{
	pc->data = (PeoInfo*)malloc(DEFAULY_SZ * sizeof(PeoInfo));
	if (pc->data == NULL)//如果空间开辟失败
	{
		perror("InitContact");
		return;
	}
	pc->sz = 0;//初始化后默认为0
	pc->capacity = DEFAULY_SZ;

	//加载文件
	LoadContact(pc);
}

void CheckContact(Contact* pc)
{
	if (pc->sz == pc->capacity)
	{
		PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(PeoInfo));
		if (ptr != NULL)
		{
			pc->data = ptr;
			pc->capacity += INC_SZ;
			printf("增容成功\n");
		}
		else
		{
			perror("ADDContact");
			printf("增加联系人失败\n");
			return;
		}
	}
}

增加信息 - 静态版本
//void ADDContact(Contact* pc)
//{
//	//处理通讯录满了的情况
//	if (pc->sz == MAX)
//	{
//		printf("通讯录已满,无法添加\n");
//		return;
//	}
//
//	//增加一个人的信息
//	printf("请输入名字:");
//	scanf("%s", pc->data[pc->sz].name);//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 ADDContact(Contact* pc)
{
	//考虑增容
	CheckContact(pc);

	//增加一个人的信息
	printf("请输入名字:");
	scanf("%s", pc->data[pc->sz].name);//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 PRINTContact(const Contact* pc)
{
	int i = 0;
	//打印标题
	printf("%-20s\t%-5s\t%-5s\t%-12s\t%-20s\n", "名字", "年龄", "性别", "电话", "地址");
	//打印数据
	for (i = 0; i < pc->sz; i++)
	{
		printf("%-20s\t%-5d\t%-5s\t%-12s\t%-20s\n",
			pc->data[i].name,
			pc->data[i].age,
			pc->data[i].sex,
			pc->data[i].tele,
			pc->data[i].addr);
	}
}

//寻找特定联系人
static int FindByName(Contact* pc, char name[], int i)
{
	for (; i < pc->sz; i++)
	{
		if (strcmp(pc->data[i].name, name) == 0)//找到了
			return i;
	}
	return -1;//找不到
}

//删除信息
void DELContact(Contact* pc)
{
	char name[MAX_NAME];
	int pos = 0;
	char ch = 0;

	//检测通讯录是否为空
	if (pc->sz == 0)
	{
		printf("通讯录为空,无需删除\n");
		return;
	}

	printf("请输入要删除联系人的名字:");
	scanf("%s", name);
	getchar();

	//1.查找删除的对象
	//判断:对象是否存在
	pos = FindByName(pc, name, pos);
	if (pos == -1)
	{
		printf("要删除的联系人不存在\n");
		return;
	}

	//2.对象存在,执行删除操作
	do
	{
		SEARCHContact(pc, name);
		printf("请输入需要删除的联系人所在的行号:");
		scanf("%d", &pos);
		getchar();

		int i = 0;
		for (i = pos; i < pc->sz - 1; i++)
		{
			pc->data[i] = pc->data[i + 1];
		}
		pc->sz--;
		printf("删除成功\n");

		pos = FindByName(pc, name, 0);
		if (pos == -1)
		{
			printf("删除完毕\n");
			return;
		}

		printf("是否继续删除(Y/N):");
		scanf("%c", &ch);
		getchar();
	} while (ch != 'N');

	if (pc->capacity > DEFAULY_SZ && pc->sz == pc->capacity - INC_SZ)
	{
		PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity - INC_SZ) * sizeof(PeoInfo));
		if (ptr != NULL)
		{
			pc->data = ptr;
			pc->capacity -= INC_SZ;
			printf("减容成功\n");
		}
		else
		{
			perror("DELCContact");
			printf("删除联系人失败\n");
			return;
		}
	}

	printf("退出成功\n");
}

//查找指定联系人
void SEARCHContact(Contact* pc, char name[])
{
	int pos = 0;
	pos = FindByName(pc, name, pos);

	if (pos == -1)
	{
		printf("要查找的人不存在\n");
		return;
	}

	else
	{
		printf("%s\t%-20s\t%-5s\t%-5s\t%-12s\t%-20s\n", "行号", "名字", "年龄", "性别", "电话", "地址");

		while (1)
		{
			pos = FindByName(pc, name, pos);
			if (pos != -1)
			{
				printf("%d\t%-20s\t%-5d\t%-5s\t%-12s\t%-20s\n",
					pos,
					pc->data[pos].name,
					pc->data[pos].age,
					pc->data[pos].sex,
					pc->data[pos].tele,
					pc->data[pos].addr);

				pos++;
			}
			else
				return;
		}
	}
}

//修改指定联系人
void MODIFYContact(Contact* pc, char name[])
{
	int pos = 0;
	char ch = 0;

	pos = FindByName(pc, name, pos);
	if (pos == -1)
	{
		printf("要查找的人不存在\n");
		return;
	}

	do
	{
		SEARCHContact(pc, name);
		printf("请输入需要修改的联系人所在的行号:");
		scanf("%d", &pos);
		getchar();

		printf("请输入名字:");
		scanf("%s", pc->data[pos].name);//name是一个数组,故不用取地址
		printf("请输入年龄:");
		scanf("%d", &(pc->data[pos].age));
		printf("请输入性别:");
		scanf("%s", pc->data[pos].sex);
		printf("请输入电话:");
		scanf("%s", pc->data[pos].tele);
		printf("请输入地址:");
		scanf("%s", pc->data[pos].addr);
		printf("修改成功\n");

		pos = FindByName(pc, name, 0);
		if (pos == -1)
		{
			printf("修改完毕\n");
			return;
		}

		getchar();
		printf("是否继续修改(Y/N):");
		scanf("%c", &ch);
		getchar();
	} while (ch != 'N');

	printf("退出成功\n");
}



//排序信息
void SORTContact(Contact* pc)
{
	if (pc->data[0].name == 0)
	{
		printf("通讯录为空,无法排序\n");
		return;
	}

	int i = 0;
	int j = 0;
	for (i = 0; i < pc->sz; i++)
	{
		for (j = 0; j < pc->sz - 1; j++)
		{
			PeoInfo tmp[] = { 0 };
			if (strcmp(pc->data[j].name, pc->data[j + 1].name) > 0)
			{
				tmp[0] = pc->data[j];
				pc->data[j] = pc->data[j + 1];
				pc->data[j + 1] = tmp[0];
			}
		}
	}
	printf("排序成功\n");
}
  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值