C语言数据结构-基于动态顺序表实现通讯录

文章目录

  • 1 基础要求
  • 2 通讯录功能
    • 2.1 引入顺序表的文件
    • 2.2 定义联系人数据结构
    • 2.3 通讯录的初始化和销毁
    • 2.4 添加联系人
    • 2.5 删除联系人
    • 2.6 修改联系人
    • 2.7 查找联系人
    • 2.8 查看通讯录
  • 3 完整代码展示
    • 3.1 SeqList_copy.h
    • 3.2 SeqList_copy.c
    • 3.3 Contact.h
    • 3.4 Contact.c
    • 3.5 test.c


1 基础要求

C语言基础要求:结构体,动态内存管理,顺序表,文件操作

思路:利用已经完成的顺序表,对顺序表简易修改,应用到通讯录.

2 通讯录功能

通讯录只能能存100个人
用户信息:名字,性别,年龄,电话,地址
通讯录的展示,增,删,改,查,找.

2.1 引入顺序表的文件

我们已经完成了顺序表,我备份并且更名为SeqList_copy.hSeqList_copy.c
创建Contact.hContact.c以及测试代码功能的test.c
test.cContact.c引用的头文件都是Contact.hSeqList_copy.h

C语言数据结构-基于动态顺序表实现通讯录_第1张图片

2.2 定义联系人数据结构

Contact.h头文件中
定义名字最大长度,性别最大长度,电话最大长度,地址最大长度

#define NAME_MAX 120
#define SEX_MAX 10
#define TEL_MAX 20
#define ADDR_MAX 50
//定义结构体
typedef struct ContactInfo
{ 
	char name[NAME_MAX];
	char sex[SEX_MAX];
	int age;
	char tel[TEL_MAX];
	char addr[ADDR_MAX];
}CInfo;

//通讯录的底层是顺序表来实现
//给 SeqList 取名叫 contact ,更改名称
typedef struct SeqList contact;    

2.3 通讯录的初始化和销毁

在顺序表已经完成了初始化和销毁,这里我们直接套用

void ContactInit(contact* pcon)
{
	SLInit(pcon);    //直接调用我们已经实现的顺序表的方法
}

void ContactDestory(contact* pcon)
{
	SLInit(pcon);
}

2.4 添加联系人

我想要一步一步完成对联系人的添加,就分次加入,显得臃肿了.

我们获取的都是CInfo结构体要求的数据
name,sex,tel,addr都是字符串数组,整个数组代表数组的首元素地址,使用scanf,不需要再&取地址
但是age是int类型数组,使用scanf,仍需要&取地址

void ContactAdd(contact* pcon)
{
	CInfo info;
	printf("输入联系人姓名:\n");
	scanf("%s", info.name);    
	printf("输入联系人性别:\n");
	scanf("%s", info.sex);
	printf("输入联系人年龄:\n");
	scanf("%d", &info.age);   //这里注意,定义age是int类型数组,使用scanf,仍需要&取地址
	printf("输入联系人电话:\n");
	scanf("%s", info.tel);
	printf("输入联系人住址:\n");
	scanf("%s", info.addr);
	printf("输入完成\n\n");

	//往通讯录插入数据
	SLPushBack(pcon, info);
}

2.5 删除联系人

我这里通过姓名删除联系人,比较简易
要点1,创建FindByName函数,检查是否能通过名字找到.
要点2,利用顺序表的SLErase函数,观察它的参数,巧妙利用下标

创建FindByName函数

int FindByName(contact* pcon,char name[])
{
	for (int i = 0; i < pcon->size; i++)
	{
		if (strcmp(pcon->a[i].name, name) == 0)  //比较字符串姓名
		{
			return i;   //找到了就返回索引i
		}
	}
	return -1;  //没有找到就用-1代替
}
//删除联系人
void ContactDel(contact* pcon)
{
	printf("需要删除联系人的名字:\n");
	char name[NAME_MAX];
	scanf("%s", name);

	//找到人的下标记录一下
	int findindex = FindByName(pcon, name);
	//没找到联系人就是不存在
	if (findindex < 0) {
		printf("不存在!\n");
		return;
	}
	//这里找到联系人,删除findindex位置的数据 
	//参考函数void SLErase(SL* ps, int pos)  根据索引删除
	SLErase(pcon, findindex);
}

2.6 修改联系人

再次利用FindByName函数.通过姓名修改联系人

void ContactModify(contact* pcon)
{
	char name[NAME_MAX];
	printf("请输入您要修改的联系人:\n");
	scanf("%s", name);  //同样的道理,name本身是字符串数据,不用&

	//获取到的通讯录下标的位置find,利用数组修改
	int find = FindByName(pcon, name);
	if (find < 0)
	{
		printf("要修改的用户不存在\n");
		return;
	}
	printf("请输入新的用户名:\n");
	scanf("%s", pcon->a[find].name);
	printf("请输入新的用户性别\n");
	scanf("%s", pcon->a[find].sex);
	printf("请输入新的用户年龄:\n");
	scanf("%d", &pcon->a[find].age);   //age 是整型,我们要把地址传递过去
	printf("请输入新的用户电话:\n");
	scanf("%s", pcon->a[find].tel);
	printf("请输入新的用户地址:\n");
	scanf("%s", pcon->a[find].addr);

	printf("修改完成\n");
}

2.7 查找联系人

void ContactFind(contact* pcon)
{
	char name[NAME_MAX];
	printf("请输入要查找的用户名:\n");
	scanf("%s", name);
	int find = FindByName(pcon, name);
	if (find < 0)
	{
		printf("该联系人不存在\n");
		return;
	}
	//提前打印表头,便于查看
	printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "电话", "住址");
	printf(
		"%s %s %d %s %s\n",
		pcon->a[find].name, 
		pcon->a[find].sex,
		pcon->a[find].age, 
		pcon->a[find].tel, 
		pcon->a[find].addr
	);
}

2.8 查看通讯录

这个逻辑跟顺序表有点不一样,就重新编写了一份

void ContactShow(contact* pcon)
{
	//打印第一列header
	printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "电话", "住址");
	for (int i = 0; i < pcon->size; i++)
	{
		printf("%s %s %d %s %s\n", pcon->a[i].name, pcon->a[i].sex,
			pcon->a[i].age, pcon->a[i].tel, pcon->a[i].addr);
	}

}

3 完整代码展示

3.1 SeqList_copy.h

SeqList_copy.h 部分的代码,查找顺序表的函数,不适用于通讯录,就注释了
这一步一定注意更改动态顺序表数据类型为通讯录数据类型

#pragma once
#include
#include
#include
#include
#include
#include"Contact.h"    //添加头文件

//动态顺序表
//typedef int SLDataType;
//1. 更改动态顺序表数据类型为通讯录数据类型
typedef struct ContactInfo SLDataType;   //这里不能直接使用ContactInfo的别名 CInfo

//或者在这里再次命名,但是这样很多余
/*
//typedef struct ContactInfo CInfo;
//typedef CInfo SLDataType;
*/

typedef struct SeqList
{
	SLDataType* a;
	int size;	// 顺序表中有效的个数
	int capacity;	//顺序表当前的空间大小
}SL;	//简化结构体名称

//对顺序表进行初始化
void SLInit(SL* ps);
void SLDestory(SL* ps);


//头部,尾部  插入,删除
void SLPushBack(SL* ps, SLDataType x);
void SLPushFront(SL* ps, SLDataType x);
void SLPopBack(SL* ps);
void SLPopFront(SL* ps);

//打印SL
SLprint(SL* ps);

//判断SL是否为空
bool SLIsEmpty(SL* ps);

//在指定位置之前插入数据
void SLInsert(SL* ps, int pos, SLDataType x);

//删除指定位置的数据
void SLErase(SL* ps, int pos);

//查找数据
//bool SLFind(SL* ps, SLDataType x);

3.2 SeqList_copy.c

SLFind()函数不适用,我们的FindByName()函数是通过姓名查看姓名是否存在通讯录
SLPrint()函数同样不适用,所以才有ContactShow()函数

#include"SeqList_copy.h"

void SLInit(SL* ps) {
	ps->a = NULL;
	ps->size = ps->capacity = 0;
}

void SLDestroy(SL* ps) {
	if (ps->a)
		free(ps->a);
	ps->a = NULL;
	ps->size = ps->capacity = 0;
} 

void SLCheckCapacity(SL* ps) {
	if (ps->size == ps->capacity) {
		//空间不足以再额外插入一个数据
		//扩容
		int newCapcity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
		SLDataType* tmp = (SLDataType*)realloc(ps->a, newCapcity * sizeof(SLDataType));
		if (tmp == NULL) {
			perror("realloc fail!\n");
			return 1;
		}
		ps->a = tmp;
		ps->capacity = newCapcity;
	}
}

//尾插
void SLPushBack(SL* ps, SLDataType x) {
	//assert(ps != NULL);
	//暴力的方式
	assert(ps);
	//柔和方式
	//if (ps == NULL) {
	//	return;
	//}

	SLCheckCapacity(ps);
	//直接插入数据
	ps->a[ps->size++] = x;
}
//头插
void SLPushFront(SL* ps, SLDataType x) {
	assert(ps);
	//判断空间是否足够,不够则扩容
	SLCheckCapacity(ps);
	//空间足够,历史数据后移一位
	for (size_t i = ps->size; i > 0; i--)
	{
		ps->a[i] = ps->a[i - 1];
	}
	ps->a[0] = x;
	ps->size++;
}

//尾插
void SLPopBack(SL* ps) {
	//判断顺序表是否为空
	assert(ps);
	assert(!SLIsEmpty(ps));
	//ps->a[ps->size - 1] = 0;
	ps->size--;
}
void SLPopFront(SL* ps) {
	assert(ps);
	assert(!SLIsEmpty(ps));
	//让后面的数据往前挪动一位
	for (size_t i = 0; i < ps->size - 1; i++)
	{
		ps->a[i] = ps->a[i + 1];
	}
	ps->size--;
}

//void SLPrint(SL* ps) {
//	for (size_t i = 0; i < ps->size; i++)
//	{
//		printf("%d ", ps->a[i]);
//	}
//	printf("\n");
//}

bool SLIsEmpty(SL* ps) {
	assert(ps);
	//这样子是不对的,这里只能判断空间是否足够
	//return ps->size == ps->capacity;
	return ps->size == 0;
}

//在指定的位置之前插入数据
void SLInsert(SL* ps, int pos, SLDataType x) {
	assert(ps);
	//不要忘了对pos加以限制
	assert(pos >= 0 && pos <= ps->size);
	//扩容
	SLCheckCapacity(ps);
	//把pos位置及以后的数据往后挪动一位
	//循环条件里i的初始值是size还是size-1都是可以的,但不同的初始值对应不同的结束条件
	//for (int i = ps->size; i > pos; i--)
	//{
	//	//pos+1
	//	ps->a[i] = ps->a[i - 1];//pos->a[pos+1] = ps->a[pos]
	//}
	for (int i = ps->size - 1; i > pos - 1; i--)
	{
		//最后一次进来的i是pos
		ps->a[i + 1] = ps->a[i];//ps->a[pos+1] = ps->a[pos]
	}
	ps->a[pos] = x;
	ps->size++;
}
//删除指定位置的数据
void SLErase(SL* ps, int pos) {
	assert(ps);
	assert(!SLIsEmpty(ps));
	//pos的范围需要限制
	assert(pos >= 0 && pos < ps->size);

	for (int i = pos; i < ps->size - 1; i++)
	{
		//最后一次进来的i的数据ps->size-2
		ps->a[i] = ps->a[i + 1];//ps->a[ps->size-2] = ps->a[ps->size-1]
	}
	ps->size--;
}

//bool SLFind(SL* ps, SLDataType x) {
//	assert(ps);
//	for (int i = 0; i < ps->size; i++)
//	{
//		if (ps->a[i].name == x) {
//			//找到了
//			return true;
//		}
//	}
//	return false;
//}

3.3 Contact.h

#pragma once
//创建保存联系人数据的结构
#define NAME_MAX 120
#define SEX_MAX 10
#define TEL_MAX 20
#define ADDR_MAX 50

typedef struct ContactInfo
{ 
	char name[NAME_MAX];
	char sex[SEX_MAX];
	int age;
	char tel[TEL_MAX];
	char addr[ADDR_MAX];
}CInfo;

//通讯录的底层是顺序表来实现
//给 SeqList 取名叫 contact
typedef struct SeqList contact;    

//1.通讯录的初始化和销毁
void ContactInit(contact* pcon);
void ContactDestory(contact* pcon);

//2.添加联系人
void ContactAdd(contact* pcon);

//3.删除联系人
void ContactDel(contact* pcon);

//4.修改联系人
void ContactModify(contact* pcon);

//5.查找联系人
void ContactFind(contact* pcon);

//6.查看通讯录
void ContactShow(contact* pcon);

3.4 Contact.c

#define _CRT_SECURE_NO_WARNINGS
#include"SeqList_copy.h"
#include"Contact.h"


//1.通讯录的初始化和销毁
void ContactInit(contact* pcon)
{
	SLInit(pcon);    //直接调用我们已经实现的顺序表的方法
}

void ContactDestory(contact* pcon)
{
	SLInit(pcon);
}

//2.添加联系人
//我们获取的都是CInfo结构体要求的数据
//name,sex,tel,addr都是字符串数组,整个数组代表数组的首元素地址,使用scanf,不需要再&取地址
//但是age是int类型,使用scanf,仍需要&取地址
void ContactAdd(contact* pcon)
{
	CInfo info;
	printf("输入联系人姓名:\n");
	scanf("%s", info.name);    
	printf("输入联系人性别:\n");
	scanf("%s", info.sex);
	printf("输入联系人年龄:\n");
	scanf("%d", &info.age);
	printf("输入联系人电话:\n");
	scanf("%s", info.tel);
	printf("输入联系人住址:\n");
	scanf("%s", info.addr);
	printf("输入完成\n\n");

	//往通讯录插入数据
	SLPushBack(pcon, info);
}

//3.删除联系人
//我们这里通过姓名删除联系人

int FindByName(contact* pcon,char name[])
{
	for (int i = 0; i < pcon->size; i++)
	{
		if (strcmp(pcon->a[i].name, name) == 0)  //比较字符串姓名
		{
			return i;   //找到了就返回索引i
		}
	}
	return -1;  //没有找到就用-1代替
}

void ContactDel(contact* pcon)
{
	printf("需要删除联系人的名字:\n");
	char name[NAME_MAX];
	scanf("%s", name);

	//找到人的下标记录一下
	int findindex = FindByName(pcon, name);
	//没找到联系人就是不存在
	if (findindex < 0) {
		printf("不存在!\n");
		return;
	}
	//这里找到联系人,删除findindex位置的数据 
	//参考函数void SLErase(SL* ps, int pos)  根据索引删除
	SLErase(pcon, findindex);
}

//4.修改联系人
void ContactModify(contact* pcon)
{
	char name[NAME_MAX];
	printf("请输入您要修改的联系人:\n");
	scanf("%s", name);

	//获取到的通讯录下标的位置find,利用数组修改
	int find = FindByName(pcon, name);
	if (find < 0)
	{
		printf("要修改的用户不存在\n");
		return;
	}
	printf("请输入新的用户名:\n");
	scanf("%s", pcon->a[find].name);
	printf("请输入新的用户性别\n");
	scanf("%s", pcon->a[find].sex);
	printf("请输入新的用户年龄:\n");
	scanf("%d", &pcon->a[find].age);   //age 是整型,我们要把地址传递过去
	printf("请输入新的用户电话:\n");
	scanf("%s", pcon->a[find].tel);
	printf("请输入新的用户地址:\n");
	scanf("%s", pcon->a[find].addr);

	printf("修改完成\n");
}

//5.查找联系人
void ContactFind(contact* pcon)
{
	char name[NAME_MAX];
	printf("请输入要查找的用户名:\n");
	scanf("%s", name);
	int find = FindByName(pcon, name);
	if (find < 0)
	{
		printf("该联系人不存在\n");
		return;
	}
	
	printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "电话", "住址");
	printf(
		"%s %s %d %s %s\n",
		pcon->a[find].name, 
		pcon->a[find].sex,
		pcon->a[find].age, 
		pcon->a[find].tel, 
		pcon->a[find].addr
	);
}

//6.查看通讯录
void ContactShow(contact* pcon)
{
	//打印第一列header
	printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "电话", "住址");
	for (int i = 0; i < pcon->size; i++)
	{
		printf("%s %s %d %s %s\n", pcon->a[i].name, pcon->a[i].sex,
			pcon->a[i].age, pcon->a[i].tel, pcon->a[i].addr);
	}

}

3.5 test.c

#define _CRT_SECURE_NO_WARNINGS
#include"SeqList_copy.h"
#include"Contact.h"

void Contact_Test()  //测试函数
{
	contact con;
	ContactInit(&con);
	//以下是测试

	//添加联系人测试
	ContactAdd(&con); // 1 1 1 1 1 
	ContactAdd(&con); // 2 2 2 2 2
	ContactShow(&con);// 1 1 1 1 1 2 2 2 2 2

	//删除联系人测试
	//ContactDel(&con);
	//ContactShow(&con);

	//修改联系人测试
	//ContactModify(&con);   //1 3 3 3 3 3 
	//ContactShow(&con);	//3 3 3 3 3  2 2 2 2 2


	//查找联系人测试
	ContactFind(&con);

	//以上是测试

	ContactDestory(&con);
}


void menu()
{
	printf("****************************\n");
	printf("****************************\n");
	printf("************通讯录***********\n");
	printf("*********1.添加联系人********\n");
	printf("*********2.删除联系人********\n");
	printf("*********3.修改联系人********\n");
	printf("*********4.查找联系人********\n");
	printf("*********5.查看通讯录********\n");
	printf("*********0.退  出************\n");
	printf("****************************\n");
	printf("****************************\n");
}
int main()
{
	//Contact_Test();
	//测试完成,函数封装成菜单,在以下展示

	int op = 0;
	contact con;
	ContactInit(&con);
	do {
		menu();
		printf("请选择:\n");
		scanf("%d", &op);
		switch (op)
		{
		case 1:
			ContactAdd(&con);
			break;
		case 2:
			ContactDel(&con);
			break;
		case 3:
			ContactModify(&con);
			break;
		case 4:
			ContactFind(&con);
			break;
		case 5:
			ContactShow(&con);
			break;
		case 0:
			printf("退出了哈");
			break;
		default:
			printf("输入错误,请重新输入");
			break;
		}

	} while (op!=0);
	ContactDestory(&con);



	//system("Pause");
	return 0;
}

C语言数据结构-基于动态顺序表实现通讯录_第2张图片

你可能感兴趣的:(c语言,数据结构,开发语言)