引言

在现代社会中,通讯录作为一种重要的个人信息管理工具,广泛应用于手机、电脑等电子设备中。通讯录的基本功能包括添加联系人、删除联系人、查找联系人和更新联系人信息等。为了实现这些功能,我们可以采用多种数据结构,其中顺序表(即数组)是一种常见且高效的选择。本文将深入探讨使用顺序表实现通讯录的实现思路和原因。一起来看看吧!!!

重生之我在异世界学编程之C语言小项目:通讯录-LMLPHP


正文


一、通讯录系统的需求分析

此外,为了提高用户体验和系统的健壮性,我们还需要考虑以下几点:

二、顺序表的选择与优势

在实现通讯录系统时,有多种数据结构可供选择,如链表、树、图等。然而,考虑到通讯录的特性和需求,顺序表具有以下几个显著的优势:

  • 当然,顺序表也存在一些局限性,如插入和删除操作可能需要移动大量元素,但在这种应用场景下,由于联系人信息的变动相对较少,这一缺点并不明显。

三、通讯录系统的实现思路

1. 数据结构设计

首先,我们需要定义一个结构体来表示联系人信息。这个结构体可以包含以下字段:

typedef struct {
    char name[50];  // 姓名
    char phone[20]; // 电话号码
    // 可以根据需要添加其他字段,如地址、邮箱等
} Contact;

然后,我们定义一个顺序表来存储联系人信息:

#define MAX_CONTACTS 1000  // 最大联系人数量

typedef struct {
    Contact contacts[MAX_CONTACTS];  // 存储联系人信息的数组
    int size;                        // 当前联系人数量
} AddressBook;

2. 添加联系人

添加联系人时,我们首先检查通讯录是否已满。如果未满,则将新联系人信息添加到数组的末尾,并更新当前联系人数量。

 
void addContact(AddressBook *ab, const char *name, const char *phone) {
    if (ab->size >= MAX_CONTACTS) {
        printf("通讯录已满,无法添加新联系人!
");
        return;
    }
    strcpy(ab->contacts[ab->size].name, name);
    strcpy(ab->contacts[ab->size].phone, phone);
    ab->size++;
}

3. 删除联系人

删除联系人时,我们需要找到要删除的联系人在数组中的位置,并将其后的所有联系人向前移动一位以覆盖被删除的联系人。最后,更新当前联系人数量。

void deleteContactByName(AddressBook *ab, const char *name) {
   int i;
   for (i = 0; i < ab->size; i++) {
       if (strcmp(ab->contacts[i].name, name) == 0) {
           break;
       }
   }
   if (i >= ab->size) {
       printf("未找到名为 %s 的联系人!
", name);
       return;
   }
   for (; i < ab->size - 1; i++) {
       ab->contacts[i] = ab->contacts[i + 1];
   }
   ab->size--;
}

同样地,我们也可以按电话号码删除联系人,只需将查找条件改为电话号码即可。


4. 查找联系人

查找联系人时,我们遍历整个数组,比较每个联系人的姓名或电话号码是否与要查找的值匹配。如果找到匹配的联系人,则返回其位置;否则返回-1表示未找到。

 
int findContactByName(AddressBook *ab, const char *name) {
    for (int i = 0; i < ab->size; i++) {
        if (strcmp(ab->contacts[i].name, name) == 0) {
            return i;
        }
    }
    return -1;
}

int findContactByPhone(AddressBook *ab, const char *phone) {
    for (int i = 0; i < ab->size; i++) {
        if (strcmp(ab->contacts[i].phone, phone) == 0) {
            return i;
        }
    }
    return -1;
}

5. 更新联系人信息

更新联系人信息时,我们首先找到要更新的联系人在数组中的位置,然后修改其信息。

 
void updateContact(AddressBook *ab, const char *name, const char *newPhone) {
    int index = findContactByName(ab, name);
    if (index == -1) {
        printf("未找到名为 %s 的联系人!
", name);
        return;
    }
    strcpy(ab->contacts[index].phone, newPhone);
}
 

6. 显示所有联系人

显示所有联系人时,我们遍历整个数组,并打印出每个联系人的信息。

 
void displayContacts(AddressBook *ab) {
    for (int i = 0; i < ab->size; i++) {
        printf("姓名: %s, 电话号码: %s
", ab->contacts[i].name, ab->contacts[i].phone);
    }
}

四、实现理由的深入剖析

通过上述实现思路,我们可以看到使用顺序表来实现通讯录系统是合理且高效的。以下是对这一选择的深入剖析:


五、源码

(1)contact.h

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

#define _CRT_SECURE_NO_WARNINGS 1
#define NAME_MAX 20
#define SEX_MAX 6
#define SITE_MAX 20
#define TELE_MAX  12
//#define MAX 100
#define DEFAULT_SZ 3
#define ADD_SZ 2

//一个人的信息
typedef struct person {
	char name[NAME_MAX];    //名字
	int age;                //年龄
	char sex[SEX_MAX];      //性别
	char site[SITE_MAX];    //地址
	char tele[TELE_MAX];    //电话号码
}per;

//静态版本
通讯录的信息
//typedef struct contact {
//	int sz;                 //通讯录中联系人的数量
//	per arr[MAX];           //通讯录的联系人的信息
//}con;

//动态版本
//通讯录的信息
typedef struct contact {
	int sz;                 //通讯录中联系人的数量
	per* arr;               //通讯录的联系人的信息
	int capcity;            //通讯录的最大容量
}con;

//初始化通讯录
void initcontact(con* pc);

//显示通讯录信息
void Showcontact(const con* pc);

//查找联系人是否存在
int Findbyname(const con* pc, const char* name);

//增加联系人信息
void Addcontact(con* pc);

//删除联系人信息
void Delcontact(con* pc);

//查找联系人信息
void Searchcontact(const con* pc);

//修改联系人信息
void Modifycontact(con* pc);

//排序通讯录信息
void Sortcontact(con* pc);  

//按照名字对联系人进行排序
int compare_contact_by_Name(const void* e1, const void* e2);

//按照年龄对联系人进行排序
int compare_contact_by_Age(void* e1, const void* e2);

//按照性别对联系人进行排序
int compare_contact_by_Sex(const void* e1, const void* e2);

// 按照地址对联系人进行排序
int compare_contact_by_Site(const void* e1, const void* e2);

// 按照电话号码对联系人进行排序
int compare_contact_by_Tele(const void* e1, const void* e2);
 
//销毁通讯录
void Destory(con* pc);

//保存通讯录信息至文本
void Savecontact(con* pc);

//加载文本信息至通讯录
void loadcontact(con* pc);

(2)contact.c

#define _CRT_SECURE_NO_WARNINGS 1

#include"contact.h"
enum sort {
	Exit,
	Name,  
	Age,
	Sex,    
	Site, 
	Tele,
};
//静态版本
//初始化通讯录

//void initcontact(con* pc) {
//	assert(pc);
//	pc->sz = 0;
//	memset(pc->arr, 0, sizeof(pc->arr));      //memset()函数可以把一个给定数组的地址(第一个参数)的元素按照字节(字节数为第三个参数)的方式设置成一个数(第二个参数)
//}

//动态版本
//初始化通讯录

void initcontact(con* pc) {
	assert(pc);
	pc->sz = 0;
	per* ptr = (per*)calloc(DEFAULT_SZ , sizeof(per));       //calloc函数可以开辟一个指定元素个数(第一个参数)和单个元素大小(第二个参数)的空间
	if (ptr != NULL) {
		pc->arr = ptr;
	    pc->capcity = DEFAULT_SZ;
	}
	else {
		perror("initcontact");
	}
	//加载信息到通讯录
	loadcontact(pc);
}

//显示通讯录

void Showcontact(const con* pc) {
	assert(pc);
	printf("%-20s%-4s%-5s%-20s%-12s\n", "姓名","年龄", "性别", "地址","电话号码");
	int i = 0;
	for (; i < pc->sz; i++) {
		printf("%-20s%-4d%-5s%-20s%-12s\n", pc->arr[i].name, (pc->arr[i].age), pc->arr[i].sex, pc->arr[i].site, pc->arr[i].tele);
	}
}

//静态版本
增加联系人的信息
//
//void Addcontact(con* pc) {
//	assert(pc);
//	//先判断通讯录有没有满
//	if ((pc->sz) == 100) {
//		printf("通讯录已满,无法再存放联系人信息\n");
//		return;
//	}
//	//
//		printf("请输入您要输入的联系人姓名:>");
//		scanf("%s", pc->arr[pc->sz].name);
//		printf("请输入您要输入的联系人年龄:>");
//		scanf("%d", &(pc->arr[pc->sz].age));
//		printf("请输入您要输入的联系人性别:>");
//		scanf("%s", pc->arr[pc->sz].sex);
//		printf("请输入您要输入的联系人的地址:>");
//		scanf("%s", pc->arr[pc->sz].site);
//		printf("请输入您要输入的联系人的电话号码:>");
//		scanf("%s", pc->arr[pc->sz].tele);
//		(pc->sz)++;
//		printf("增加联系人成功\n");
//		return;
//}

//增加容量
void check_capcity(con* pc) {
	assert(pc);
	if (pc->sz == pc->capcity) {
		printf("开始增容\n");
		per* ptr =(per*) realloc(pc->arr, (pc->capcity + ADD_SZ) * sizeof(per));
		if (ptr != NULL){
			pc->arr = ptr;
			pc->capcity += ADD_SZ;
			printf("增容成功\n");
			return;
		}
		else {
			perror("check_capcity->realloc");
		}
	}
}
//动态版本
//增加联系人的信息

void Addcontact(con* pc) {
	assert(pc);
	//先判断通讯录有没有满,需不需要增容,要增容就增容
	check_capcity(pc);
	//
	printf("请输入您要输入的联系人姓名:>");
	scanf("%s", pc->arr[pc->sz].name);
	printf("请输入您要输入的联系人年龄:>");
	scanf("%d", &(pc->arr[pc->sz].age));
	printf("请输入您要输入的联系人性别:>");
	scanf("%s", pc->arr[pc->sz].sex);
	printf("请输入您要输入的联系人的地址:>");
	scanf("%s", pc->arr[pc->sz].site);
	printf("请输入您要输入的联系人的电话号码:>");
	scanf("%s", pc->arr[pc->sz].tele);
	(pc->sz)++;
	printf("增加联系人成功\n");
}

//查找联系人是否存在
Findbyname(const con* pc, const char* name) {
	assert(pc);
	int i = 0;
	for (i; i < pc->sz; i++) {
		if (strcmp(pc->arr[i].name, name) == 0) {
			return i;
		}
	}
	return -1;
}

//删除联系人
void Delcontact(con* pc){
	assert(pc);
	//先判断通讯录里还有没有联系人
	if (pc->sz == 0) {
		printf("通讯录里无联系人,无法删除\n");
		return;
	}

	//删除联系人
	//1.输入要查找的联系人
	char name[NAME_MAX] = { 0 };
	printf("请输入要删除的联系人的姓名:>");
	scanf("%s", name);

	//2.查找要删除的联系人
	int pos = Findbyname(pc, name);

	//3.删除联系人
	if (pos != -1) {
		for (int i = pos; i < pc->sz - 1; i++) {
			pc->arr[i] = pc->arr[i + 1];
		}
		printf("删除成功\n");
		return;
	}
	printf("您要删除的人不存在\n");
	return;
}

//查找联系人的信息
void Searchcontact(const con* pc) {
	assert(pc);
	//输入要查找的联系人
	printf("请输入要查找的联系人:>");
	char name[20];
	scanf("%s", name);
	//查找联系人是否存在
	int pos = Findbyname(pc, name);
	//打印联系人的信息
	if (pos != -1) {
		printf("%-20s%-4s%-5s%-20s%-12s\n", "姓名", "年龄", "性别", "地址", "电话号码");
		printf("%-20s%-4d%-5s%-20s%-12s\n", pc->arr[pos].name, pc->arr[pos].age, pc->arr[pos].sex, pc->arr[pos].site, pc->arr[pos].tele);
		return;
	}
	printf("您要修改的联系人不存在\n");
	return;
}

//修改联系人的信息
void Modifycontact(con* pc) {
	assert(pc);

	//输入要修改的联系人
	printf("请输入要修改的联系人:>");
	char name[20];
	scanf("%s", name);

	//查找联系人是否存在
	int pos = Findbyname(pc, name);

	//修改联系人的信息
	if (pos != -1) {
		printf("请输入您要修改后的信息:\n");
		printf("联系人姓名:>");
		scanf("%s", pc->arr[pc->sz].name);
		printf("联系人年龄:>");
		scanf("%d", &(pc->arr[pc->sz].age));
		printf("联系人性别:>");
		scanf("%s", pc->arr[pc->sz].sex);
		printf("联系人地址:>");
		scanf("%s", pc->arr[pc->sz].site);
		printf("联系人电话号码:>");
		scanf("%s", pc->arr[pc->sz].tele);
		printf("修改联系人成功\n");
		return;
	}
	printf("您要修改的联系人不存在\n");
	return;
}

//按照名字对联系人进行排序
int compare_contact_by_Name(const void* e1, const void* e2) {
	assert(e1 && e2);
	per* a = (per*)e1;
	per* b = (per*)e2;
	return a->name - b->name;
}

//按照年龄对联系人进行排序
int compare_contact_by_Age(const void* e1, const void* e2) {
	assert(e1 && e2);
	per* a = (per*)e1;
	per* b = (per*)e2;
	return a->age - b->age;
}

//按照性别对联系人进行排序
int compare_contact_by_Sex(const void* e1, const void* e2) {
	assert(e1 && e2);
	per* a = (per*)e1;
	per* b = (per*)e2;
	return a->sex - b->sex;
}

//按照地址对联系人进行排序
int compare_contact_by_Site(const void* e1, const void* e2) {
	assert(e1 && e2);
	per* a = (per*)e1;
	per* b = (per*)e2;
	return a->site - b->site;
}

//按照电话号码对联系人进行排序
int compare_contact_by_Tele(const void* e1, const void* e2) {
	assert(e1 && e2);
	per* a = (per*)e1;
	per* b = (per*)e2;
	return a->tele - b->tele;
}

//打印排序方式
void menu1(void) {
	printf("*****************************\n");
	printf("****1.Name    2.Age    ******\n");
	printf("****3.Sex     4.Site   ******\n");
	printf("****5.Tele    0.Exit   ******\n");
	printf("*****************************\n");
}

//排序通讯录信息
void Sortcontact(con* pc) {
	assert(pc);
	//选择排序方法
	int input = 0;
	do {
		menu1();
		printf("请输入您要选择的排序方法:>");
		scanf("%d", &input);
		size_t sz = sizeof(pc->arr) / sizeof(pc->arr[0]);
		size_t width = sizeof(pc->arr[0]);
		switch (input) {
		case Name:
			qsort(pc->arr, sz, width, compare_contact_by_Name);  //按照名字对联系人进行排序
			printf("排序成功\n");
			break;
		case Age:
			qsort(pc->arr, sz, width, compare_contact_by_Age);  //按照年龄对联系人进行排序
			printf("排序成功\n");
			break;
		case Sex:
			qsort(pc->arr, sz, width, compare_contact_by_Sex);  //按照性别对联系人进行排序
			printf("排序成功\n");
			break;
		case Site:
			qsort(pc->arr, sz, width, compare_contact_by_Site);  //按照地址对联系人进行排序
			printf("排序成功\n");
			break;
		case Tele:
			qsort(pc->arr, sz, width, compare_contact_by_Tele);  //按照电话号码对联系人进行排序
			printf("排序成功\n");
			break;
		case Exit:
			printf("退出\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (input);
	return ;
}

//销毁通讯录
void Destory(con* pc) {
	free(pc->arr);
	pc->arr = NULL;
	pc->capcity = 0;
	pc->sz = 0;
	pc = NULL;
}

//保存通讯录信息
void Savecontact(con* pc)
{
	assert(pc);
	//打开文件
	FILE* pf = fopen("contact.txt", "wb");
	if (NULL == pf) {
		perror("fopen");
		printf("打开文件失败\n");
	}
	//把内存信息写入文件
	else
	{
		int i = 0;
		for (i = 0; i < pc->sz; i++) {
			fwrite(pc->arr + i, 1, sizeof(per),pf);
		}
		printf("保存成功\n");
		//关闭文件
		fclose(pf);
		pf = NULL;
	}
}

//加载文件文信息
void loadcontact(con* pc) {
	assert(pc);
	//打开文件
	FILE* pf = fopen("contact.txt", "rb");
	if (NULL == pf) {
		perror("loadcontact");
		printf("打开文件失败\n");
	}
	else {
		//读取文件信息
		//创建一个临时结构体变量方便传值和判断
		per temp = { 0 };
		int i = 0;
		while (fread(&temp, 1, sizeof(per), pf) ){    //利用fread返回值判断文件里的信息是否被完全读取
			//增容
			check_capcity(pc);
			pc->arr[i] = temp;
			pc->sz++;
			i++;
		}
		printf("加载信息成功\n");
		fclose(pf);
		pf = NULL;
	}
}

(3)Test.c

#define _CRT_SECURE_NO_WARNINGS 1

#include"contact.h"
enum contact_function {
	Exit,
	Add = 1,
	Del = 2,
	Search,
	Modify,
	Show,
	Sort,
};
void menu() {
	printf("*****************************\n");
	printf("****1.Add     2.Del    ******\n");
	printf("****3.Search  4.Modify ******\n");
	printf("****5.Show    6.Sort   ******\n");
	printf("****      0.Exit       ******\n");
	printf("*****************************\n");
}
int main() {
	//创建通讯录
	con c;

	//初始化通讯录
	initcontact(&c);

	//选择通讯录的功能
	int input = 0;
	do {
		menu();
		printf("请输入您要选择的通讯录功能:>");
		scanf("%d", &input);
		switch (input) {
		case Add:
			Addcontact(&c);    //增加联系人信息
			break;
		case Del:
			Delcontact(&c);    //删除联系人信息
			break;
		case Search: 
			Searchcontact(&c); //查找联系人信息
			break;
		case Modify: 
			Modifycontact(&c); //修改联系人的信息
			break;
		case Show:
			Showcontact(&c);   //展示通讯录信息
			break;
		case Sort: 
			Sortcontact(&c);   //排序通讯录信息
			break;
		case Exit:             //先保存信息至文本再销毁
			Savecontact(&c);
			Destory(&c);
			printf("退出通讯录\n");  
			break;
		default: 
			printf("输入错误,请重新输入:>\n"); 
			break;
		}
	} while (input);
	return 0;
}

六、进一步优化与改进

尽管使用顺序表已经能够实现一个功能完善的通讯录系统,但在实际应用中,我们还可以进一步对其进行优化和改进:

不要担心,小编会在以后的日子里带着宝子们一起实现这个优化的!!!


七、结论

  • 综上所述,使用顺序表来实现通讯录系统是一种合理且高效的选择。它能够满足通讯录系统的基本需求并提供良好的性能和可扩展性。在实际应用中,我们还可以根据具体需求和场景对其进行优化和改进以提高系统的实用性和用户体验。

快乐的时光总是短暂,咱们下篇博文再见啦!!!不要忘了,给小编点点赞和收藏支持一下,在此非常感谢!!!

12-25 13:35