【数据结构】实现通讯录:基于C语言动态顺序表


前言

通讯录本质上是顺序表的应用层封装 。底层用动态顺序表管理内存,上层把存储类型从 int 换成自定义结构体 peoInfo,所有增删改查逻辑复用顺序表接口。

理解这篇文章需要先掌握动态顺序表,可参考:顺序表实现


一、项目结构

复制代码
Contact/
├── SeqList.h       // 顺序表声明
├── SeqList.c       // 顺序表实现
├── Contact.h       // 通讯录声明 & 联系人结构体
├── Contact.c       // 通讯录功能实现
└── main.c          // 菜单与主循环

设计思路Contact 就是 SL(顺序表)的别名,SLDateType 替换为 peoInfo,实现完全复用底层代码。


二、联系人结构体

cpp 复制代码
// Contact.h
#pragma once
#include <string.h>

#define NAME_MAX  20
#define GENDER_MAX 10
#define TEL_MAX   20
#define ADDR_MAX  100

typedef struct PersonInfo {
    char name[NAME_MAX];
    char gender[GENDER_MAX];
    int  age;
    char tel[TEL_MAX];
    char addr[ADDR_MAX];
} peoInfo;

要点:字段全用定长字符数组,避免指针管理复杂度,适合嵌入顺序表的连续内存。


三、顺序表适配

将顺序表头文件中的数据类型替换:

复制代码
// SeqList.h(节选)
#include "Contact.h"
typedef peoInfo SLDateType;   // 核心改动:把 int 换成 peoInfo

其余顺序表代码(SLInit / SLDestroy / SLPushBack / SLErase无需改动,直接复用。


四、通讯录功能实现

4.1 初始化 & 销毁

cpp 复制代码
void ContactInit(Contact* con) {
    SLInit(con);      // 直接调用顺序表初始化
}

void ContactDestroy(Contact* con) {
    SLDestroy(con);   // 释放堆内存,防止泄漏
}

4.2 添加联系人

cpp 复制代码
void ContactAdd(Contact* con) {
    peoInfo info;
    printf("姓名: ");  scanf("%s", info.name);
    printf("性别: ");  scanf("%s", info.gender);
    printf("年龄: ");  scanf("%d", &info.age);
    printf("电话: ");  scanf("%s", info.tel);
    printf("地址: ");  scanf("%s", info.addr);
    SLPushBack(con, info);   // 尾插到顺序表
    printf("添加成功!\n");
}

注意con 本身已经是指针,传给 SLPushBack 时不需要再取地址。

4.3 按姓名查找(核心辅助函数)

cpp 复制代码
// 返回下标,找不到返回 -1
int FindByName(Contact* con, char name[]) {
    for (int i = 0; i < con->size; i++) {
        if (strcmp(con->arr[i].name, name) == 0)
            return i;
    }
    return -1;
}

面试考点 :字符串比较必须用 strcmp,不能用 ==(比较的是指针地址)。

4.4 删除联系人

cpp 复制代码
void ContactDel(Contact* con) {
    char name[NAME_MAX];
    printf("请输入要删除的姓名: ");
    scanf("%s", name);

    int pos = FindByName(con, name);
    if (pos == -1) { printf("联系人不存在\n"); return; }

    SLErase(con, pos);   // 顺序表按下标删除
    printf("删除成功!\n");
}

4.5 修改联系人

cpp 复制代码
void ContactModify(Contact* con) {
    char name[NAME_MAX];
    printf("请输入要修改的姓名: ");
    scanf("%s", name);

    int pos = FindByName(con, name);
    if (pos == -1) { printf("联系人不存在\n"); return; }

    // 直接通过下标修改结构体字段
    printf("新姓名: ");  scanf("%s", con->arr[pos].name);
    printf("新性别: ");  scanf("%s", con->arr[pos].gender);
    printf("新年龄: ");  scanf("%d", &con->arr[pos].age);
    printf("新电话: ");  scanf("%s", con->arr[pos].tel);
    printf("新地址: ");  scanf("%s", con->arr[pos].addr);
    printf("修改成功!\n");
}

4.6 查找 & 展示

cpp 复制代码
void ContactFind(Contact* con) {
    char name[NAME_MAX];
    printf("请输入要查找的姓名: ");
    scanf("%s", name);

    int pos = FindByName(con, name);
    if (pos == -1) { printf("联系人不存在\n"); return; }

    printf("%-10s %-6s %-4s %-12s %s\n", "姓名","性别","年龄","电话","地址");
    printf("%-10s %-6s %-4d %-12s %s\n",
        con->arr[pos].name, con->arr[pos].gender,
        con->arr[pos].age,  con->arr[pos].tel,
        con->arr[pos].addr);
}

void ContactShow(Contact* con) {
    printf("%-10s %-6s %-4s %-12s %s\n", "姓名","性别","年龄","电话","地址");
    for (int i = 0; i < con->size; i++) {
        printf("%-10s %-6s %-4d %-12s %s\n",
            con->arr[i].name, con->arr[i].gender,
            con->arr[i].age,  con->arr[i].tel,
            con->arr[i].addr);
    }
}

优化点 :用 %-Ns 格式化对齐,打印更整洁,面试官会注意细节。


五、主菜单

cpp 复制代码
// main.c
#include "Contact.h"

void menu() {
    printf("==== 通讯录 ====\n");
    printf("1. 添加联系人\n");
    printf("2. 删除联系人\n");
    printf("3. 修改联系人\n");
    printf("4. 查找联系人\n");
    printf("5. 展示所有\n");
    printf("0. 退出\n");
}

int main() {
    Contact con;
    ContactInit(&con);
    int op = -1;
    do {
        menu();
        printf("请输入操作: ");
        scanf("%d", &op);
        switch (op) {
            case 1: ContactAdd(&con);      break;
            case 2: ContactDel(&con);      break;
            case 3: ContactModify(&con);   break;
            case 4: ContactFind(&con);     break;
            case 5: ContactShow(&con);     break;
            case 0: printf("退出\n");      break;
            default: printf("输入有误\n"); break;
        }
    } while (op != 0);

    ContactDestroy(&con);
    return 0;
}

六、常见面试问题

问题 答案要点
为什么不用 == 比较字符串? == 比较指针地址;字符串内容比较用 strcmp
realloc 失败怎么处理? 用临时指针接收返回值,为 NULL 则报错退出,不能直接赋给原指针(会丢失原内存)
SLErase 删除的时间复杂度? O(n),需要移动后续所有元素
通讯录怎么持久化(扩展方向)? 增加 ContactSave / ContactLoad,用 fwrite / fread 将结构体数组写入二进制文件

总结

通讯录 = 顺序表 + 自定义结构体,核心改动只有两处

  1. SLDateTypeint 换成 peoInfo
  2. 查找时用 strcmp 代替 ==,打印时通过 .字段名 解引用

掌握这个项目,顺序表的泛化思想和结构体应用就都打通了。

相关推荐
tankeven2 小时前
动态规划专题(06):树形动态规划(未完待续)
c++·算法·动态规划
睡觉就不困鸭2 小时前
第13天 四数相加II
数据结构·哈希算法·散列表
米粒12 小时前
力扣算法刷题 Day 52
算法·leetcode·职场和发展
覆东流2 小时前
第6天:python综合练习——制作简易计算器
开发语言·后端·python
今儿敲了吗2 小时前
应用实战2:新闻列表
学习·算法
waves浪游2 小时前
进程间通信(上)
linux·运维·服务器·开发语言·c++
圆弧YH2 小时前
Python→ Bookmark
开发语言·python
珎珎啊2 小时前
Python3 数据结构
数据结构·python
hhb_6182 小时前
C Shell脚本编程与系统管理技术实践指南
java·c语言·开发语言