C语言学习笔记20260629-分文件通讯录完整实现

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