C语言学习笔记20260629-分文件通讯录完整实现
项目概述
本项目实现了一个基于控制台的通讯录管理系统,采用多文件工程结构(3文件拆分),将声明、实现与测试分离,是C语言工程化开发的经典实践。
功能需求
- 最多存储 100 人
- 每人信息包含:姓名、年龄、性别、电话、地址
- 支持功能:增、删、查、改、排序、显示全部
工程结构
contact.h → 头文件:结构体定义 + 函数声明
contact.c → 源文件:所有功能函数的具体实现
stu20260629.c → 测试文件:main函数 + 菜单交互界面
核心设计思想
数据结构设计
通讯录的核心是两个嵌套的结构体,采用顺序表(数组实现)结构:
c
// 单人信息结构体
typedef struct PeoInfo {
char name[NAME_LEN];
int age;
char sex[SEX_LEN];
char tel[TEL_LEN];
char addr[ADDR_LEN];
} PeoInfo;
// 通讯录结构体
typedef struct Contact {
PeoInfo data[MAX_PEOPLE]; // 存放所有人的数组
int count; // 当前有效人数
} Contact;
文件一:contact.h(头文件)
作用
头文件是整个工程的"接口说明书",包含宏定义、结构体类型和函数声明。
c
#ifndef __CONTACT_H__
#define __CONTACT_H__
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX_PEOPLE 100
#define NAME_LEN 20
#define SEX_LEN 6
#define TEL_LEN 15
#define ADDR_LEN 30
// 单人信息结构体
typedef struct PeoInfo
{
char name[NAME_LEN];
int age;
char sex[SEX_LEN];
char tel[TEL_LEN];
char addr[ADDR_LEN];
} PeoInfo;
// 通讯录结构体
typedef struct Contact
{
PeoInfo data[MAX_PEOPLE];
int count; // 当前有效联系人个数
} Contact;
// 初始化通讯录
void InitContact(Contact* pc);
// 1. 新增联系人
void AddContact(Contact* pc);
// 2. 显示所有联系人
void ShowContact(const Contact* pc);
// 3. 查找联系人(按姓名,返回下标,无则-1)
int FindByName(const Contact* pc, char name[]);
// 4. 删除指定联系人
void DelContact(Contact* pc);
// 5. 修改联系人信息
void ModifyContact(Contact* pc);
// 6. 搜索联系人并打印
void SearchContact(const Contact* pc);
// 7. 按姓名排序联系人
void SortContact(Contact* pc);
#endif
关键细节
- 头文件守卫:防止重复包含
- const修饰:只读函数参数使用const
- 标准库包含:stdio.h、string.h、stdlib.h
文件二:contact.c(功能实现)
作用
所有功能函数的具体实现都放在这里。
c
#include "contact.h"
// 初始化通讯录
void InitContact(Contact* pc)
{
pc->count = 0;
memset(pc->data, 0, sizeof(pc->data));
}
// 新增联系人
void AddContact(Contact* pc)
{
if (pc->count >= MAX_PEOPLE)
{
printf("通讯录已满,无法添加!\n");
return;
}
printf("请输入姓名:");
scanf("%s", pc->data[pc->count].name);
printf("请输入年龄:");
scanf("%d", &pc->data[pc->count].age);
printf("请输入性别:");
scanf("%s", pc->data[pc->count].sex);
printf("请输入电话:");
scanf("%s", pc->data[pc->count].tel);
printf("请输入地址:");
scanf("%s", pc->data[pc->count].addr);
pc->count++;
printf("添加成功!\n");
}
// 显示全部联系人
void ShowContact(const Contact* pc)
{
if (pc->count == 0)
{
printf("通讯录为空!\n");
return;
}
printf("%-10s %-5s %-6s %-15s %-20s\n", "姓名", "年龄", "性别", "电话", "地址");
for (int i = 0; i < pc->count; i++)
{
printf("%-10s %-5d %-6s %-15s %-20s\n",
pc->data[i].name,
pc->data[i].age,
pc->data[i].sex,
pc->data[i].tel,
pc->data[i].addr);
}
}
// 根据姓名查找,返回下标,找不到返回-1
int FindByName(const Contact* pc, char name[])
{
for (int i = 0; i < pc->count; i++)
{
if (strcmp(pc->data[i].name, name) == 0)
{
return i;
}
}
return -1;
}
// 删除联系人
void DelContact(Contact* pc)
{
if (pc->count == 0)
{
printf("通讯录为空,无需删除!\n");
return;
}
char name[NAME_LEN];
printf("请输入要删除的姓名:");
scanf("%s", name);
int pos = FindByName(pc, name);
if (pos == -1)
{
printf("未找到该联系人!\n");
return;
}
// 后续数据前移覆盖
for (int i = pos; i < pc->count - 1; i++)
{
pc->data[i] = pc->data[i + 1];
}
pc->count--;
printf("删除成功!\n");
}
// 修改联系人
void ModifyContact(Contact* pc)
{
if (pc->count == 0)
{
printf("通讯录为空!\n");
return;
}
char name[NAME_LEN];
printf("请输入要修改的姓名:");
scanf("%s", name);
int pos = FindByName(pc, name);
if (pos == -1)
{
printf("未找到该联系人!\n");
return;
}
printf("请输入新姓名:");
scanf("%s", pc->data[pos].name);
printf("请输入新年龄:");
scanf("%d", &pc->data[pos].age);
printf("请输入新性别:");
scanf("%s", pc->data[pos].sex);
printf("请输入新电话:");
scanf("%s", pc->data[pos].tel);
printf("请输入新地址:");
scanf("%s", pc->data[pos].addr);
printf("修改完成!\n");
}
// 查找并打印联系人
void SearchContact(const Contact* pc)
{
if (pc->count == 0)
{
printf("通讯录为空!\n");
return;
}
char name[NAME_LEN];
printf("请输入要查找的姓名:");
scanf("%s", name);
int pos = FindByName(pc, name);
if (pos == -1)
{
printf("未找到该联系人!\n");
return;
}
printf("查询结果:\n");
printf("%-10s %-5s %-6s %-15s %-20s\n", "姓名", "年龄", "性别", "电话", "地址");
printf("%-10s %-5d %-6s %-15s %-20s\n",
pc->data[pos].name,
pc->data[pos].age,
pc->data[pos].sex,
pc->data[pos].tel,
pc->data[pos].addr);
}
// 按姓名升序排序(冒泡排序)
void SortContact(Contact* pc)
{
if (pc->count <= 1)
{
printf("联系人数量不足,无需排序!\n");
return;
}
for (int i = 0; i < pc->count - 1; i++)
{
for (int j = 0; j < pc->count - 1 - i; j++)
{
if (strcmp(pc->data[j].name, pc->data[j + 1].name) > 0)
{
PeoInfo temp = pc->data[j];
pc->data[j] = pc->data[j + 1];
pc->data[j + 1] = temp;
}
}
}
printf("排序完成!\n");
}
核心算法解析
- 删除操作:数据前移覆盖,时间复杂度O(N)
- 查找操作:字符串比较使用strcmp
- 排序操作:冒泡排序配合strcmp,时间复杂度O(N²)
文件三:stu20260629.c(主程序)
作用
提供用户交互界面(菜单),调用contact.h中声明的函数。
c
#include "contact.h"
void Menu()
{
printf("====================通讯录====================\n");
printf("1. 新增联系人 2. 删除联系人\n");
printf("3. 查找联系人 4. 修改联系人\n");
printf("5. 联系人排序 6. 显示全部\n");
printf("0. 退出程序\n");
printf("===============================================\n");
printf("请输入功能序号:");
}
int main()
{
Contact con;
InitContact(&con);
int input = 0;
do
{
Menu();
scanf("%d", &input);
switch (input)
{
case 1:
AddContact(&con);
break;
case 2:
DelContact(&con);
break;
case 3:
SearchContact(&con);
break;
case 4:
ModifyContact(&con);
break;
case 5:
SortContact(&con);
break;
case 6:
ShowContact(&con);
break;
case 0:
printf("程序退出,再见!\n");
break;
default:
printf("输入错误,请重新选择!\n");
break;
}
printf("\n");
} while (input != 0);
return 0;
}
运行示例
====================通讯录====================
1. 新增联系人 2. 删除联系人
3. 查找联系人 4. 修改联系人
5. 联系人排序 6. 显示全部
0. 退出程序
===============================================
请输入功能序号:1
请输入姓名:张三
请输入年龄:25
请输入性别:男
请输入电话:11112223456
请输入地址:北京
添加成功!
知识点总结
关键技术点
| 知识点 | 说明 |
|---|---|
| 多文件工程 | .h声明、.c实现、分离编译 |
| 头文件守卫 | #ifndef / #define / #endif防止重复包含 |
| 结构体嵌套 | Contact中包含PeoInfo数组 |
| 指针传参 | 大结构体用指针传递,避免拷贝开销 |
| const修饰 | 只读参数加const,提高安全性 |
| 字符串比较 | 用strcmp而非== |
| 顺序表删除 | 删除后后续元素前移覆盖 |
| 冒泡排序 | 配合strcmp实现字符串排序 |
| memset | 批量初始化内存为0 |
拓展思考
- 输入安全性 :可改用
fgets防止缓冲区溢出 - 动态扩容 :可改用
malloc+realloc实现动态数组 - 文件持久化:增加保存到文件/从文件加载功能
- 排序优化 :数据量大时可改用快速排序(
qsort)