C语言通讯录系统:从设计到源码实现的完整指南

Chloe
Chloe
发布时间:2026-10-15| 5人浏览过
原创

本文详细解析如何利用C语言实现一个功能完备的通讯录管理系统,涵盖项目结构定义、动态内存扩容策略以及基于冒泡排序的通用算法应用,帮助开发者掌握结构体在实际工程中的应用。

1. 系统整体架构设计

通讯录系统本质上是一个基于数组(此处为动态扩容数组)的数据存储与检索工具。为了实现模块化开发,通常将工程拆分为头文件声明、功能逻辑实现和主程序控制三个部分。

第1步:定义数据结构与接口

目的: 确定单个联系人信息的存储格式以及整个通讯录容器的状态字段。

入口: Contact.h 头文件

关键操作:

  • 定义 PeoInfo 结构体,包含姓名、年龄、电话等成员。
  • 定义 Contact 结构体作为容器,其中包含指向 PeoInfo 的动态指针 data、当前元素数量 count 和容量 capacity。
  • 声明各功能函数的原型,如添加、删除、查找、展示、销毁等。
#define _CRT_SECURE_NO_WARNINGS 1
#ifndef __CONTACT_H_
#define __CONTACT_H_

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#define DEFAULT_SZ 3 // 初始容量

enum CHOOSE // 菜单选项枚举
{
    EXIT,
    ADD,
    DEL,
    SHOW,
    FIND,
    SORT,
    MODIFY,
};

typedef struct CONTACT // 单人信息结构
{
    char name[20];
    int age;
    char phone[12];
}PeoInfo;

typedef struct Contact // 通讯录容器结构
{
    PeoInfo* data;   // 动态分配的数组空间
    int count;       // 当前有效人数
    int capacity;    // 当前最大容量
}Contact, *pContact;

// 功能函数声明
void AddContact(pContact pc);
void InitContact(pContact pc);
void ShowContact(pContact pc);
void DelContact(pContact pc);
void FindContact(pContact pc);
void DestroyContact(pContact pc);
void ModifyContact(pContact pc);
void SortContact(pContact pc);

#endif //CONTACT

第2步:编写主循环与交互界面

目的: 建立用户输入流,根据用户选择调用相应的业务逻辑函数。

入口: main.c

关键操作:

  • 初始化通讯录对象,分配默认空间。
  • 使用 do-while 循环显示菜单并读取用户指令。
  • 通过 switch-case 分发对应函数,注意在退出前执行销毁操作以释放内存。
#include "Contact.h"
#include "bubble_sort.h"

void menu()
{
    printf("+-------------------------------+\n");
    printf("|   1.Add        2.Del          |\n");
    printf("|   3.Show       4.Find         |\n");
    printf("|   5.Sort       6.Modify       |\n");
    printf("|         0.Exit                |\n");
    printf("+-------------------------------+\n");
}

int main()
{
    int choose = 0;
    Contact my_con;
    
    // 初始化:分配初始空间
    InitContact(&my_con);    
    
    do
    {
        menu();
        printf("请选择要进行的操作:\n");
        scanf_s("%d", &choose);
        switch (choose)
        {
            case ADD:     AddContact(&my_con); break;
            case DEL:     DelContact(&my_con); break;
            case SHOW:    ShowContact(&my_con); break;
            case FIND:    FindContact(&my_con); break;
            case SORT:    SortContact(&my_con); break;
            case MODIFY:  ModifyContact(&my_con); break;
            case EXIT:
                DestroyContact(&my_con); // 释放内存
                break;
            default:
                printf("请重新选择!\n");
                break;
        }

    } while (choose);

    return 0;
}

2. 核心逻辑实现

第3步:处理增容与基本增删改

目的: 解决固定数组长度限制问题,实现数据的动态维护。

入口: test.c 中的具体实现函数

关键操作:

  • 增容检查: 在执行 AddContact 前判断 count == capacity。若满则调用 realloc 扩大数组大小(例如每次增加2个单位),并更新 capacity。
  • 数据录入: 直接将新信息填入 data[count] 并递增计数器。
  • 删除逻辑: 先通过姓名查找目标索引,随后将该位置之后的所有元素向前覆盖移动一位,最后递减计数器。这保证了数组紧凑性。
  • 修改逻辑: 查找成功后直接覆写相应字段。
代码片段:测试环境下的主要逻辑实现 (test.c)
#include"Contact.h"
#include"bubble_sort.h"

void InitContact(pContact pc) // 初始化通讯录
{
    pc->count = 0;
    pc->capacity = DEFAULT_SZ;
    // 使用calloc初始化为0,防止垃圾值
    pc->data =(PeoInfo *) calloc(sizeof(PeoInfo),pc->capacity);
}

void check_capacity(pContact pc) // 检查并扩容
{
    PeoInfo* p = (PeoInfo *)realloc(pc->data, (pc->capacity + 2)*sizeof(PeoInfo));
    if (p != NULL)
    {
        pc->data = p;
        p = NULL;
    }
    pc->capacity += 2;
    printf("增容成功!\n");
}

void AddContact(pContact pc) // 增加信息
{
    char c = 0;
    if (pc->count == pc->capacity)
    {
        printf("通讯录已满!\n");
        printf("请问是否增容:(Y \ N)\n");
        getchar(); // 清除缓冲区
        scanf("%c", &c);
        if (c == 'Y')
        {
            check_capacity(pc);
        }
        else
        {
            return;
        }
    }
    printf("请输入名字:");
    scanf("%s", pc->data[pc->count].name);
    printf("请输入年龄:");
    scanf("%d", &(pc->data[pc->count].age));
    printf("请输入电话:");
    scanf("%s", pc->data[pc->count].phone);
    pc->count++;
    printf("添加成功!\n");
}

void ShowContact(pContact pc) // 展示通讯录
{
    int i = 0;
    if (pc->count == 0)
    {
        printf("通讯录为空!\n");
        return;
    }
    printf("%7s %5s %10s\n","姓名","年龄","电话");
    for (i = 0; i < pc->count; i++)
    {
        printf("%7s %5d %10s\n", pc->data[i].name, pc->data[i].age, pc->data[i].phone);
    }
    printf("--------------------------\n");
}

int FindEntry(char name[],pContact pc) // 内部查找辅助函数,返回索引
{
    int i = 0;
    for (i = 0; i < pc->count; i++)
    {
        if (strcmp(pc->data[i].name, name) == 0)
        {
            return i;
        }
    }
    return -1;
}

void DelContact(pContact pc) // 删除信息
{
    char name[20] = { 0 };
    int pos = 0;
    int i = 0;
    if (pc->count == 0)
    {
        printf("通讯录为空!\n");
        return ;
    }
    printf("要查找的人的名字:");
    scanf("%s", name);
    pos = FindEntry(name,pc);
    if (pos == -1)
    {
        printf("要删除的人不存在!\n");
        return ;
    }
    // 覆盖法删除
    for (i = pos; i < pc->count-1;i++)
    {
        pc->data[i] = pc->data[i + 1];
    }
    pc->count--;
    printf("删除成功!\n");
}

void FindContact(pContact pc) // 查找信息
{
    int n = 0;
    char name[20] = { 0 };
    printf("请输入要查找的人名:\n");
    scanf("%s", name);
    n = FindEntry(name,pc);
    if (n >= 0)
    {
        printf("%s     %d       %s\n", pc->data[n].name, pc->data[n].age, pc->data[n].phone);
    }
    else 
    {
        printf("查无此人!\n");
    }
}

void DestroyContact(pContact pc) // 销毁通讯录,释放内存
{
    free(pc->data);
    pc->data = NULL; // 悬空指针置空,防止重复释放
    pc->count = 0;
    pc->capacity = 0;
    printf("成功销毁!\n");
}

void ModifyContact(pContact pc) // 修改信息
{
    char name[20] = { 0 };
    int n = 0;
    printf("请输入要修改的人名:\n");
    scanf("%s", name);
    // 原代码逻辑瑕疵修正:这里应先查找再判定
    n = FindEntry(name, pc);
    if (n >= 0)
    {
        printf("请输入名字:");
        scanf("%s", pc->data[n].name);
        printf("请输入年龄:");
        scanf("%d", &(pc->data[n].age));
        printf("请输入电话:");
        scanf("%s", pc->data[n].phone);
        printf("修改成功!\n");
    }
    else
    {
        printf("查无此人!\n");
    }
}

第4步:复用通用排序算法

目的: 实现对不同字段(如姓名字符串、年龄整数)的灵活排序,而不必为每种类型重写排序代码。

原理: 利用 qsort 类似的思路,使用 void* 指针接收任意类型的数组首地址,并使用函数指针 cmp 来决定比较规则。

实现方案:

虽然标准库提供了 qsort,但为了练习底层逻辑,我们可以手动实现一个通用的冒泡排序接口。关键在于数据拷贝交换——当比较函数返回需要交换的信号时,我们按字节宽度逐字节交换内存块。

代码片段:通用排序算法实现 (bubble_sort.c/h)
// bubble_sort.h
#ifndef __BUBBLESORT_H_
#define __BUBBLESORT_H_

#include<stdlib.h>
#include<stdio.h>
#include"Contact.h" // 为了获取PeoInfo结构用于比较回调

// 冒泡排序声明
void bubble_sort(void * base, int sz, int width, int(*cmp)(const void* e1, const void *e2));
void swap(char *buf1, char  *buf2, int width);

#endif //__BUBBLESORT_H__

// bubble.c
#include"bubble_sort.h"

void swap(char *buf1, char  *buf2, int width)
{
    int i = 0;
    for (i = 0; i < width; i++)
    {
        char tmp = *buf1;
        *buf1 = *buf2;
        *buf2 = tmp;
        buf1++;
        buf2++;
    }
}

// 定义比较规则的回调函数
int cmp_name(const void *e1, const void *e2)
{
    // 强制转换为结构体指针进行成员比较
    return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
}

int cmp_age(const void *e1, const void *e2)
{
    return ((PeoInfo*)e1)->age - ((PeoInfo*)e2)->age;
}

void bubble_sort(void * base, int sz, int width, int(*cmp)(const void* e1, const void *e2))
{
    int i = 0;
    int j = 0;
    for (i = 0; i < sz - 1; i++)
    {
        for (j = 0; j < sz - 1 - i; j++)
        {
            // 计算相邻两个元素的内存地址
            if (cmp((char*)base+width*j, (char*)base+width*(j+1))>0)
            {
                // 如果cmp返回大于0,说明顺序不对,需要交换
                swap((char *)base + width*j, (char*)base + width*(j + 1),width);
            }
        }
    }
}

3. 关键技术与排错建议

内存泄漏防范

在使用 realloc 扩容时,务必先将返回值赋给临时指针。只有当临时指针不为 NULL 时,才将其赋值给原结构体指针。否则一旦 realloc 失败,原指针丢失且无法释放,造成严重的内存泄漏。

scanf 缓冲区残留

在混合使用 scanf 读取数字和字符时,回车键留下的换行符会滞留在缓冲区,导致后续读取字符变量出错。建议在读取字符前调用 getchar() 清空缓冲区,或在格式串中加入空格吸收空白符。

以上就是用C语言实现通讯录的详细内容,更多关于C语言数据结构实战的资料请关注本站其它相关文章!
相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。