C语言课程设计:通讯录管理系统
项目概述
本项目旨在使用C语言开发一个基于命令行的简易通讯录管理系统,该系统将实现通讯录的基本功能,包括添加、删除、查找、显示、修改和保存联系人信息,数据将持久化存储在文件中,确保程序关闭后再次打开时数据不会丢失。

(图片来源网络,侵删)
功能需求分析
系统需要实现以下核心功能:
- 添加联系人: 用户可以输入新联系人的信息(如姓名、电话、邮箱、地址等),并将其添加到通讯录中。
- 删除联系人: 用户可以通过输入姓名或电话号码来查找并删除指定的联系人。
- 查找联系人: 用户可以通过输入姓名或电话号码来查找联系人,并显示其详细信息。
- 显示所有联系人: 以列表形式显示通讯录中所有联系人的信息。
- 修改联系人: 用户可以通过输入姓名或电话号码来查找联系人,并修改其部分或全部信息。
- 保存联系人: 将当前通讯录中的所有数据保存到磁盘文件中。
- 加载联系人: 从磁盘文件中加载已保存的联系人数据到通讯录。
- 清空通讯录: 提供一个功能,可以清空当前通讯录中的所有数据。
- 退出系统: 退出程序前,系统会自动提示用户保存数据,然后安全退出。
数据结构设计
为了高效地管理联系人信息,我们需要选择合适的数据结构。
-
联系人信息结构体 (
struct Contact): 使用结构体来封装单个联系人的所有属性。#include <stdio.h> #include <string.h> #include <stdlib.h> // 定义联系人结构体 typedef struct { char name[50]; char phone[20]; char email[50]; char address[100]; } Contact; -
通讯录结构体 (
struct AddressBook): 通讯录本质上是一个联系人列表,我们可以使用一个结构体来包含一个联系人数组和一个变量来记录当前通讯录中联系人的数量。
(图片来源网络,侵删)// 定义通讯录结构体 #define MAX_CONTACTS 1000 // 最大联系人数量 typedef struct { Contact contacts[MAX_CONTACTS]; // 联系人数组 int count; // 当前联系人数量 } AddressBook;
核心功能模块设计
我们将整个程序划分为多个功能模块,每个模块用一个函数来实现。
| 函数名 | 功能描述 |
|---|---|
initBook(AddressBook *book) |
初始化通讯录,将联系人数量设为0。 |
addContact(AddressBook *book) |
添加联系人,并更新联系人数量。 |
deleteContact(AddressBook *book) |
查找并删除联系人。 |
findContact(AddressBook *book) |
查找并显示联系人信息。 |
displayAllContacts(AddressBook *book) |
显示所有联系人。 |
modifyContact(AddressBook *book) |
查找并修改联系人信息。 |
saveToFile(AddressBook *book) |
将通讯录数据保存到文件。 |
loadFromFile(AddressBook *book) |
从文件加载通讯录数据。 |
clearContacts(AddressBook *book) |
清空通讯录。 |
showMenu() |
显示主菜单界面。 |
main() |
程序入口,调用其他函数,控制程序流程。 |
完整代码实现
下面是完整的C语言代码,你可以直接复制到编译器(如 VS Code, Dev-C++, Visual Studio)中运行。
#include <stdio.h>
#include <string.h>
#include <stdlib.h> // 用于 system("pause") 和 system("cls")
// 定义联系人结构体
typedef struct {
char name[50];
char phone[20];
char email[50];
char address[100];
} Contact;
// 定义通讯录结构体
#define MAX_CONTACTS 1000
typedef struct {
Contact contacts[MAX_CONTACTS];
int count;
} AddressBook;
// 函数声明
void initBook(AddressBook *book);
void showMenu();
void addContact(AddressBook *book);
void deleteContact(AddressBook *book);
void findContact(AddressBook *book);
void displayAllContacts(AddressBook *book);
void modifyContact(AddressBook *book);
void saveToFile(AddressBook *book);
void loadFromFile(AddressBook *book);
void clearContacts(AddressBook *book);
// --- 核心功能函数实现 ---
// 初始化通讯录
void initBook(AddressBook *book) {
book->count = 0;
printf("通讯录已初始化,\n");
}
// 显示主菜单
void showMenu() {
printf("\n\n\n");
printf("********************\n");
printf("* 通讯录管理系统 *\n");
printf("********************\n");
printf("* 1. 添加联系人 *\n");
printf("* 2. 删除联系人 *\n");
printf("* 3. 查找联系人 *\n");
printf("* 4. 显示所有联系人 *\n");
printf("* 5. 修改联系人 *\n");
printf("* 6. 清空通讯录 *\n");
printf("* 7. 保存到文件 *\n");
printf("* 8. 从文件加载 *\n");
printf("* 0. 退出系统 *\n");
printf("********************\n");
printf("请输入您的选择 (0-8): ");
}
// 添加联系人
void addContact(AddressBook *book) {
if (book->count >= MAX_CONTACTS) {
printf("通讯录已满,无法添加!\n");
return;
}
Contact newContact;
printf("请输入姓名: ");
scanf("%s", newContact.name);
printf("请输入电话: ");
scanf("%s", newContact.phone);
printf("请输入邮箱: ");
scanf("%s", newContact.email);
printf("请输入地址: ");
scanf("%s", newContact.address); // 简单处理,地址中不能有空格
book->contacts[book->count] = newContact;
book->count++;
printf("联系人 %s 添加成功!\n", newContact.name);
}
// 删除联系人
void deleteContact(AddressBook *book) {
if (book->count == 0) {
printf("通讯录为空,无法删除!\n");
return;
}
char keyword[50];
printf("请输入要删除的联系人姓名或电话: ");
scanf("%s", keyword);
int foundIndex = -1;
for (int i = 0; i < book->count; i++) {
if (strcmp(book->contacts[i].name, keyword) == 0 || strcmp(book->contacts[i].phone, keyword) == 0) {
foundIndex = i;
break;
}
}
if (foundIndex == -1) {
printf("未找到该联系人!\n");
} else {
// 将后面的元素前移
for (int i = foundIndex; i < book->count - 1; i++) {
book->contacts[i] = book->contacts[i + 1];
}
book->count--;
printf("联系人删除成功!\n");
}
}
// 查找联系人
void findContact(AddressBook *book) {
if (book->count == 0) {
printf("通讯录为空!\n");
return;
}
char keyword[50];
printf("请输入要查找的联系人姓名或电话: ");
scanf("%s", keyword);
int found = 0;
for (int i = 0; i < book->count; i++) {
if (strcmp(book->contacts[i].name, keyword) == 0 || strcmp(book->contacts[i].phone, keyword) == 0) {
printf("\n--- 找到联系人 ---\n");
printf("姓名: %s\n", book->contacts[i].name);
printf("电话: %s\n", book->contacts[i].phone);
printf("邮箱: %s\n", book->contacts[i].email);
printf("地址: %s\n", book->contacts[i].address);
printf("------------------\n");
found = 1;
// 如果想找到所有匹配的,可以去掉 break
// break;
}
}
if (!found) {
printf("未找到该联系人!\n");
}
}
// 显示所有联系人
void displayAllContacts(AddressBook *book) {
if (book->count == 0) {
printf("通讯录为空!\n");
return;
}
printf("\n--- 所有联系人列表 (共 %d 人) ---\n", book->count);
printf("------------------------------------------------------------\n");
printf("序号 | 姓名 | 电话 | 邮箱 | 地址\n");
printf("------------------------------------------------------------\n");
for (int i = 0; i < book->count; i++) {
printf("%-4d | %-10s | %-12s | %-17s | %s\n",
i + 1,
book->contacts[i].name,
book->contacts[i].phone,
book->contacts[i].email,
book->contacts[i].address);
}
printf("------------------------------------------------------------\n");
}
// 修改联系人
void modifyContact(AddressBook *book) {
if (book->count == 0) {
printf("通讯录为空,无法修改!\n");
return;
}
char keyword[50];
printf("请输入要修改的联系人姓名或电话: ");
scanf("%s", keyword);
int foundIndex = -1;
for (int i = 0; i < book->count; i++) {
if (strcmp(book->contacts[i].name, keyword) == 0 || strcmp(book->contacts[i].phone, keyword) == 0) {
foundIndex = i;
break;
}
}
if (foundIndex == -1) {
printf("未找到该联系人!\n");
} else {
Contact *pContact = &book->contacts[foundIndex];
printf("找到联系人: %s\n", pContact->name);
printf("请输入新的姓名 (原: %s, 直接回车保持不变): ", pContact->name);
char input[100];
getchar(); // 清除输入缓冲区中的换行符
fgets(input, sizeof(input), stdin);
if (strlen(input) > 1) { // 如果用户输入了内容
input[strcspn(input, "\n")] = 0; // 去掉末尾的换行符
strcpy(pContact->name, input);
}
printf("请输入新的电话 (原: %s, 直接回车保持不变): ", pContact->phone);
fgets(input, sizeof(input), stdin);
if (strlen(input) > 1) {
input[strcspn(input, "\n")] = 0;
strcpy(pContact->phone, input);
}
printf("请输入新的邮箱 (原: %s, 直接回车保持不变): ", pContact->email);
fgets(input, sizeof(input), stdin);
if (strlen(input) > 1) {
input[strcspn(input, "\n")] = 0;
strcpy(pContact->email, input);
}
printf("请输入新的地址 (原: %s, 直接回车保持不变): ", pContact->address);
fgets(input, sizeof(input), stdin);
if (strlen(input) > 1) {
input[strcspn(input, "\n")] = 0;
strcpy(pContact->address, input);
}
printf("联系人信息修改成功!\n");
}
}
// 保存到文件
void saveToFile(AddressBook *book) {
if (book->count == 0) {
printf("通讯录为空,无需保存!\n");
return;
}
FILE *fp = fopen("contacts.dat", "wb");
if (fp == NULL) {
printf("无法打开文件进行保存!\n");
return;
}
fwrite(book, sizeof(AddressBook), 1, fp);
fclose(fp);
printf("通讯录已成功保存到 contacts.dat 文件!\n");
}
// 从文件加载
void loadFromFile(AddressBook *book) {
FILE *fp = fopen("contacts.dat", "rb");
if (fp == NULL) {
printf("未找到数据文件,将创建一个新的通讯录,\n");
return;
}
fread(book, sizeof(AddressBook), 1, fp);
fclose(fp);
printf("通讯录已成功从 contacts.dat 文件加载!共加载 %d 个联系人,\n", book->count);
}
// 清空通讯录
void clearContacts(AddressBook *book) {
if (book->count == 0) {
printf("通讯录已经是空的!\n");
return;
}
char confirm;
printf("确定要清空所有联系人吗?此操作不可恢复!(y/n): ");
scanf(" %c", &confirm); // 注意 %c 前的空格,用于跳过空白字符
if (confirm == 'y' || confirm == 'Y') {
book->count = 0;
printf("通讯录已清空!\n");
} else {
printf("操作已取消,\n");
}
}
// --- 主函数 ---
int main() {
AddressBook book;
initBook(&book);
loadFromFile(&book); // 程序启动时加载数据
int choice;
do {
showMenu();
scanf("%d", &choice);
switch (choice) {
case 1:
addContact(&book);
break;
case 2:
deleteContact(&book);
break;
case 3:
findContact(&book);
break;
case 4:
displayAllContacts(&book);
break;
case 5:
modifyContact(&book);
break;
case 6:
clearContacts(&book);
break;
case 7:
saveToFile(&book);
break;
case 8:
loadFromFile(&book);
break;
case 0:
saveToFile(&book); // 退出前自动保存
printf("感谢使用,再见!\n");
break;
default:
printf("无效的输入,请重新输入 (0-8)!\n");
break;
}
system("pause"); // 暂停,按任意键继续
system("cls"); // 清屏 (Windows系统有效)
} while (choice != 0);
return 0;
}
如何编译和运行
- 保存代码: 将以上代码复制并粘贴到一个文本编辑器中,保存为
address_book.c。 - 打开终端/命令行:
- Windows: 打开 CMD 或 PowerShell。
- Linux/macOS: 打开 Terminal。
- 编译代码: 使用 GCC 编译器进行编译。
gcc address_book.c -o address_book
- 运行程序:
- Windows:
address_book.exe
- Linux/macOS:
./address_book
- Windows:
项目扩展思路 (进阶)
如果你觉得这个项目太简单,可以尝试以下扩展来增加挑战性:
-
动态内存分配:
(图片来源网络,侵删)- 将
AddressBook结构体中的固定大小数组contacts替换为一个指针Contact *contacts。 - 使用
malloc()来动态分配内存,初始时可以分配一个小一点的容量(如 10)。 - 当联系人数量达到容量上限时,使用
realloc()来重新分配更大的内存空间(如容量翻倍)。 - 这样可以更高效地利用内存,避免
MAX_CONTACTS的限制。
- 将
-
更健壮的输入处理:
- 当前代码使用
scanf和fgets处理输入,对于包含空格的字符串(如地址)处理不够优雅。 - 可以编写一个通用的
getStringInput函数,封装fgets和strcspn的逻辑,使所有输入处理都统一且安全。
- 当前代码使用
-
增加排序功能:
- 增加一个菜单项 "按姓名排序"。
- 使用
qsort函数(C标准库中的快速排序)对contacts数组按姓名字母顺序进行排序。
-
改进数据持久化:
- 当前使用二进制文件 (
"wb","rb"),优点是读写快、文件小,缺点是可读性差,且如果修改了Contact结构体,旧文件可能无法正确读取。 - 可以改用文本文件(如 CSV 格式),使用
fprintf和fscanf进行读写,优点是可读性好,兼容性更强,缺点是文件体积稍大,解析速度稍慢。
- 当前使用二进制文件 (
-
图形用户界面:
使用跨平台的GUI库(如 GTK, Qt, wxWidgets)将命令行界面改为图形界面,这会是一个巨大的飞跃,但非常适合作为更高阶的课程设计。
这个通讯录管理系统项目涵盖了C语言的核心知识点,包括:
- 结构体: 用于复杂数据类型的定义。
- 数组: 用于存储批量数据。
- 文件操作: (
fopen,fwrite,fread,fclose) 用于数据持久化。 - 指针: 函数参数传递地址,实现对结构体内部数据的修改。
- 循环和分支: (
for,while,if-else,switch) 用于控制程序流程。 - 字符串处理: (
strcpy,strcmp,strlen,strcspn) 用于处理联系人信息。
通过完成这个项目,你将能很好地巩固和综合运用C语言的基础知识,并为后续学习更复杂的编程概念打下坚实的基础,祝你课程设计顺利!
