【 C语言初级项目:通讯录(结构体+枚举+指针)】

【 C语言初级项目:通讯录(结构体+枚举+指针)】,第1张

通讯录
    • 通讯录的基本信息定义
    • 通讯录运行的框架(菜单)
    • 通讯录的初始化
    • 通讯录的信息录入
    • 通讯录的信息打印
    • 通讯录信息的删除
    • 通讯录查找(以名字位关键字)
    • 通讯录信息清空
    • 通讯录信息的修改
    • 排序通讯录中的联系人(名字或年龄)
    • 通讯录的源码
      • Contact.h
      • Contact.cpp
      • test.cpp

通讯录的基本信息定义

通讯录的具体功能:记录联系人的具体信息(名字、电话、性别、年龄、住址等)。


因此我们用结构体来实现通讯录,但联系人不可能只有一个,应该有很多,可以用结构体数组来实现,但这并不能良好的体现出联系人信息、联系人个数,所以我们在用一个结构体将联系人信息、个数封装起来。


结构体的嵌套定义


#define MAX 1000
#define NAME_SIZE 20
#define SEX_SIZE 20
#define TELE_SIZE 20
#define ADDR_SIZE 20

//存放个人信息
typedef struct PeoInfo
{
	char name[NAME_SIZE];//名字
	int age;//年龄
	char sex[SEX_SIZE];//性别
	char tele[TELE_SIZE];//电话
	char addr[ADDR_SIZE];//地址
}PeoInfo;

//通讯录结构体(结构体的嵌套定义)
typedef struct Contact
{
	PeoInfo data[MAX];
	int sz = 0;
}Contact;

//通讯录功能
enum Option
{
	EXIT,
	ADD,
	DEL,
	SEARCH,
	MODIFY,
	SORT,
	CLEAR,
	PRINT
};

//修改功能
enum MoC
{
	NAME = 11,
	AGE,
	SEX,
	TELE,
	ADDR
};
通讯录运行的框架(菜单)

同时,通讯录也有很多的功能:添加联系人,删除联系人,修改联系人信息,清空联系人信息等,这里我们显然是得用Switch语句来处理的,但是case 1:,case 2:这种代码的可读性并不高,因此我们使用枚举常量

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

枚举的使用,将case1变为了case Add,这是不是更能让人清除的知道这部分代码的功能呢,维护起来也显得更容易。


void menu()
{
	printf("********************************\n");
	printf("***** 1.Add       2.Del    *****\n");
	printf("***** 3.Search    4.Modify *****\n");
	printf("***** 5.Sort      6.Clear  *****\n");
	printf("***** 7.Print      0.Exit  *****\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 SORT:
			SortContact(&con);
			break;
		case CLEAR:
			ClearContact(&con);
			break;
		case PRINT:
			PrintContact(&con);
			break;
		case EXIT:
			printf("通讯录已退出\n");
			break;
		default:
			printf("请重新输入\n");
			break;
		}
	} while (input);
}
int main()
{
	test();
	return 0;
}
通讯录的初始化

通讯录初始时没有联系人,所以大小(联系人个数)应该是0,联系人信息也是没有的,data也是0。


常规处理一般是用循环将data的元素一个一个置为0,如果要想代码显得更高级一点呢,我们可以使用一个库函数memset(内存 *** 作函数)

memset:是在一段内存块中填充某个给定的值。


因为它只能填充一个值,所以该函数的初始化为原始初始化,无法将变量初始化为程序中需要的数据。


用memset初始化完后,后面程序中再向该内存空间中存放需要的数据。


memset 一般使用“0”初始化内存单元,而且通常是给数组或结构体进行初始化。


//通讯录初始化
void InitContact(Contact* pc)
{
	pc->sz = 0;
	memset(pc->data, 0, sizeof(pc->data));
}
通讯录的信息录入

嵌套结构体指针的使用:pc是contact*的类型,先指向data数组,sz记录了联系人的个数,它的值也就是数组data的下标,所以再将pc指向sz,就能使用peoinfo里的成员变量了。


添加完后,不要忘记sz也要++


//录入信息
void  AddContact(Contact* pc)
{
	if (pc->sz == MAX)
	{
		printf("通讯录已满,无法添加\n");
		return;
	}
	printf("请输入名字\n");
	scanf("%s", pc->data[pc->sz].name);

	printf("请输入年龄\n");
	scanf("%d", & pc->data[pc->sz].age);

	printf("请输入性别\n");
	scanf("%s", pc->data[pc->sz].sex);

	printf("请输入电话\n");
	scanf("%s", pc->data[pc->sz].tele);

	printf("请输入地址\n");
	scanf("%s", pc->data[pc->sz].addr);

	pc->sz++;
	printf("添加成功\n");
}
通讯录的信息打印

利用for循环逐个打印。


//打印通讯录
void PrintContact(Contact* pc)
{
	assert(pc);

	printf("%-20s %-3s % -5s %-12s %-30s\n", 
		"姓名", "年龄", "性别", "电话", "地址");

	int i = 0;
	for (i = 0;i < pc->sz;i++)
	{
		printf("%-20s %-3d %-5s %-12s %-30s\n", pc->data[i].name,
			pc->data[i].age, pc->data[i].sex,
			pc->data[i].tele, pc->data[i].addr);
	}
}
通讯录信息的删除

删除某人,首先得先找到他。


用peoinfo里的成员变量即可(不考虑重名等类似的情况)。


如果是用名字来查找,本质是就是判断两个字符串是否相等,那可以直接使用库函数strcmp来判断。


然后将后面的逐个往前覆盖,这样该联系人的信息就被删除了。


//通过名字找到某人
int FindByName(Contact* pc, char name[])
{
	assert(pc);

	int i = 0;
	for (i = 0;i < pc->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[NAME_SIZE] = {0};
	printf("请输入要删除人的名字:>");
	scanf("%s", name);
	int pos = FindByName(pc, name);

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

	int j = 0;
	for (j = pos;j < pc->sz-1;j++)
	{
		pc->data[j] = pc->data[j + 1];
	}
	pc->sz--;
	printf("删除成功\n");
}
通讯录查找(以名字位关键字)

将此过程封装成一个函数后面的修改信息也需要查找到某人。


//通过名字查询某人的信息
void SearchContact(Contact* pc)
{
	char name[NAME_SIZE] = { 0 };
	printf("请输入要查找人的名字:>");
	scanf("%s", name);
	int pos = FindByName(pc, name);

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

	printf("%-20s %-3s % -5s %-12s %-30s\n",
		"姓名", "年龄", "性别", "电话", "地址");
	printf("%-20s %-3d %-5s %-12s %-30s\n", pc->data[pos].name,
		pc->data[pos].age, pc->data[pos].sex,
		pc->data[pos].tele, pc->data[pos].addr);
}
通讯录信息清空

其实只要把data里的全置为0,通讯录的内容也就清空了,这和通讯录的初始化是一样的。


//清空通讯录
void ClearContact(Contact* pc)
{
	pc->sz = 0;
	memset(pc->data, 0, sizeof(pc->data));
	printf("清除所有人成功\n");
}
通讯录信息的修改

个人信息有5种,所以得用switch来区分出名字或年龄等信息,然后再进行修改,再做一个menu,同时使用枚举,在调用前面的查找函数,这样修改信息的函数就完成了。


//修改功能
enum MoC
{
	NAME = 11,
	AGE,
	SEX,
	TELE,
	ADDR
};
//修改信息列表
void menu2()
{
	printf("********************************\n");
	printf("***** 11.姓名     12.年龄    *****\n");
	printf("***** 13.性别     14.电话    *****\n");
	printf("***** 15.地址               *****\n");
	printf("********************************\n");
}

//修改某人的通讯录信息
void ModifyContact(Contact* pc)
{
	char name[NAME_SIZE] = { 0 };
	printf("请输入要修改的人的名字:>");
	scanf("%s", name);
	int pos = FindByName(pc, name);

	if (pos == -1)
	{
		printf("要修改的人不存在\n");
		return;
	}
	menu2();
	int op = 0;
	printf("请选择:>");
	scanf("%d", &op);
	switch (op)
	{
	case NAME:
		scanf("%s", pc->data[pos].name);
		break;
	case AGE:
		scanf("%d", pc->data[pos].age);
		break;
	case SEX:
		scanf("%s", pc->data[pos].sex);
		break;
	case TELE:
		scanf("%s", pc->data[pos].tele);
		break;
	case ADDR:
		scanf("%s", pc->data[pos].addr);
		break;
	default:
		printf("输入错误\n");
		break;
	}
}
排序通讯录中的联系人(名字或年龄)

以名字排序来举例:

核心:

  1. 对名字排序(字符串大小比较)。


  2. 交换peoinfo里的所有成员变量。


由于比较的是字符串,而c语言又没string类型的变量,用常规的冒泡或者选择的话显然是非常麻烦的,而且嵌套的for是三层,是非常耗时的算法,若使用库函数qsort,排序是能得到很好的解决,但他仅交换名字的内容,也不可行。


既然两者单独是不行的,所以我们就把他俩综合一下,模拟库函数qsort的实现方法来写冒泡排序,这样交换时,我们可以直接在写一个可以实现整个结构体交换的函数,这两点就十分考验指针的使用能力!

swap:结构体的整体交换,结构体本身(整体,包括里面由于对齐浪费的空间)是一块连续的空间,所以可以逐个字节进行交换,用char*的指针,将结构体的字节数传入即可。


模拟qsort的冒泡排序里面使用了函数指针,使其t可以调用cmp函数来进行比较。


比较的是name的大小,两个name之间相差了一个完整的结构体,使用只需找到第一个元素的name,后面的加上结构体的大小就可以找到。


本部分主要是函数指针的使用,利用char*指针来实现字节为单位的交换、读取。


//通讯录的排序
void swap(char* buf1, char* buf2, int width)
{
	int i = 0;
	for (i = 0; i < width; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}

}


void menu3()
{
	printf("请选择按照何种方式排序:\n");
	printf("1.姓名      2.年龄      \n");
	printf("      0.exit            \n");
}

int cmp_name(const void* s1, const void* s2)
{
	return strcmp(((PeoInfo*)s1)->name, ((PeoInfo*)s2)->name);
}

int cmp_age(const void* s1, const void* s2)
{
	return ((PeoInfo*)s1)->age - ((PeoInfo*)s2)->age;
}

void bubble_sort(void* base, int sz, int width, int(*cmp)(const void* s1, const void* s2))
{
	int i = 0;
	int j = 0;
	for (i = 0; i < sz - 1; i++)
	{
		for (j = 0; j < sz - 1 - i; j++)
		{
			if (cmp((char*)base + width * j, (char*)base + width * (j + 1)) > 0)
			{
				//交换
				swap((char*)base + width * j, (char*)base + width * (j + 1), width);
			}
		}
	}

}

void SortContact(Contact* pc)
{
	int input = 0;
	menu3();
	scanf("%d", &input);
	switch (input)
	{
	case 1:
		bubble_sort(pc->data, pc->sz, sizeof(PeoInfo), cmp_name);
		break;
	case 2:
		bubble_sort(pc->data, pc->sz, sizeof(PeoInfo), cmp_age);
		break;
	case 0:
		break;
	default:
		printf("请重新选择!\n");
	}
	printf("排序成功!\n");
}
通讯录的源码 Contact.h
#pragma once

#include
#include
#include
#include

#define MAX 1000
#define NAME_SIZE 20
#define SEX_SIZE 20
#define TELE_SIZE 20
#define ADDR_SIZE 20

//存放个人信息
typedef struct PeoInfo
{
	char name[NAME_SIZE];//名字
	int age;//年龄
	char sex[SEX_SIZE];//性别
	char tele[TELE_SIZE];//电话
	char addr[ADDR_SIZE];//地址
}PeoInfo;

//通讯录结构体
typedef struct Contact
{
	PeoInfo data[MAX];
	int sz = 0;
}Contact;

//通讯录功能
enum Option
{
	EXIT,
	ADD,
	DEL,
	SEARCH,
	MODIFY,
	SORT,
	CLEAR,
	PRINT
};

//修改功能
enum MoC
{
	NAME = 11,
	AGE,
	SEX,
	TELE,
	ADDR
};

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

//录入信息
void  AddContact(Contact* pc);

//打印通讯录
void PrintContact(Contact* pc);

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

//通过名字找到某人
int FindByName(Contact* pc, char name[]);

//通过名字查询某人的信息
void SearchContact(Contact* pc);

//清空通讯录
void ClearContact(Contact* pc);

//修改某人的通讯录信息
void ModifyContact(Contact* pc);

//修改信息列表
void menu2();

//排序所有人(名字或年龄)
void SortContact(Contact* pc);
Contact.cpp
#define _CRT_SECURE_NO_WARNINGS 1

#include"Contact.h"

//通讯录初始化
void InitContact(Contact* pc)
{
	pc->sz = 0;
	memset(pc->data, 0, sizeof(pc->data));
}

//录入信息
void  AddContact(Contact* pc)
{
	if (pc->sz == MAX)
	{
		printf("通讯录已满,无法添加\n");
		return;
	}
	printf("请输入名字\n");
	scanf("%s", pc->data[pc->sz].name);

	printf("请输入年龄\n");
	scanf("%d", & pc->data[pc->sz].age);

	printf("请输入性别\n");
	scanf("%s", pc->data[pc->sz].sex);

	printf("请输入电话\n");
	scanf("%s", pc->data[pc->sz].tele);

	printf("请输入地址\n");
	scanf("%s", pc->data[pc->sz].addr);

	pc->sz++;
	printf("添加成功\n");
}

//打印通讯录
void PrintContact(Contact* pc)
{
	assert(pc);

	printf("%-20s %-3s % -5s %-12s %-30s\n", 
		"姓名", "年龄", "性别", "电话", "地址");

	int i = 0;
	for (i = 0;i < pc->sz;i++)
	{
		printf("%-20s %-3d %-5s %-12s %-30s\n", pc->data[i].name,
			pc->data[i].age, pc->data[i].sex,
			pc->data[i].tele, pc->data[i].addr);
	}
}

//通过名字找到某人
int FindByName(Contact* pc, char name[])
{
	assert(pc);

	int i = 0;
	for (i = 0;i < pc->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[NAME_SIZE] = {0};
	printf("请输入要删除人的名字:>");
	scanf("%s", name);
	int pos = FindByName(pc, name);

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

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

//通过名字查询某人的信息
void SearchContact(Contact* pc)
{
	char name[NAME_SIZE] = { 0 };
	printf("请输入要查找人的名字:>");
	scanf("%s", name);
	int pos = FindByName(pc, name);

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

	printf("%-20s %-3s % -5s %-12s %-30s\n",
		"姓名", "年龄", "性别", "电话", "地址");
	printf("%-20s %-3d %-5s %-12s %-30s\n", pc->data[pos].name,
		pc->data[pos].age, pc->data[pos].sex,
		pc->data[pos].tele, pc->data[pos].addr);
}

//清空通讯录
void ClearContact(Contact* pc)
{
	pc->sz = 0;
	memset(pc->data, 0, sizeof(pc->data));
	printf("清除所有人成功\n");
}

//修改信息列表
void menu2()
{
	printf("********************************\n");
	printf("***** 11.姓名     12.年龄    *****\n");
	printf("***** 13.性别     14.电话    *****\n");
	printf("***** 15.地址               *****\n");
	printf("********************************\n");
}

//修改某人的通讯录信息
void ModifyContact(Contact* pc)
{
	char name[NAME_SIZE] = { 0 };
	printf("请输入要修改的人的名字:>");
	scanf("%s", name);
	int pos = FindByName(pc, name);

	if (pos == -1)
	{
		printf("要修改的人不存在\n");
		return;
	}
	menu2();
	int op = 0;
	printf("请选择:>");
	scanf("%d", &op);
	switch (op)
	{
	case NAME:
		scanf("%s", pc->data[pos].name);
		break;
	case AGE:
		scanf("%d", pc->data[pos].age);
		break;
	case SEX:
		scanf("%s", pc->data[pos].sex);
		break;
	case TELE:
		scanf("%s", pc->data[pos].tele);
		break;
	case ADDR:
		scanf("%s", pc->data[pos].addr);
		break;
	default:
		printf("输入错误\n");
		break;
	}
}



//通讯录的排序
void swap(char* buf1, char* buf2, int width)
{
	int i = 0;
	for (i = 0; i < width; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}

}


void menu3()
{
	printf("请选择按照何种方式排序:\n");
	printf("1.姓名      2.年龄      \n");
	printf("      0.exit            \n");
}

int cmp_name(const void* s1, const void* s2)
{
	return strcmp(((PeoInfo*)s1)->name, ((PeoInfo*)s2)->name);
}

int cmp_age(const void* s1, const void* s2)
{
	return ((PeoInfo*)s1)->age - ((PeoInfo*)s2)->age;
}

void bubble_sort(void* base, int sz, int width, int(*cmp)(const void* s1, const void* s2))
{
	int i = 0;
	int j = 0;
	for (i = 0; i < sz - 1; i++)
	{
		for (j = 0; j < sz - 1 - i; j++)
		{
			if (cmp((char*)base + width * j, (char*)base + width * (j + 1)) > 0)
			{
				//交换
				swap((char*)base + width * j, (char*)base + width * (j + 1), width);
			}
		}
	}

}

void SortContact(Contact* pc)
{
	int input = 0;
	menu3();
	scanf("%d", &input);
	switch (input)
	{
	case 1:
		bubble_sort(pc->data, pc->sz, sizeof(PeoInfo), cmp_name);
		break;
	case 2:
		bubble_sort(pc->data, pc->sz, sizeof(PeoInfo), cmp_age);
		break;
	case 0:
		break;
	default:
		printf("请重新选择!\n");
	}
	printf("排序成功!\n");
}

test.cpp
#define _CRT_SECURE_NO_WARNINGS 1

#include"Contact.h"

void menu()
{
	printf("********************************\n");
	printf("***** 1.Add       2.Del    *****\n");
	printf("***** 3.Search    4.Modify *****\n");
	printf("***** 5.Sort      6.Clear  *****\n");
	printf("***** 7.Print      0.Exit  *****\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 SORT:
			SortContact(&con);
			break;
		case CLEAR:
			ClearContact(&con);
			break;
		case PRINT:
			PrintContact(&con);
			break;
		case EXIT:
			printf("通讯录已退出\n");
			break;
		default:
			printf("请重新输入\n");
			break;
		}
	} while (input);
}
int main()
{
	test();
	return 0;
}

认认真真将指针、结构体、枚举的内容踏踏实实学懂,写一个简易通讯录是不难的。


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/562620.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-02
下一篇 2022-04-02

发表评论

登录后才能评论

评论列表(0条)

保存