【C语言练习】通讯录(结构体指针 + 动态内存)

文章目录

  • 相关知识
  • 1. contacts.h 头文件、函数/常量/结构体声明
  • 2. test.c 主界面菜单打印、菜单功能选项选择
  • 3. contacts.c 函数实现
  • 4. 实现时该注意的地方

相关知识

  1. 结构体
    文章:计算结构体的大小!结构体内存对齐的意义是什么?
    文章:结构体自引用与传参原则
  2. 动态内存(核心)
    文章:指针与动态内存
  3. 枚举
    文章:为什么建议使用枚举而不是#define?
  4. 函数指针与void*指针
    文章:利用void*、函数指针回调函数,模仿库函数qsort()将排序算法通用化,可以对任何数据排序。

1. contacts.h 头文件、函数/常量/结构体声明

#pragma once

#include 
#include 
#include 
#include 
#include 

// 姓名和地址长度
#define NAME_LEN 15
#define ADDR_LEN 25

// 通讯录大小(能存储多少个联系人)
//#define CONTACTS_SIZE 1000

// 通讯录默认容量以及默认扩容的容量
#define DEFAULT_CAPACITY 3


/* 联系人信息:姓名、性别、年龄、电话、住址。 */
typedef struct PeopleInfo
{
	/* 设计结构体最好将占用内存小的往前放 */
	char gender;
	short age;
	char tele[11];
	// calloc分配内存空间并初始化
	char* name;
	char* addr;
} PeopleInfo;


/* 通讯录信息 */
typedef struct Contacts
{
	// 用calloc初始化
	PeopleInfo* peopleInfo;
	// 当前通讯录联系人个数
	int peopleNums;
	// 当前最大容量
	int capacity;
} Contacts;


// 初始化通讯录
void InitContacts(Contacts* contacts);

// 添加联系人
void AddContacts(Contacts* contacts);

// 查找联系人
int SearchContactsByName(Contacts* contacts);

// 显示所有联系人
void ShowAllContacts(Contacts* contacts);

// 修改联系人
void ModifyByName(Contacts* contacts);

// 删除联系人
void DeleteContacts(Contacts* contacts);

// 对所有联系人按照某种方式排序
void SortContacts(Contacts* contacts);

// 销毁通讯录
void DestroyContacts(Contacts* contacts);

2. test.c 主界面菜单打印、菜单功能选项选择

#define _CRT_SECURE_NO_WARNINGS 1

/*
 *(1)实现一个通讯录;
	通讯录可以用来存储1000个人的信息,每个人的信息包括:姓名、性别、年龄、电话、住址
 *(2)提供方法:
	1.添加联系人信息
	2.删除指定联系人信息
	3.查找指定联系人信息
	4.修改指定联系人信息
	5.显示所有联系人信息
	6.清空所有联系人
	7.以名字排序所有联系人
*/

#include "contacts.h"

// static修饰,不将该函数暴露在外部
static void menu()
{
	printf("\n----- OPTIONS ----- OPTIONS ----- OPTIONS -----\n");
	printf(" ************** 1.ADD ********************* \n");
	printf(" ************** 2.DELETE ****************** \n");
	printf(" ************** 3.SEARCH ****************** \n");
	printf(" ************** 4.MODIFY ****************** \n");
	printf(" ************** 5.SHOW ALL **************** \n");
	printf(" ************** 6.DELETE ALL ************** \n");
	printf(" ************** 7.SORT ******************** \n");
	printf(" ************** 0.EXIT ******************** \n");
	printf("----- OPTIONS ----- OPTIONS ----- OPTIONS -----\n\n");
}


/* 菜单选项作为枚举常量表示 */
enum Option
{
	EXIT,
	ADD,
	DELETE,
	SEARCH,
	MODIFY,
	SHOW_ALL,
	DELETE_ALL,
	SORT
};


int main()
{
	/* 初始化通讯录 */
	Contacts contacts;
	InitContacts(&contacts);

	short input = 0;
	do 
	{
		menu();
		printf("请选择:");
		scanf("%hd", &input);
		getchar();
		/* 根据选项执行相应操作 */
		switch (input)
		{
		case ADD:
			printf("\nADD.\n");
			AddContacts(&contacts);
			break;

		case DELETE:
			printf("\nDELETE.\n");
			DeleteContacts(&contacts);
			break;

		case SEARCH:
			printf("SEARCH.\n");
			printf("Who do you want to search?\n");
			int index = SearchContactsByName(&contacts);
			if (index < 0)
			{
				printf("The contacts you just searched doesn't exist in your contacts.\n\n");
			}
			else 
			{
				printf("SEARCH SUCCEED!\n");
				printf("| %-20s | %s | %s | %-11s | %-40s |\n",
					"NAME", "GENDER", "AGE", " TELEPHONE ", "ADDRESS");
				printf("------------------------------------------------------------------------------------------------\n");
				printf("| %-20s | %3c    | %2hd  | %-11s | %-40s |\n\n",
					(contacts.peopleInfo + index)->name,
					(contacts.peopleInfo + index)->gender,
					(contacts.peopleInfo + index)->age,
					(contacts.peopleInfo + index)->tele,
					(contacts.peopleInfo + index)->addr);
			}
			break;

		case MODIFY:
			printf("\nMODIFY.\n");
			ModifyByName(&contacts);
			break;

		case SHOW_ALL:
			printf("\nSHOW ALL CONTACTS.\n");
 			ShowAllContacts(&contacts);
			break;

		case DELETE_ALL:
			printf("\nDELETE ALL CONTACTS.\n");
			DestroyContacts(&contacts);
			InitContacts(&contacts);
			printf("DELETE ALL CONTACTS SUCCEED!\n\n");
			break;

		case SORT:
			printf("\nSORT.\n");
			SortContacts(&contacts);
			break;

		case EXIT:
			printf("\nEXIT.\n");
			DestroyContacts(&contacts);
			break;

		default:
			printf("\nWRONG OPERATION.\n");
			break;
		}
	} while (input);

	return 0;
}

3. contacts.c 函数实现

#define _CRT_SECURE_NO_WARNINGS 1

#include "contacts.h"

/* getchar()目的为了把输入缓冲区的换行字符清空 */

static void initNameAndAddr(PeopleInfo* peopleInfo, int beginning, int end)
{
	for (int i = beginning; i < end; i++)
	{
		// 地址
		(peopleInfo + i)->addr = (char*)calloc(ADDR_LEN, sizeof(char));
		if ((peopleInfo + i)->addr == NULL)
		{
			perror("calloc peopleInfo->addr initPeopleInfo()");
			return;
		}
		// 姓名
		(peopleInfo + i)->name = (char*)calloc(NAME_LEN, sizeof(char));
		if ((peopleInfo + i)->name == NULL)
		{
			perror("calloc peopleInfo->name initPeopleInfo()");
			return;
		}
	}
}

/* 初始化通讯录(以动态内存方式) */
void InitContacts(Contacts* contacts)
{	
	// 最开始时联系人的个数和最大容量
	contacts->peopleNums = 0;
	contacts->capacity = DEFAULT_CAPACITY;

	/*
	* 给联系人信息分配内存并初始化
	* 注意:
	* 1.calloc会把姓名、年龄和电话的值初始化为0;
	* 2.calloc不会帮指针成员分配内存,即姓名和地址的内存要单独申请。
	*/
	contacts->peopleInfo = (PeopleInfo*)malloc(contacts->capacity * sizeof(PeopleInfo));
	if (contacts->peopleInfo == NULL)
	{
		perror("\ncalloc PeopleInfo - InitContacts()\n");
		return;
	}
	initNameAndAddr(contacts->peopleInfo, 0, contacts->capacity);
}

/* 销毁通讯录 */
void DestroyContacts(Contacts* contacts)
{
	/* 释放通讯录数据 */
	free(contacts->peopleInfo->addr);
	contacts->peopleInfo->addr = NULL;
	free(contacts->peopleInfo->name);
	contacts->peopleInfo->name = NULL;
	free(contacts->peopleInfo);
	contacts->peopleInfo = NULL;
}

static void CheckCapacity(Contacts* contacts)
{
	if (contacts->peopleNums == contacts->capacity)
	{	
		PeopleInfo* tmpPeopleInfo = realloc(contacts->peopleInfo,
			((contacts->capacity + DEFAULT_CAPACITY) * sizeof(PeopleInfo)));

		if (tmpPeopleInfo == NULL)
		{
			perror("\nrealloc peopleInfo - CheckCapacity()\n");
			return;
		}
		else
		{
			contacts->peopleInfo = tmpPeopleInfo;
			contacts->capacity += DEFAULT_CAPACITY;
			initNameAndAddr(contacts->peopleInfo, contacts->peopleNums, contacts->capacity);
		}
		printf("\nENLARGE CAPACITY SUCCEED!\n");
	}
}

/* 添加联系人 */
void AddContacts(Contacts* contacts)
{
	assert(contacts);
	CheckCapacity(contacts);

	PeopleInfo* peopleInfo = contacts->peopleInfo + contacts->peopleNums;

	printf("NAME:");
	scanf("%[^\n]s", peopleInfo->name);
	getchar();

	printf("GENDER:"); 
	scanf("%c", &(peopleInfo->gender));
	getchar();

	printf("AGE:");
	scanf("%hd", &(peopleInfo->age));
	getchar();

	printf("TELEPHONE:");
	scanf("%s", peopleInfo->tele);
	getchar();

	printf("ADDRESS:");
	scanf("%[^\n]s", peopleInfo->addr);
	getchar();

	(contacts->peopleNums)++;
	printf("\nADD SUCCEED!\n");
}


/*
 * 查找联系人
 * @返回值:如果不存在返回-1,找到返回下标。
*/
int SearchContactsByName(Contacts* contacts)
{
	char name[NAME_LEN];
	printf("\nPlease enter the contacts' name:");
	scanf("%[^\n]s", name);
	getchar();
	for (int i = 0; i < contacts->peopleNums; i++)
	{
		if (strcmp(name, (contacts->peopleInfo + i)->name) == 0)
		{
			return i;
		}
	}
	return -1;
}


/* 显示所有联系人 */
void ShowAllContacts(Contacts* contacts)
{
	printf("| %-20s | %s | %s | %-11s | %-40s |\n",
		"NAME", "GENDER", "AGE", " TELEPHONE ", "ADDRESS");
	printf("-----------------------------------------------------------------------------------------------------\n");
	for (int i = 0; i < contacts->peopleNums; i++)
	{
		printf("| %-20s | %3c    | %2hd  | %-11s | %-40s |\n",
			(contacts->peopleInfo + i)->name,
			(contacts->peopleInfo + i)->gender,
			(contacts->peopleInfo + i)->age,
			(contacts->peopleInfo + i)->tele,
			(contacts->peopleInfo + i)->addr);
	}
}


/* 修改联系人 */
void ModifyByName(Contacts* contacts)
{
	if (contacts->peopleNums <= 0)
	{
		printf("No contacts in your contacts book.\n\n");
		return;
	}
	assert(contacts->peopleInfo);
	printf("\nWhose information you want to modify?\n");
	int index = SearchContactsByName(contacts);
	if (index < 0)
	{
		printf("\nCannot find the information.\n\n");
		return;
	}

	PeopleInfo* peopleInfo = contacts->peopleInfo + index;

	printf("\nORIGINAL INFORMATION:\n");
	printf("| %-20s | %s | %s | %-11s | %-40s |\n",
		"NAME", "GENDER", "AGE", " TELEPHONE ", "ADDRESS");
	printf("------------------------------------------------------------------------------------------------\n");
	printf("| %-20s | %3c    | %2hd  | %-11s | %-40s |\n\n",
		peopleInfo->name,
		peopleInfo->gender,
		peopleInfo->age,
		peopleInfo->tele,
		peopleInfo->addr);

	/* 选择修改指定信息 */
	printf("----- MODIFICATION OPTIONS ----- \n");
	printf(" *********** 1.NAME *********** \n");
	printf(" *********** 2.GENDER *********** \n");
	printf(" *********** 3.AGE *********** \n");
	printf(" *********** 4.TELEPHONE *********** \n");
	printf(" *********** 5.ADDRESS *********** \n");
	printf(" *********** 0.EXIT MODIFICATION *********** \n");
	printf("----- MODIFICATION OPTIONS ----- \n\n");

	short input;
	do
	{
		printf("What information you want to modify?(Or 0 to exit):");
		scanf("%hd", &input);
		getchar();
		switch (input)
		{
		case 0:
			printf("\nEXIT MODIFICATION.\n");
			break;

		case 1:
			printf("\nMODIFY NAME:");
			scanf("%[^\n]s", peopleInfo->name);
			getchar();
			break;

		case 2:
			printf("\nMODIFY GENDER:");
			scanf("%c", &(peopleInfo->gender));
			getchar();
			break;

		case 3:
			printf("\nMODIFY AGE:");
			scanf("%hd", &(peopleInfo->age));
			getchar();
			break;

		case 4:
			printf("\nMODIFY TELEPHONE:");
			scanf("%s", peopleInfo->tele);
			getchar();
			break;

		case 5:
			printf("\nMODIFY ADDRESS:");
			scanf("%[^\n]s", peopleInfo->addr);
			getchar();
			break;

		default:
			printf("\nWRONG OPERATION.\n");
			break;
		}
	} while (input);

	printf("\nMODIFIED INFORMATION:\n");
	printf("| %-20s | %s | %s | %-11s | %-40s |\n",
		"NAME", "GENDER", "AGE", " TELEPHONE ", "ADDRESS");
	printf("------------------------------------------------------------------------------------------------\n");
	printf("| %-20s | %3c    | %2hd  | %-11s | %-40s |\n\n",
		peopleInfo->name,
		peopleInfo->gender,
		peopleInfo->age,
		peopleInfo->tele,
		peopleInfo->addr);
	printf("\nMODIFY SUCCEED!\n");
}


/* 删除联系人 */
void DeleteContacts(Contacts* contacts)
{
	if (contacts->peopleNums <= 0)
	{
		printf("You don't have any contacts.\n\n");
		return;
	}
	assert(contacts->peopleInfo);
	ShowAllContacts(contacts);
	printf("\nWho do you want to delete?\n");
	int index = SearchContactsByName(contacts);
	if (index < 0)
	{
		printf("\nThe contacts you just searched doesn't exist in your contacts.\n\n");
		return;
	}

	PeopleInfo* peopleInfo = contacts->peopleInfo;
	for (int i = index; i < contacts->peopleNums - 1; i++)
	{	// 只要成员一样,结构体可以直接赋值另一个结构体
		*(peopleInfo + i) = *(peopleInfo + i + 1);
	}
	contacts->peopleNums--;
	printf("\nDELETE SUCCEED!\n\n");
}


/* 根据年龄升序排序 */
static int CmpByAge(PeopleInfo* p1, PeopleInfo* p2)
{
	return p1->age - p2->age;
}
/* 根据姓名中的字母升序排序 */
static int CmpByName(PeopleInfo* p1, PeopleInfo* p2)
{
	return strcmp(p1->name, p2->name);
}
/* 对联系人排序 */
static void SortPeopleInfo(Contacts* contacts, int (*pfCmp)(PeopleInfo*, PeopleInfo*))
{
	PeopleInfo* peopleInfo = contacts->peopleInfo;
	int size = contacts->peopleNums;

	int sortedBorder = size - 1;
	int lastExchange = 0;
	for (int i = 0; i < size - 1; i++)
	{
		bool flg = true;
		for (int j = 0; j < sortedBorder; j++)
		{
			if (pfCmp(peopleInfo + j, peopleInfo + j + 1) > 0)
			{
				flg = false;
				PeopleInfo tmp = *(peopleInfo + j);
				*(peopleInfo + j) = *(peopleInfo + j + 1);
				*(peopleInfo + j + 1) = tmp;
				lastExchange = j;
			}

		}
		if (flg)
		{
			break;
		}
		sortedBorder = lastExchange;
	}
}
/* 按照选择的方式排序 */
void SortContacts(Contacts* contacts)
{
	printf("Sorting by what?Please Choose:\n");
	printf("********** 1. the age.\n");
	printf("********** 2. the name.\n\n");
	short input;
	scanf("%hd", &input);
	switch (input)
	{
	case 1:
		printf("Sorting by age...\n\n");
		SortPeopleInfo(contacts, CmpByAge);
		break;
	case 2:
		printf("Sorting by name...\n\n");
		SortPeopleInfo(contacts, CmpByName);
		break;

	default:
		printf("\nWRONG CHOICE.\n");
		break;
	}
	printf("The contacts information after sorting:\n\n");
	ShowAllContacts(contacts);
}

4. 实现时该注意的地方

  1. 核心设计【C语言练习】通讯录(结构体指针 + 动态内存)_第1张图片

  2. 初始化【C语言练习】通讯录(结构体指针 + 动态内存)_第2张图片
    【C语言练习】通讯录(结构体指针 + 动态内存)_第3张图片

  3. realloc扩容通讯录【C语言练习】通讯录(结构体指针 + 动态内存)_第4张图片

  4. 排序的设计【C语言练习】通讯录(结构体指针 + 动态内存)_第5张图片
    【C语言练习】通讯录(结构体指针 + 动态内存)_第6张图片
    【C语言练习】通讯录(结构体指针 + 动态内存)_第7张图片

  5. 删除全部信息【C语言练习】通讯录(结构体指针 + 动态内存)_第8张图片

  6. 释放动态内存【C语言练习】通讯录(结构体指针 + 动态内存)_第9张图片

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