最近在 C++ 的学习旅程里,捣鼓出了一个通信录管理系统。虽说还有些小 bug 没解决,但整个开发过程让我对 C++ 编程有了不少新感悟,决定好好记录下来,也算是给自己这段学习时光的一个小总结。
一、成品展示
这个通信录管理系统,能实现最基础的联系人管理功能。可以添加联系人,录入姓名、号码、地址;能删除联系人,根据姓名精准查找并移除;还能查找联系人信息,修改联系人号码 ,最后也能把所有联系人信息展示出来。虽然界面不算花哨,但核心功能都跑通了,对于还在学习基础阶段的我来说,算是一个小小的里程碑。

二、代码解析与思路
(一)结构体与常量定义
先定义了 Contact
结构体,把联系人的姓名、电话、地址信息封装起来,方便管理。用 #define N 100
设定联系人列表最大容量,这样后续数组创建就有了明确边界。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <locale.h>
#define N 100 // 定义联系人列表的最大容量
typedef struct {
char name[20];
char phone[20];
char addr[100];
}Contact;
(二)菜单与功能函数
show_menu
函数很简单,就是打印系统操作菜单,让用户清楚有哪些功能可以用。
void show_menu() {
printf("\n * * * * * * * * * * * * * * * * * * * * * * * * * * *\n");
printf("* 通信录管理系统 *\n");
printf("* a - 添加联系人 *\n");
printf("* d - 删除联系人 *\n");
printf("* f - 查找联系人 *\n");
printf("* m - 修改联系人 *\n");
printf("* p - 显示联系人 *\n");
printf("* q - 退出系统 *\n");
printf("\n * * * * * * * * * * * * * * * * * * * * * * * * * * *\n");
}
排序函数 sort_data
用冒泡排序,按联系人姓名对列表排序,这样查找和展示时,数据更规整,也方便后续处理。
void sort_data(Contact contact_list[], int count) {
int i, j, flag;
Contact temp;
for (i = 1; i < count; i++) {
flag = 0;
for (j = count - 1; j >= i; j--) {
if (strcmp(contact_list[j - 1].name, contact_list[j].name) > 0) {
flag = 1;
temp = contact_list[j];
contact_list[j] = contact_list[j - 1];
contact_list[j - 1] = temp;
}
}
if (flag == 0) {
break;
}
}
}
load_data
函数负责从文件 data.txt
加载数据,用二进制读的方式,把数据读到 contact_list
数组里,加载完还调用 sort_data
排序,保证数据有序。
int load_data(Contact contact_list[]) {
int count = 0;
FILE* fp;
if ((fp = fopen("data.txt", "rb")) == NULL) {
printf("载入数据失败!\n");
return -1;
}
while (!feof(fp)) {
if (fread(&contact_list[count], sizeof(Contact), 1, fp) == 1) {
count++;
}
}
fclose(fp);
sort_data(contact_list, count);
printf("当前有 %d 个联系人\n", count);
return count;
}
添加联系人的 add_new_contact
函数,先让用户输入信息,再以二进制追加写的方式,把新联系人数据存到 data.txt
里,实现数据持久化。
void add_new_contact() {
Contact contact;
printf("请输入添加的联系人信息:\n");
printf("姓名:");
getchar();
scanf("%19s", contact.name);
printf("号码:");
getchar();
scanf("%19s", contact.phone);
printf("地址:");
scanf(" %99s", contact.addr);
FILE* fp;
if ((fp = fopen("data.txt", "ab+")) == NULL) {
printf("添加联系人失败!\n");
return;
}
fwrite(&contact, sizeof(Contact), 1, fp);
fclose(fp);
printf("添加联系人成功!\n");
}
print_contact
函数用来展示联系人列表,遍历数组,把每个联系人的姓名、电话、地址打印出来,方便用户查看。
void print_contact(Contact contact_list[],int count) {
int i;
if (count == 0) {
printf("当前没有联系人!\n");
return;
}
printf("姓名\t 号码\t 地址\n");
for(i = 0;i <count ;i++) {
printf("%s\t%s\t%s \n",contact_list[i].name,contact_list[i].phone,contact_list[i].addr);
}
}
删除联系人的 delete_contact
函数,先根据姓名找到要删除的联系人位置,然后把后面的联系人数据往前移,覆盖要删除的,最后重新把数组数据写入文件,实现删除效果。
void delete_contact(Contact contact_list[], int count) {
int i, j;
char name[20];
getchar();
printf("输入需要删除的联系人姓名:\n");
scanf("%19s", name);
for (i = 0;i < count;i++) {
if (strcmp(contact_list[i].name, name) == 0) {
break;
}
}
if (i == count) {
printf("没有该联系人的信息!\n");
return;
}
for (j = i;j < count - 1;j++) {
contact_list[j] = contact_list[j + 1];
}
FILE* fp;
if ((fp = fopen("data.txt", "wb")) == NULL) {
printf("打开文件错误,删除失败!\n");
return;
}
fwrite(&contact_list[0], sizeof(Contact), count - 1, fp);
fclose(fp);
}
find_contact
函数根据姓名查找联系人,遍历数组对比姓名,找到就打印信息,没找到提示用户。
void find_contact(Contact contact_list[], int count) {
int i;
char name[20];
printf("输入需要查找的联系人姓名:\n");
getchar();
scanf("%19s",name);
for (i = 0;i < count;i++) {
if (strcmp(contact_list[i].name, name) == 0) {
printf("姓名:%s,号码:%s,地址:%s\n", contact_list[i].name, contact_list[i].phone, contact_list[i].addr);
break;
}
}
if (i == count) {
printf("没有该联系人信息!\n");
}
}
modify_contact
函数先找到要修改的联系人,修改其号码后,把整个联系人数组重新写入文件,完成修改。
void modify_contact(Contact contact_list[],int count) {
int i;
char name[20];
printf("请输入需要修改的联系人姓名");
getchar();
scanf("%19s",name);
for (i = 0;i < count;i++) {
if (strcmp(contact_list[i].name, name) == 0) {
break;
}
}
if (i == count) {
printf("没有该联系人信息!\n");
return;
}
printf("请输入%s的新号码:",name);
getchar();
scanf("%99s",&contact_list[i].phone);
FILE* fp;
if ((fp = fopen("data.txt","wb"))== NULL) {
printf("修改联系人失败!\n");
return;
}
fwrite(&contact_list[0],sizeof(Contact),count,fp);
fclose(fp);
printf("修改成功!\n");
}
(三)主函数流程
主函数 main
里,先调用 load_data
加载数据,然后展示菜单,通过循环和 switch
语句,根据用户输入的指令,调用对应的功能函数,实现交互。
int main() {
int count = 0;
Contact contact_list[N];
count = load_data(contact_list);
if (count == -1) {
return 0;
}
show_menu();
char ch;
printf("数据加载成功,请输入你需要的操作: \n");
while ((ch = getchar()) != EOF) {
if (ch == '\n') continue; // 忽略换行符
if (ch == 'q') break;
switch (ch) {
case 'a':
add_new_contact();
count = load_data(contact_list);
break;
case 'p':
print_contact(contact_list,count);
break;
case 'd':
delete_contact(contact_list, count);
break;
case 'f':
find_contact(contact_list, count);
break;
case 'm':
modify_contact(contact_list, count);
break;
default:
printf("输入的命令有误! \n");
break;
}
printf("请输入你需要的操作: \n");
fflush(stdin);
scanf("%c", &ch);
}
printf("退出系统! \n");
return 0;
}
三、遇到的问题与不足
(一)输入的小麻烦
在 add_new_contact
这类函数里,用 getchar
处理输入缓冲区的换行符,还是偶尔会出问题。比如连续输入时,可能会跳过某些输入步骤,用户体验不太好。后来了解到,其实可以用 scanf(" %c", &ch);
这种带空格的格式控制,或者用 fgets
处理字符串输入,能更稳定解决缓冲区问题,后续优化打算试试这些方法。
(二)数据处理的隐患
删除和修改联系人时,是把整个联系人数组重新写入文件。如果联系人数量多了,这样操作效率低,而且一旦写入过程中出错,数据可能丢失。之后想优化的话,可以研究下文件的局部修改,比如用索引标记数据位置,精准修改,不用每次都重写整个文件。
(三)功能的局限性
目前系统功能比较基础,只能按姓名查找、删除、修改,要是有用户想按号码查找,就实现不了。而且联系人信息也只有姓名、电话、地址,实际使用中,可能还需要邮箱、备注等更多字段,后续可以考虑拓展结构体,丰富功能。
四、收获与成长
通过写这个通信录管理系统,我对 C++ 的结构体、文件操作、函数封装这些知识,从书本上的概念,真正变成了能动手用的技能。遇到 bug 时,学会了一步步调试,看程序执行流程,找变量的值变化,这种排错能力太重要了。
同时,也意识到编程不只是写代码,还要考虑用户体验、数据安全这些实际问题。一个小小的通信录系统,想要做好,也得不断优化细节。这也让我对后续深入学习 C++ 更有期待,想看看能把这样的小项目,迭代成多完善的工具。
虽然现在的通信录管理系统还有不少瑕疵,但它就像一个成长的标记,记录着我学 C++ 过程中的思考和实践。我会带着这些问题和收获,继续在编程路上折腾,把这个系统越改越好,也让自己的技术越来越扎实~