C/C++ 通讯录程序构思与实现

前言

前段时间完善了自己之前写的通讯录程序。程序用c语言进行编写,分为使用静态版本的v1版程序,和使用动态内存管理的v2版程序。
通讯录中存储的个人信息包括:姓名、性别、年龄、电话、住址。
要求通讯录包含以下功能:
//1 添加联系人信息
//2 删除指定联系人信息
//3 查找指定联系人信息
//4 修改指定联系人信息
//5 显示所有联系人信息
//6 清空所有联系人
//7 以名字排序所有联系人

v1-静态版本的通讯录程序:可以存储1000个联系人信息的通讯录功能实现

编写思路

编程使用vs2022完成。采用了分文件编写的思路,将程序分为自定义头文件contact.h,源文件contact.ctest.c。思路如下:

  1. contact.h包含程序所需的头文件和宏定义、对类型的声明和对函数的声明。
  2. contact.c包含对函数功能的具体实现,囊括了通讯录共7个功能的函数实现。
  3. test.c是测试用源文件,包含了通讯录程序的总体框架与main函数。

以下对v1版本通讯录程序进行具体介绍。

实现通讯录功能的前期准备

1. 我需要一个菜单

在拿到实现通讯录功能的这个题目后,我首先想到的是程序打开后呈现给使用者的交互界面。程序有义务为使用者说明自身功能并提供提示。因此在编写的开始,我需要一个菜单为使用者提供指引。

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

程序共7个功能,在菜单上一一罗列出来。并加入了退出选项。
程序运行效果如下:
菜单
为方便后续代码编写,menu()写在测试源文件test.c中。无需在头文件contact.h中进行声明。

2. 我需要多个接口

接下来是程序总体框架的搭建。在面对多个功能的选择时,选用 switch case语句是很容易想到的。
我需要创建一个输入值num,当输入不同的num值时,进入相对应的接口完成函数调用,从而实现相应的通讯录功能。
考虑到通讯录在使用时,存在需要多次输入、反复调用不同功能的情况,因此需要将switch case放进循环中。
为了调用菜单函数menu(),并完成对num的读取操作,使用do while循环以提供多次输入功能。
代码如下:

int num = 0;//输入值
do
	{
		menu();
		printf("请输入->");
		scanf("%d", &num);

		switch (num)
		{
		case 1:
			add_contact(&data);
			break;
		case 2:
			del_contact(&data);
			break;
		case 3:
			search_contact(&data);
			break;
		case 4:
			modify_contact(&data);
			break;
		case 5:
			print_contact(&data);
			break;
		case 6:
			init_contact(&data);
			break;
		case 7:
			sort_by_name_contact(&data);
			break;
		case 0:
			printf("退出通讯录。\n");
			break;
		default://switch语句的默认选项
			printf("请输入正确的num值。\n");
			break;
		}
	} while (num);
2.1 使用枚举类型增加代码可读性

值得注意的是,在case语句中,使用1、2、3的表示形式,容易在编写时造成可读性低的麻烦,从而导致不必要的编写错误。我选用声明一个枚举类型的方式,用ADD、DEL等字符串代替数字,增加程序的可读性。
枚举类型应写在头文件contact.h中,声明如下:

enum option
{
	//使用枚举,增加可读性
	EXIT,
	ADD,
	DEL,
	SEARCH,
	MODIFY,
	PRINT,
	CLEAR,
	SORT_BY_NAME
};

由枚举类型的语法定义知:第一个枚举成员的默认值为整型的 0,后续枚举成员的值在前一个成员上加 1。
EXIT == 0,ADD == 1,以此类推。
枚举类型的功能与宏定义功能一致,但代码更加简洁。
若用宏定义实现,代码如下:

#define EXIT 0
#define ADD 1
#define DEL 2
...

相比之下明显选择使用枚举类型更加简便。

至此,test.c文件中的程序总体框架基本搭建完毕。

3. 我需要声明并创建结构体

题目要求通讯录能够存储姓名、性别、年龄、电话、住址等信息,所以可创建结构体struct peoinfo ,对以上信息进行存储。并使用typedef对结构体名称进行简化。
此结构体声明应当放在头文件contact.h中。代码如下:

typedef struct peoinfo
{
	char name[20];
	char gender[10];
	int age;
	char tele[20];
	char address[20];
}peoinfo;

结构体类型peoinfo可以存储通讯录所需的各种信息。但值得注意的是,通讯录中应存在许多peoinfo结构体,用来存储不同人的信息。因此我还需要一个整形参数sz对通讯录中已存储的信息进行计数。
考虑到通讯录有7个或更多功能,需要编写多个函数。每次传入peoinfo数组和整形参数sz太过繁琐。因此将proinfo数组和整形参数sz封装进一个新构造体struct contact
声明如下:

typedef struct contact
{
	//创建通讯录
	peoinfo contact[Con_MAX];
	int sz;//目前已存储联系人数
	//注意在定义结构体时不能在结构体中进行初始化!!!
}contact;

peoinfo contact[Con_MAX];

为了方便修改通讯录可储存的信息个数,在头文件contact.h中使用宏定义,#define Con_MAX 1000
在后续的函数传参中,只需要传入contact类型变量的地址,就可以完成对通讯录中信息的修改了。
在进入do while循环前,创建结构体变量contact data;

3.1 不能在结构体声明中对结构体参数进行初始化

这里在一开始进行封装时我犯了一个错误,将sz在结构体声明中初始化了。在声明中只能出现int sz; ,而不能出现int sz = 0;

4. 对结构体data进行初始化

由于结构体contact中嵌套有结构体peoinfo,为方便后续可能存在的修改,并提高程序安全性,我需要单独写一个函数将contact data进行初始化。
函数在头文件contact.h中进行声明,在源文件contact.c中进行实现,在源文件test.c中进行调用。
函数实现如下:

void init_contact(contact* pc)
{
	pc->sz = 0;
	memset(pc->contact, 0, Con_MAX * sizeof(peoinfo));//初始化为0
}

由于结构体contact data过大,且需要修改data的值,函数使用指针传参。初始化整形变量sz为0,使用库函数memset对结构体所占内存的每一个字节进行置0操作。在这里记得要在contact.h中引用头文件<string.h>
初始化函数在创建结构体contact data后调用。

5. 对test()函数进行初步封装

现在我已经完成了除实现通讯录功能所需的函数之外的一切工作。在此,对test.c中的测试函数test()进行初步封装。其中所有函数调用均为预留,在这一步程序可以运行,但无法实现通讯录要求的任何功能。程序运行前需要将函数调用注释掉。
测试函数test()代码如下:

void test()
{
	//创建并初始化输入值
	int num = 0;

	//创建通讯录
	contact data;
	//初始化通讯录
	init_contact(&data);

	do
	{
		menu();
		printf("请输入->");
		scanf("%d", &num);

		switch (num)
		{
		case ADD:
			add_contact(&data);
			break;
		case DEL:
			del_contact(&data);
			break;
		case SEARCH:
			search_contact(&data);
			break;
		case MODIFY:
			modify_contact(&data);
			break;
		case PRINT:
			print_contact(&data);
			break;
		case CLEAR:
			init_contact(&data);
			printf("已清空通讯录。\n");
			break;
		case SORT_BY_NAME:
			sort_by_name_contact(&data);
			break;
		case 0:
			printf("退出通讯录。\n");
			break;
		default://switch语句的默认选项
			printf("请输入正确的num值。\n");
			break;
		}
	} while (num);
}

通讯录各功能的具体实现

本通讯录要求实现共7个功能,为每个功能编写相应的函数。
在头文件contact.h中进行函数声明,在源文件contact.c中进行函数实现,在test.c中进行函数调用。以下不再赘述。

1. 添加联系人

 void add_contact(contact* pc)
 {
	if (pc->sz == Con_MAX)
	{
		printf("通讯录已满。\n");
		return;
	}
	printf("添加第%d位联系人:>\n", pc->sz + 1);
	printf("请输入姓名:>");
	scanf("%s", pc->contact[pc->sz].name);
	printf("请输入性别:>");
	scanf("%s", pc->contact[pc->sz].gender);
	printf("请输入年龄:>");
	scanf("%d", &(pc->contact[pc->sz].age));
	printf("请输入电话:>");
	scanf("%s", pc->contact[pc->sz].tele);
	printf("请输入地址:>");
	scanf("%s", pc->contact[pc->sz].address);
	pc->sz++;

	printf("添加成功。\n");
}

首先判断通讯录是否已满,如通讯录已满,则输出提示**通讯录已满。**并退出添加联系人函数。
添加联系人的功能实现很简单,不过在编写时需要注意读入不同类型参数时,scanf()所需的类型说明符%d,%s。避免在细节上犯错。

2. 删除联系人

void del_contact(contact* pc)
{
	//根据姓名
	char name_tmp[20];
	printf("请输入想删除联系人的姓名:>");
	scanf("%s", name_tmp);

	//比对
	for (int i = 0; i < pc->sz; i++) {
		if (strcmp(name_tmp, pc->contact[i].name) == 0)
		{
			//将i+1位置挪至i位置
			for (int j = i; j < pc->sz - 1; j++)//注意是sz-1,如果j<sz,则会发生越界
			{
				pc->contact[j] = pc->contact[j + 1];
				memset(pc->contact + j + 1, 0, sizeof(peoinfo));
			}
			pc->sz--;
			printf("删除成功。\n");
			return;
		}
	}
}

我采用根据姓名删除相应的联系人。使用strcmp函数将输入的姓名与通讯录中所存储的姓名一一进行比对,此处不考虑重名的出现
删除联系人的底层逻辑是,将内存中后面存储的联系人信息统一向前移动一位,即使pc->contact[j + 1]处的数据存入pc->contact[j]中。并将pc->contact[j + 1]中的数据清空。
完成覆盖后,再记得将sz--

3. 查找联系人

void search_contact(contact* pc)
{
	//根据姓名
	char name_tmp[20];
	printf("请输入想查找联系人的姓名:>");
	scanf("%s", name_tmp);

	//比对
	for (int i = 0; i < pc->sz; i++) {
		if (strcmp(name_tmp, pc->contact[i].name) == 0)
		{
			printf("找到此人。\n");
			printf("%-20s %-5s %-5s %-16s %-30s\n", "姓名", "年龄", "性别", "电话", "住址");
			printf("%-20s %-5d %-5s %-16s %-30s\n", pc->contact[i].name, pc->contact[i].age, pc->contact[i].gender, pc->contact[i].tele, pc->contact[i].address);
			return;
		}
	}
	printf("此人未在通讯录中。\n");
}

我选择通过姓名查找的方式。数据的输入和比对与删除联系人函数类似。在找到想要查找的人后,对此人的全部信息进行打印。

printf(“%-20s %-5s %-5s %-16s %-30s\n”, “姓名”, “年龄”, “性别”, “电话”, “住址”);
printf(“%-20s %-5d %-5s %-16s %-30s\n”, pc->contact[i].name, pc->contact[i].age, pc->contact[i].gender, pc->contact[i].tele, pc->contact[i].address);

这里的%-20s是为了使显示出的数据对齐。%-20s表示左对齐20个字符,%20s表示右对齐20个字符。

printf(“%-20s %-5s %-5s %-16s %-30s\n”, “姓名”, “年龄”, “性别”, “电话”, “住址”);

在希望输出常量时,这样的写法给我启发。

4. 修改联系人信息

void modify_contact(contact* pc)
{
	//根据姓名
	char name_tmp[20];
	printf("请输入想修改联系人的姓名:>");
	scanf("%s", name_tmp);

	//比对
	for (int i = 0; i < pc->sz; i++) {
		if (strcmp(name_tmp, pc->contact[i].name) == 0)
		{
			int input_tmp = 0;
			do {
				printf("需要修改此人的哪一项信息?\n");
				printf("1.姓名 2.年龄 3.性别 4.电话 5.住址 0.不修改\n");
				scanf("%d", &input_tmp);
				char name[20];
				char gender[10];
				int age;
				char tele[20];
				char address[20];

				switch (input_tmp)
				{
				case 1:
					printf("请输入修改后的姓名:>");
					scanf("%s", name);
					//将原内容清空
					memset(pc->contact[i].name, 0, strlen(pc->contact[i].name));
					strncpy(pc->contact[i].name, name,strlen(name));
					printf("修改成功。\n");
					break;
				case 2:
					printf("请输入修改后的年龄:>");
					scanf("%d", &age);
					pc->contact[i].age = age;
					printf("修改成功。\n");
					break;
				case 3:
					printf("请输入修改后的性别:>");
					scanf("%s", gender);
					memset(pc->contact[i].gender, 0, strlen(pc->contact[i].gender));
					strncpy(pc->contact[i].gender, gender, strlen(gender));
					printf("修改成功。\n");
					break;
				case 4:
					printf("请输入修改后的电话:>");
					scanf("%s", tele);
					memset(pc->contact[i].tele, 0, strlen(pc->contact[i].tele));
					strncpy(pc->contact[i].tele, tele,strlen(tele));
					printf("修改成功。\n");
					break;
				case 5:
					printf("请输入修改后的地址:>");
					scanf("%s", address);
					memset(pc->contact[i].address, 0, strlen(pc->contact[i].address));
					strncpy(pc->contact[i].address, address,strlen(address));
					printf("修改成功。\n");
					break;
				case 0:
					printf("退出修改。\n");
					break;
				default:
					printf("请输入正确的值。\n");
					break;
				}
			} while (input_tmp);
			return;
		}
	}
	printf("查无此人。\n");
}

修改联系人信息的功能相对而言代码量大一些。采用了与整体框架相类似的do while语句和switch case语句的嵌套。
需要注意的也是细节之处。比如在将临时变量name[20]中存储的字符串拷贝至通讯录中存储的字符串空间pc->contact[i].name时,需要调用库函数strcpy,在第一次编写时,我犯了pc->contact[i].name =name 的错误,实属不该。
注意在修改前,应当将已有内容先进行清空,再调用strcpy函数。

5. 显示联系人信息

//显示通讯录
void print_contact(contact* pc)
{
	assert(pc);
	printf("%-20s %-5s %-5s %-16s %-30s\n", "姓名", "年龄", "性别", "电话", "住址");
	//当通讯录为空时
	if (pc->sz == 0)
	{
		printf("通讯录为空,请添加联系人后打印。\n");
		return;
	}
	for (int i = 0; i < pc->sz; i++)
	{
		printf("%-20s %-5d %-5s %-16s %-30s\n", pc->contact[i].name, pc->contact[i].age, pc->contact[i].gender, pc->contact[i].tele, pc->contact[i].address);
	}
	printf("打印完毕。\n");
}

其底层逻辑在上述查找联系人函数中已有说明,此处不再赘述。

6. 清空通讯录

此处直接调用初始化函数即可。

7. 根据姓名进行排序 - 调用库函数qsort

int compar_by_name(const void* pc1, const void* pc2)
{
	return strcmp(((peoinfo*)pc1)->name, ((peoinfo*)pc2)->name);
}
void sort_by_name_contact(contact* pc)
{
	qsort(pc->contact, pc->sz, sizeof(peoinfo), compar_by_name);
	printf("排序完毕。\n");
}
7.1 对qsort()函数的简介

此处调用了c语言库函数qsort,需要引用头文件<stdlib.h>。其函数声明为:

void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void , const void))

各参数的解释如下:

  1. base – 指向要排序的数组的第一个元素的指针。
  2. nitems – 由 base 指向的数组中元素的个数。
  3. size – 数组中每个元素的大小,以字节为单位。
  4. compar – 用来比较两个元素的函数。

其中compar函数是需要我们自己写的,在这里因为是根据姓名进行排序,所以我编写了比较函数int compar_by_name(const void* pc1, const void* pc2)
通过查阅MSDN,得知compar函数的判断逻辑如下:
在这里插入图片描述

compar函数的返回值规定为int类型,默认以升序进行排列。当返回值小于0时,表示第一个元素小于第二个元素,排序为元素1,元素2;当返回值为0时,不改变两元素排序;当返回值大于0时,排序为元素2,元素1

各功能测试视频

通讯录功能测试

通过视频可以看出,此程序各功能正常,均已实现。

V2-加入动态内存管理后的通讯录程序

编写思路

在学习了c语言中动态内存管理部分的知识后,引入头文件<stdlib.h>,使用库函数malloc、realloc、free等对V1版本的通讯录程序进行优化。
默认程序运行起来时,能存放3个人的信息(可以是任何值,设置为3方便测试),如通讯录存储已满,则再向内存申请两个peoinfo类型的空间。实现动态内存管理。

程序优化

1. struct contact 通讯录结构体改进

typedef struct contact
{
	//创建通讯录
	peoinfo* contact;
	int sz;//目前已存储联系人数
	int capacity;//记录通讯录当前可存储联系人数
}contact;

peoinfo* contact;

如需使用malloc函数进行动态内存开辟,则contact结构体中无需创建数组,而应该修改为创建一个peoinfo类型的指针,指向一段空间。在程序中使用malloc函数在堆区开辟空间。
创建整型变量capacity,是为了进行下一步的增容操作。每次完成增加联系人操作时,判断sz和capacity的大小关系。当sz == capacity时,进行增容。

2. init_contact(contact* pc)初始化结构体函数改进

修改果结构体struct contact后,相应的初始化函数也需要进行修改。这里即体现出将初始化结构体这一行为封装成函数的智慧,方便后续的修改和优化。
修改后函数如下:

// 初始化通讯录
void init_contact(contact* pc)
{
	pc->sz = 0;
	pc->capacity = 3;
	pc->contact = (peoinfo*)malloc(pc->capacity * sizeof(peoinfo));
	//开辟后进行判空操作
	if(pc->contact == NULL)
	{
		perror("init_contact::malloc");//表明在init_contact函数内部调用malloc时出现问题
		return;
	}
	memset(pc->contact, 0, pc->capacity * sizeof(peoinfo));//初始化为0

在此实现通讯录运行时,默认能够存储三个人信息。这里capacity的初始化数值也可用define定义为一个符号。

3. 动态内存管理的实现 - realloc

当通讯录中已存入3个人的信息时,使用realloc函数对通讯录进行扩容。应对增加联系人函数进行修改:

void add_contact(contact* pc)
{
	if (pc->sz == pc->capacity)
	{
		//printf("通讯录已满。\n");
		//return;
		peoinfo* tmp=(peoinfo*)realloc(pc->contact, (pc->capacity + 2) * sizeof(peoinfo));
		if(tmp != NULL)
		{
			pc->contact = tmp;
		}
		pc->capacity += 2;
	}

	//以下部分和静态版本一样
	printf("添加第%d位联系人:>\n", pc->sz + 1);
	printf("请输入姓名:>");
	scanf("%s", pc->contact[pc->sz].name);
	printf("请输入性别:>");
	scanf("%s", pc->contact[pc->sz].gender);
	printf("请输入年龄:>");
	scanf("%d", &(pc->contact[pc->sz].age));
	printf("请输入电话:>");
	scanf("%s", pc->contact[pc->sz].tele);
	printf("请输入地址:>");
	scanf("%s", pc->contact[pc->sz].address);
	pc->sz++;

	printf("添加成功。\n");
}

其中新添加的增容代码如下,可将其封装成一个函数:

void check_if_full(contact* pc)
{
	if (pc->sz == pc->capacity)
	{
		//printf("通讯录已满。\n");
		//return;
		peoinfo* tmp=(peoinfo*)realloc(pc->contact, (pc->capacity + 2) * sizeof(peoinfo));
		if(tmp != NULL)
		{
			pc->contact = tmp;
		}
		pc->capacity += 2;
		printf("增容成功");
	}
}

当调用增加联系人函数时,对通讯录可存储的联系人数有影响。此时加入此先判断再增容功能,完善程序的动态内存管理机制。每当通讯录存满,就又会新开辟两个peoinfo类型的内存空间。

4. 内存空间释放 - free

当退出通讯录时,对动态开辟的内存空间进行释放。
封装一个函数destroy_contact(),在exit时调用。

void destroy_contact(contact* pc)
{
	free(pc->contact);
	pc->contact = NULL;
	pc->sz = 0;
	pc->capacity = 0;
}

总结

总的来说,通讯录程序的实现,逻辑并不复杂。总代码量约在330行左右,锻炼了分文件编程思维,对string库函数的使用,动态内存管理函数malloc、free、realloc的使用,memset等内存管理函数的使用、qsort快排库函数的使用等。完善程序至今,我的代码编写、调试能力也得到很大提升。

源码

附V1版本源码如下,V2版本请参考文中内容。
供各位参考:
contact.h:

#pragma once
//头文件声明
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<assert.h>
//#define _CRT_SECURE_NO_WARNINGS 1

#define Con_MAX 1000

//类型声明
//个人信息结构体
typedef struct peoinfo
{
	char name[20];
	char gender[10];
	int age;
	char tele[20];
	char address[20];
}peoinfo;

//通讯录结构体,将数组和计数封装进一个结构体中
typedef struct contact
{
	//创建通讯录
	peoinfo contact[Con_MAX];
	int sz;//目前已存储联系人数
	//注意在定义结构体时不能在结构体中进行初始化!!!
}contact;

//枚举类型
enum option
{
	//使用枚举,增加可读性
	EXIT,
	ADD,
	DEL,
	SEARCH,
	MODIFY,
	PRINT,
	CLEAR,
	SORT_BY_NAME
};


//函数声明
//打印菜单
//void menu();
//初始化通讯录
void init_contact(contact* pc);
//添加联系人
void add_contact(contact* pc);

void del_contact(contact* pc);

void search_contact(contact* pc);

void modify_contact(contact* pc);

void print_contact(contact* pc);

int compar_by_name(const void* name1, const void* name2);//快排比较函数
void sort_by_name_contact(contact* pc);

contact.c:

#include"contact.h"
//函数实现
// 
// 初始化通讯录
void init_contact(contact* pc)
{
	pc->sz = 0;
	memset(pc->contact, 0, Con_MAX * sizeof(peoinfo));//初始化为0
}

//添加联系人
void add_contact(contact* pc)
{
	if (pc->sz == Con_MAX)
	{
		printf("通讯录已满。\n");
		return;
	}
	printf("添加第%d位联系人:>\n", pc->sz + 1);
	printf("请输入姓名:>");
	scanf("%s", pc->contact[pc->sz].name);
	printf("请输入性别:>");
	scanf("%s", pc->contact[pc->sz].gender);
	printf("请输入年龄:>");
	scanf("%d", &(pc->contact[pc->sz].age));
	printf("请输入电话:>");
	scanf("%s", pc->contact[pc->sz].tele);
	printf("请输入地址:>");
	scanf("%s", pc->contact[pc->sz].address);
	pc->sz++;

	printf("添加成功。\n");
}

//删除联系人
void del_contact(contact* pc)
{
	//根据姓名
	char name_tmp[20];
	printf("请输入想删除联系人的姓名:>");
	scanf("%s", name_tmp);

	//比对
	for (int i = 0; i < pc->sz; i++) {
		if (strcmp(name_tmp, pc->contact[i].name) == 0)
		{
			//将i+1位置挪至i位置
			for (int j = i; j < pc->sz - 1; j++)//注意是sz-1,如果j<sz,则会发生越界
			{
				pc->contact[j] = pc->contact[j + 1];
				memset(pc->contact + j + 1, 0, sizeof(peoinfo));
			}
			pc->sz--;
			printf("删除成功。\n");
			return;
		}
	}
}

//显示通讯录
void print_contact(contact* pc)
{
	assert(pc);
	printf("%-20s %-5s %-5s %-16s %-30s\n", "姓名", "年龄", "性别", "电话", "住址");
	//当通讯录为空时
	if (pc->sz == 0)
	{
		printf("通讯录为空,请添加联系人后打印。\n");
		return;
	}
	for (int i = 0; i < pc->sz; i++)
	{
		printf("%-20s %-5d %-5s %-16s %-30s\n", pc->contact[i].name, pc->contact[i].age, pc->contact[i].gender, pc->contact[i].tele, pc->contact[i].address);
	}
	printf("打印完毕。\n");
}

//查找联系人
void search_contact(contact* pc)
{
	//根据姓名
	char name_tmp[20];
	printf("请输入想查找联系人的姓名:>");
	scanf("%s", name_tmp);

	//比对
	for (int i = 0; i < pc->sz; i++) {
		if (strcmp(name_tmp, pc->contact[i].name) == 0)
		{
			printf("找到此人。\n");
			printf("%-20s %-5s %-5s %-16s %-30s\n", "姓名", "年龄", "性别", "电话", "住址");
			printf("%-20s %-5d %-5s %-16s %-30s\n", pc->contact[i].name, pc->contact[i].age, pc->contact[i].gender, pc->contact[i].tele, pc->contact[i].address);
			return;
		}
	}
	printf("此人未在通讯录中。\n");
}

//修改通讯录
void modify_contact(contact* pc)
{
	//根据姓名
	char name_tmp[20];
	printf("请输入想修改联系人的姓名:>");
	scanf("%s", name_tmp);

	//比对
	for (int i = 0; i < pc->sz; i++) {
		if (strcmp(name_tmp, pc->contact[i].name) == 0)
		{
			int input_tmp = 0;
			do {
				printf("需要修改此人的哪一项信息?\n");
				printf("1.姓名 2.年龄 3.性别 4.电话 5.住址 0.不修改\n");
				scanf("%d", &input_tmp);
				char name[20];
				char gender[10];
				int age;
				char tele[20];
				char address[20];

				switch (input_tmp)
				{
				case 1:
					printf("请输入修改后的姓名:>");
					scanf("%s", name);
					//将原内容清空
					memset(pc->contact[i].name, 0, strlen(pc->contact[i].name));
					strncpy(pc->contact[i].name, name,strlen(name));
					printf("修改成功。\n");
					break;
				case 2:
					printf("请输入修改后的年龄:>");
					scanf("%d", &age);
					pc->contact[i].age = age;
					printf("修改成功。\n");
					break;
				case 3:
					printf("请输入修改后的性别:>");
					scanf("%s", gender);
					memset(pc->contact[i].gender, 0, strlen(pc->contact[i].gender));
					strncpy(pc->contact[i].gender, gender, strlen(gender));
					printf("修改成功。\n");
					break;
				case 4:
					printf("请输入修改后的电话:>");
					scanf("%s", tele);
					memset(pc->contact[i].tele, 0, strlen(pc->contact[i].tele));
					strncpy(pc->contact[i].tele, tele,strlen(tele));
					printf("修改成功。\n");
					break;
				case 5:
					printf("请输入修改后的地址:>");
					scanf("%s", address);
					memset(pc->contact[i].address, 0, strlen(pc->contact[i].address));
					strncpy(pc->contact[i].address, address,strlen(address));
					printf("修改成功。\n");
					break;
				case 0:
					printf("退出修改。\n");
					break;
				default:
					printf("请输入正确的值。\n");
					break;
				}
			} while (input_tmp);
			return;
		}
	}
	printf("查无此人。\n");
}

//按照姓名排序
int compar_by_name(const void* pc1, const void* pc2)
{
	return strcmp(((peoinfo*)pc1)->name, ((peoinfo*)pc2)->name);
}
void sort_by_name_contact(contact* pc)
{
	qsort(pc->contact, pc->sz, sizeof(peoinfo), compar_by_name);
	printf("排序完毕。\n");
}

test.c:

#define _CRT_SECURE_NO_WARNINGS
#include"contact.h"

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

//菜单
void menu()
{
	printf("******************************************\n");
	printf("****               meun               ****\n");
	printf("****     1.add           2.del        ****\n");
	printf("****     3.search        4.modify     ****\n");
	printf("****     5.print         6.clear      ****\n");
	printf("****     7.sort_by_name  0.exit       ****\n");
	printf("******************************************\n");
}
void test()
{
	//测试函数
	int num = 0;

	//创建通讯录
	contact data;
	//初始化通讯录
	init_contact(&data);

	do
	{
		menu();
		printf("请输入->");
		scanf("%d", &num);

		switch (num)
		{
		case ADD:
			add_contact(&data);
			break;
		case DEL:
			del_contact(&data);
			break;
		case SEARCH:
			search_contact(&data);
			break;
		case MODIFY:
			modify_contact(&data);
			break;
		case PRINT:
			print_contact(&data);
			break;
		case CLEAR:
			init_contact(&data);
			printf("已清空通讯录。\n");
			break;
		case SORT_BY_NAME:
			sort_by_name_contact(&data);
			break;
		case 0:
			printf("退出通讯录。\n");
			break;
		default://switch语句的默认选项
			printf("请输入正确的num值。\n");
			break;
		}
	} while (num);
}

int main()
{
	test();
	return 0;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
《精通MATLAB与C/C++混合程序设计(第三版)》是一本介绍MATLAB与C/C++混合编程的经典教材。本书详细讲解了如何将MATLAB与C/C++相结合,实现更高效的算法和程序设计。 首先,MATLAB是一种强大的数学软件,具备较高的算法开发效率。而C/C++是一种高级编程语言,具备更好的底层控制能力。结合两者的优点,可以在MATLAB中用C/C++语言实现一些特定的高性能模块,提升程序的运行效率。 《精通MATLAB与C/C++混合程序设计(第三版)》对混合编程的方法进行了全面的介绍。首先,书中详细讲解了如何使用C/C++编写mex文件,将C/C++代码嵌入到MATLAB中,实现高效的函数接口。其次,书中介绍了如何在MATLAB中调用C/C++库函数,提升计算速度和内存使用效率。同时,书中还介绍了如何在C/C++中调用MATLAB引擎的接口,实现与MATLAB的交互。 通过学习本书,读者可以了解到MATLAB与C/C++的基本语法和编程技巧。同时,读者还可以学会如何优化MATLAB代码,将一些计算密集型的任务交给C/C++实现,提升程序的性能。此外,本书还介绍了如何进行错误处理和调试,以及如何进行程序性能分析和优化。 总之,精通MATLAB与C/C++混合程序设计对于那些希望在MATLAB中利用C/C++提升编程效率和性能的人来说,是一本非常实用的工具书。无论是学术界的研究人员还是工业界的程序开发人员,都可以通过阅读本书,提升自己的编程能力和项目实施效率。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值