目录
一、前言
通讯录是一种常见的应用程序,通常用于存储和管理联系人信息。在本篇博客中,我将介绍如何使用C语言编写一个简单的通讯录程序,并提供详细的实现思路和代码示例。
二、 通讯录实现思路
我们将使用三个文件来实现通讯录功能分别是test.c、contact.c和contact.h。
test.c是主体,是通讯录功能的测试逻辑;
contact.c是通讯录的实现逻辑;
contact.h是实现通讯录所需的函数声明和结构体定义等。
通讯录有以下功能
增加联系人:用户输入联系人信息,程序将信息保存到一个结构体中,然后将该结构体添加到通讯录列表中。
读入数据:从保存通讯录信息的文件中读取数据,并将数据存储到结构体数组中。
删除联系人:用户输入要删除的联系人姓名,程序查找该联系人并将其从通讯录列表中删除。
修改联系人:用户输入要修改的联系人姓名,程序查找该联系人并允许用户修改其信息。
查询联系人:用户输入要查询的联系人姓名,程序查找该联系人并输出其信息。
排序联系人: 程序将联系人按名字进行排序
三、 通讯录的具体实现
1.前期准备
菜单的实现
建立菜单主要是实现使计算机可以与用户进行交互。因此建立菜单方便用户进行操作。
void menu() {
// 显示菜单
printf("请选择要执行的操作:\n");
printf("1. 添加联系人信息\n");
printf("2. 删除指定联系人信息\n");
printf("3. 查找指定联系人信息\n");
printf("4. 修改指定联系人信息\n");
printf("5. 显示所有联系人信息\n");
printf("6. 清空所有联系人\n");
printf("7. 以名字排序所有联系人\n");
printf("0. 退出程序\n");
}
运行效果
全局变量的定义
利用宏定义来定义这些变量是为了后续改变长度的方便
#define MAX_PERSONS 1000 // 最大联系人数量
#define MAX_NAME_LEN 20 // 姓名最大长度
#define MAX_ADDRESS_LEN 100 // 地址最大长度
#define MAX_PHONE_LEN 15 // 电话号码最大长度
存储联系人的结构体的实现
因为联系人的个人信息的类型不同,所以这里我们使用结构体来实现我们的目的。
// 联系人结构体
typedef struct Person{
char name[MAX_NAME_LEN]; // 姓名
char gender; // 性别
int age; // 年龄
char phone[MAX_PHONE_LEN]; // 电话号码
char address[MAX_ADDRESS_LEN]; // 地址
} Person;
typedef struct Contact {
Person contacts[MAX_PERSONS]; // 存储所有联系人信息的数组
int count = 0; // 当前联系人数量
}Contact;
第一个Person的结构体进行联系人个人信息的存储
第二个Contact的结构体contacts作为存储所有联系人信息的数组,count记录
当前联系人数量。
菜单选项功能的实现
这里我们使用枚举变量来定义主要是为了增加代码的可读性,然后利用switch进行选择从而进行操作。
enum Option
{
EXIT,//0
ADD,//1
DEL,//2
SEARCH,//3
MODIFY,//4
SHOW,//5
DEL_ALL,//6
SORT//7
};
int main() {
int choice = 0;
Contact con;
while (1) {
menu();
printf("请选择操作:");
scanf("%d", &choice);
switch (choice) {
case EXIT:
// 退出程序
return 0;
case ADD:
// 添加联系人信息
add_person(&con);
break;
case DEL:
// 删除指定联系人信息
remove_person(&con);
break;
case SEARCH:
// 查找指定联系人信息
find_person(&con);
break;
case MODIFY:
// 修改指定联系人信息
modify_person(&con);
break;
case SHOW:
// 显示所有联系人信息
list_persons(&con);
break;
case DEL_ALL:
// 清空所有联系人
clear_persons(&con);
break;
case SORT:
// 按照姓名排序所有联系人
sortContactsByName(&con);
break;
default:
printf("无效的选择,请重新输入。\n");
break;
}
}
}
2.函数各个功能的实现
2.1 通讯录的初始化
因为结构体内存的全是随机值,为了防止后面出现bug,所以在这里我们要进行初始化
//通讯录的初始化
void init_con(Contact* pc) {
assert(pc);
pc->count = 0;
memset(pc->contacts, 0, sizeof(pc->contacts));
}
2.2 添加联系人信息
这里进行Contact结构体里数组每个元素进行访问即可,然后存入数组。
// 添加联系人信息
void add_person(Contact* pc) {
if (pc->count == MAX_PERSONS) {
printf("通讯录已满,无法添加新的联系人。\n");
return;
}
// 输入新联系人的信息
printf("请输入新联系人的信息:\n");
printf("姓名:");
scanf("%s", pc->contacts[pc->count].name);
printf("性别(M/F):");
scanf(" %c", &(pc->contacts[pc->count].gender));
printf("年龄:");
scanf("%d", &(pc->contacts[pc->count].age));
printf("电话号码:");
scanf("%s", pc->contacts[pc->count].phone);
printf("地址:");
scanf("%s", pc->contacts[pc->count].address);
// 增加联系人数量
pc->count++;
printf("新联系人已添加。\n");
}
运行效果
2.3 删除指定联系人信息
这里我利用strcmp函数将输入的联系人姓名和数组内存储的名字进行比较,若找到将它的下标赋到index中,没有找到index会与pc->count相等。最后利用for循环将后面的联系人信息进行前移
// 删除指定联系人信息
void remove_person(Contact* pc) {
char name[MAX_NAME_LEN];
int index;
// 输入要删除的联系人姓名
printf("请输入要删除的联系人的姓名:");
scanf("%s", name);
// 查找要删除的联系人
for (index = 0; index < pc->count; index++) {
if (strcmp(pc->contacts[index].name, name) == 0) {
break;
}
}
if (index >= pc->count) {
printf("通讯录中没有名为 %s 的联系人。\n", name);
return;
}
// 将要删除的联系人后面的所有联系人往前移动一个位置
for (; index < pc->count - 1; index++) {
memcpy(&(pc->contacts[index]), &(pc->contacts[index + 1]), sizeof(Person));
}
// 减少联系人数量
pc->count--;
printf("联系人 %s 的信息已被删除。\n", name);
}
运行效果
2.4 查找指定联系人信息
这里我依旧用strcmp函数将输入的联系人姓名与数组内存储的名字进行一一比较,找到后将联系人信息进行打印。
// 查找指定联系人信息
void find_person(Contact* pc) {
char name[MAX_NAME_LEN];
int index;
// 输入要查找的联系人姓名
printf("请输入要查找的联系人的姓名:");
scanf("%s", name);
// 查找联系人并显示其信息
for (index = 0; index < pc->count; index++) {
if (strcmp(pc->contacts[index].name, name) == 0) {
printf("联系人信息如下:\n");
printf("姓名:%s\n", pc->contacts[index].name);
printf("性别:%c\n", pc->contacts[index].gender);
printf("年龄:%d\n", pc->contacts[index].age);
printf("电话号码:%s\n", pc->contacts[index].phone);
printf("地址:%s\n", pc->contacts[index].address);
return;
}
}
printf("通讯录中没有名为 %s 的联系人。\n", name);
}
运行效果
2.5 修改指定联系人信息
找到要修改的联系人后,直接在找到的下标进行联系人信息修改
// 修改指定联系人信息
void modify_person(Contact* pc) {
char name[MAX_NAME_LEN];
int index;
// 输入要修改的联系人姓名
printf("请输入要修改的联系人的姓名:");
scanf("%s", name);
// 查找要修改的联系人
for (index = 0; index < pc->count; index++) {
if (strcmp(pc->contacts[index].name, name) == 0) {
break;
}
}
if (index >= pc->count) {
printf("通讯录中没有名为 %s 的联系人。\n", name);
return;
}
// 输入新的联系人信息
printf("请输入新联系人的信息:\n");
printf("姓名:");
scanf("%s", pc->contacts[index].name);
printf("性别(M/F):");
scanf(" %c", &(pc->contacts[index].gender));
printf("年龄:");
scanf("%d", &(pc->contacts[index].age));
printf("电话号码:");
scanf("%s", pc->contacts[index].phone);
printf("地址:");
scanf("%s", pc->contacts[index].address);
printf("联系人 %s 的信息已更新。\n", name);
}
运行效果
2.6 按照姓名排序所有联系人
使用qsort函数来进行联系人姓名的排序
// 按照姓名排序所有联系人
void sortContactsByName(Contact* pc) {
// 按照姓名排序
qsort(pc->contacts, pc->count, sizeof(Person),
(int (*)(const void*, const void*))strcmp);
printf("已按照姓名排序所有联系人。\n");
}
运行效果
2.7 显示所有联系人信息
使用for循环依次访问数组来进行打印
// 显示所有联系人信息
void list_persons(Contact* pc) {
int index;
// 如果通讯录为空,则提示用户并返回
if (pc->count == 0) {
printf("通讯录为空。\n");
return;
}
// 显示所有联系人的信息
printf("所有联系人的信息如下:\n");
for (index = 0; index < pc->count; index++) {
printf("姓名:%s\n", pc->contacts[index].name);
printf("性别:%c\n", pc->contacts[index].gender);
printf("年龄:%d\n", pc->contacts[index].age);
printf("电话号码:%s\n", pc->contacts[index].phone);
printf("地址:%s\n", pc->contacts[index].address);
printf("\n");
}
}
运行效果
2.8 清空所有联系人
直接将当前联系人数量置为0即可
// 清空所有联系人
void clear_persons(Contact* pc) {
pc->count = 0;
printf("通讯录已清空。\n");
}
运行效果
四、通讯录的进一步优化
1.动态版
因为这是一个静态的结构,如果存满了只能创建一个更大的空间来进行存储而且还要进行数据转移非常麻烦,因此我们要使用动态的结构来进行我们通讯录进一步优化。
我们在这里需要对 结构体进行改变,将数组变为指针从而可以进行动态扩容,我们需要添加一个最大容量count_max来与count进行比较,若相等则进行扩容
typedef struct Contact{
Person* contacts;
int count;//记录数量
int count_max;//记录通讯录当前的最大容量
}contact;
这里初始化需要对contacts指针使用malloc函数开辟一段空间。
void init_con(contact* pc){
assert(pc);
pc->count = 0;
pc->count_max = CON_MAX;
pc->contacts = (Person*)malloc(sizeof(Person) * pc->count_max);
if (pc->contacts == NULL){
perror("InitContact::malloc");
return;
}
memset(pc->contacts, 0, pc->count_max * sizeof(Person));
}
增容
当count_max与count相等时,使用realloc函数进行扩容
//增容
void check_capacity(contact* pc){
//增容
if (pc->count == pc->count_max){
Person* tmp = (Person*)realloc(pc->contacts, (pc->count_max + 2) * sizeof(Perso));
if (tmp != NULL){
pc->contacts = tmp;
}
else{
perror("check_capacity::realloc");
return;
}
pc->count_max += 2;
printf("增容成功\n");
}
}
销毁
程序结束后要进行释放内存,否则可能出现内存泄露
//销毁
void DestroyContact(contact* pc)
{
free(pc->contacts);
pc->contacts = NULL;
pc->count_max = pc->count = 0;
printf("销毁成功\n");
}
2.文件版
因为当程序关闭后通讯录的内容无法保存,所以我们要写入文件里来进行保存
//文件的存储
void save_contact(const Contact* pc){
FILE* pf = fopen("contact.dat", "wb");
if (pf == NULL){
perror("save_contact::fopen");
return;
}
//写文件
int i = 0;
for (i = 0; i < pc->count; i++){
fwrite(pc->contacts + i, sizeof(Person), 1, pf);
}
//关闭文件
fclose(pf);
pf = NULL;
}
//文件的打开
void load_contact(Contact* pc){
//打开文件
FILE* pf = fopen("contact.dat", "rb");
if (pf == NULL) {
perror("LoadContact::fopen");
return;
}
//读文件
Person tmp = { 0 };
while (fread(&tmp, sizeof(Person), 1, pf)) {
check_capacity(pc);
pc->contacts[pc->count] = tmp;
pc->count++;
}
//关闭文件
fclose(pf);
pf = NULL;
}
五、完整代码
contact.h
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#define MAX_PERSONS 1000 // 最大联系人数量
#define MAX_NAME_LEN 20 // 姓名最大长度
#define MAX_ADDRESS_LEN 100 // 地址最大长度
#define MAX_PHONE_LEN 15 // 电话号码最大长度
#define CON_MAX 3//通讯录初始化大小
// 联系人结构体
typedef struct Person{
char name[MAX_NAME_LEN]; // 姓名
char gender; // 性别
int age; // 年龄
char phone[MAX_PHONE_LEN]; // 电话号码
char address[MAX_ADDRESS_LEN]; // 地址
} Person;
typedef struct Contact {
Person contacts[MAX_PERSONS]; // 存储所有联系人信息的数组
int count = 0; // 当前联系人数量
}Contact;
//
动态版
//typedef struct Contact
//{
// Person* contacts;
// int count;//记录数量
// int count_max;//记录通讯录当前的最大容量
//}contact;
//通讯录的初始化
void init_con(Contact* pc);
// 添加联系人信息
void add_person(Contact* pc);
// 删除指定联系人信息
void remove_person(Contact* pc);
// 查找指定联系人信息
void find_person(Contact* pc);
// 修改指定联系人信息
void modify_person(Contact* pc);
// 按照姓名排序所有联系人
void sortContactsByName(Contact* pc);
// 显示所有联系人信息
void list_persons(Contact* pc);
// 清空所有联系人
void clear_persons(Contact* pc);
//增容
void check_capacity(contact* pc);
//销毁
void destroy_contact(contact* pc);
//文件的存储
void save_contact(const Contact* pc);
//文件的打开
void load_contact(Contact* pc);
contact.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "contact.h"
//通讯录的初始化
void init_con(Contact* pc) {
assert(pc);
pc->count = 0;
memset(pc->contacts, 0, sizeof(pc->contacts));
}
//文件的存储
void save_contact(const Contact* pc){
FILE* pf = fopen("contact.dat", "wb");
if (pf == NULL){
perror("save_contact::fopen");
return;
}
//写文件
int i = 0;
for (i = 0; i < pc->count; i++){
fwrite(pc->contacts + i, sizeof(Person), 1, pf);
}
//关闭文件
fclose(pf);
pf = NULL;
}
文件的打开
//void load_contact(Contact* pc){
// //打开文件
// FILE* pf = fopen("contact.dat", "rb");
// if (pf == NULL) {
// perror("LoadContact::fopen");
// return;
// }
// //读文件
// Person tmp = { 0 };
// while (fread(&tmp, sizeof(Person), 1, pf)) {
// check_capacity(pc);
// pc->contacts[pc->count] = tmp;
// pc->count++;
// }
// //关闭文件
// fclose(pf);
// pf = NULL;
//}
动态版
//void init_con(contact* pc){
// assert(pc);
// pc->count = 0;
// pc->count_max = CON_MAX;
// pc->contacts = (Person*)malloc(sizeof(Person) * pc->count_max);
// if (pc->contacts == NULL){
// perror("InitContact::malloc");
// return;
// }
// memset(pc->contacts, 0, pc->count_max * sizeof(Person));
// //文件版
// load_contact(pc);
//}
// 添加联系人信息
void add_person(Contact* pc) {
if (pc->count == MAX_PERSONS) {
printf("通讯录已满,无法添加新的联系人。\n");
return;
}
//check_capacity(pc);
// 输入新联系人的信息
printf("请输入新联系人的信息:\n");
printf("姓名:");
scanf("%s", pc->contacts[pc->count].name);
printf("性别(M/F):");
scanf(" %c", &(pc->contacts[pc->count].gender));
printf("年龄:");
scanf("%d", &(pc->contacts[pc->count].age));
printf("电话号码:");
scanf("%s", pc->contacts[pc->count].phone);
printf("地址:");
scanf("%s", pc->contacts[pc->count].address);
// 增加联系人数量
pc->count++;
printf("新联系人已添加。\n");
}
// 删除指定联系人信息
void remove_person(Contact* pc) {
char name[MAX_NAME_LEN];
int index;
// 输入要删除的联系人姓名
printf("请输入要删除的联系人的姓名:");
scanf("%s", name);
// 查找要删除的联系人
for (index = 0; index < pc->count; index++) {
if (strcmp(pc->contacts[index].name, name) == 0) {
break;
}
}
if (index >= pc->count) {
printf("通讯录中没有名为 %s 的联系人。\n", name);
return;
}
// 将要删除的联系人后面的所有联系人往前移动一个位置
for (; index < pc->count - 1; index++) {
memcpy(&(pc->contacts[index]), &(pc->contacts[index + 1]), sizeof(Person));
}
// 减少联系人数量
pc->count--;
printf("联系人 %s 的信息已被删除。\n", name);
}
// 查找指定联系人信息
void find_person(Contact* pc) {
char name[MAX_NAME_LEN];
int index;
// 输入要查找的联系人姓名
printf("请输入要查找的联系人的姓名:");
scanf("%s", name);
// 查找联系人并显示其信息
for (index = 0; index < pc->count; index++) {
if (strcmp(pc->contacts[index].name, name) == 0) {
printf("联系人信息如下:\n");
printf("姓名:%s\n", pc->contacts[index].name);
printf("性别:%c\n", pc->contacts[index].gender);
printf("年龄:%d\n", pc->contacts[index].age);
printf("电话号码:%s\n", pc->contacts[index].phone);
printf("地址:%s\n", pc->contacts[index].address);
return;
}
}
printf("通讯录中没有名为 %s 的联系人。\n", name);
}
// 修改指定联系人信息
void modify_person(Contact* pc) {
char name[MAX_NAME_LEN];
int index;
// 输入要修改的联系人姓名
printf("请输入要修改的联系人的姓名:");
scanf("%s", name);
// 查找要修改的联系人
for (index = 0; index < pc->count; index++) {
if (strcmp(pc->contacts[index].name, name) == 0) {
break;
}
}
if (index >= pc->count) {
printf("通讯录中没有名为 %s 的联系人。\n", name);
return;
}
// 输入新的联系人信息
printf("请输入新联系人的信息:\n");
printf("姓名:");
scanf("%s", pc->contacts[index].name);
printf("性别(M/F):");
scanf(" %c", &(pc->contacts[index].gender));
printf("年龄:");
scanf("%d", &(pc->contacts[index].age));
printf("电话号码:");
scanf("%s", pc->contacts[index].phone);
printf("地址:");
scanf("%s", pc->contacts[index].address);
printf("联系人 %s 的信息已更新。\n", name);
}
// 按照姓名排序所有联系人
void sortContactsByName(Contact* pc) {
// 按照姓名排序
qsort(pc->contacts, pc->count, sizeof(Person),
(int (*)(const void*, const void*))strcmp);
printf("已按照姓名排序所有联系人。\n");
}
// 显示所有联系人信息
void list_persons(Contact* pc) {
int index;
// 如果通讯录为空,则提示用户并返回
if (pc->count == 0) {
printf("通讯录为空。\n");
return;
}
// 显示所有联系人的信息
printf("所有联系人的信息如下:\n");
for (index = 0; index < pc->count; index++) {
printf("姓名:%s\n", pc->contacts[index].name);
printf("性别:%c\n", pc->contacts[index].gender);
printf("年龄:%d\n", pc->contacts[index].age);
printf("电话号码:%s\n", pc->contacts[index].phone);
printf("地址:%s\n", pc->contacts[index].address);
printf("\n");
}
}
// 清空所有联系人
void clear_persons(Contact* pc) {
pc->count = 0;
printf("通讯录已清空。\n");
}
增容
//void check_capacity(contact* pc)
//{
// //增容
// if (pc->count == pc->count_max)
// {
// Person* tmp = (Person*)realloc(pc->contacts, (pc->count_max + 2) * sizeof(Perso));
// if (tmp != NULL)
// {
// pc->contacts = tmp;
// }
// else
// {
// perror("check_capacity::realloc");
// return;
// }
// pc->count_max += 2;
// printf("增容成功\n");
// }
//}
//
销毁
//void destroy_contact(contact* pc)
//{
// free(pc->contacts);
// pc->contacts = NULL;
// pc->count_max = pc->count = 0;
// printf("销毁成功\n");
//}
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "contact.h"
enum Option
{
EXIT,//0
ADD,//1
DEL,//2
SEARCH,//3
MODIFY,//4
SHOW,//5
DEL_ALL,//6
SORT//7
};
void menu() {
// 显示菜单
printf("请选择要执行的操作:\n");
printf("1. 添加联系人信息\n");
printf("2. 删除指定联系人信息\n");
printf("3. 查找指定联系人信息\n");
printf("4. 修改指定联系人信息\n");
printf("5. 显示所有联系人信息\n");
printf("6. 清空所有联系人\n");
printf("7. 以名字排序所有联系人\n");
printf("0. 退出程序\n");
}
int main() {
int choice = 0;
Contact con;
init_con(&con);
while (1) {
menu();
printf("请选择操作:");
scanf("%d", &choice);
switch (choice) {
case EXIT:
//文件的存储
save_contact(&con);
//销毁
destroy_contact(&con);
// 退出程序
return 0;
case ADD:
// 添加联系人信息
add_person(&con);
break;
case DEL:
// 删除指定联系人信息
remove_person(&con);
break;
case SEARCH:
// 查找指定联系人信息
find_person(&con);
break;
case MODIFY:
// 修改指定联系人信息
modify_person(&con);
break;
case SHOW:
// 显示所有联系人信息
list_persons(&con);
break;
case DEL_ALL:
// 清空所有联系人
clear_persons(&con);
break;
case SORT:
// 按照姓名排序所有联系人
sortContactsByName(&con);
break;
default:
printf("无效的选择,请重新输入。\n");
break;
}
}
}
六、结语
以上介绍了我实现通讯录的代码和思路,如果大家感兴趣,还请多多点赞,也可以自己编写实现一个通讯录程序,如果遇到问题,可以在评论区提问我们共同讨论。