高阶C语言|通讯录管理系统(详解+完整代码)

引言

功能:

  • 添加联系人
  • 删除联系人
  • 查找联系人
  • 修改联系人信息
  • 显示所有联系人
  • 清空所有联系人
  • 按名字排序联系人

系统设计

为了实现通讯录的功能,我们首先定义了一个 contact 结构体,包含了一个存储联系人信息的数组和当前已经存储的联系人个数。每个联系人信息是一个 info 结构体,包含姓名、年龄、性别、电话和地址。

typedef struct info {
    char name[MAX_NAME];
    int age;
    char gender[MAX_GENDER];
    char tele[MAX_TELE];
    char addr[MAX_ADDR];
} info;

typedef struct contact {
    info data[MAX_NUM];  // 存储联系人的信息
    int sz;  // 当前已存放联系人的个数
} contact;

功能实现

1. 初始化联系人列表

首先,我们需要一个初始化函数来清空通讯录,并将联系人数量设置为 0。这个功能由 InitContact 函数实现:

void InitContact(contact* pc) {
    assert(pc);
    pc->sz = 0;
    memset(pc->data, 0, sizeof(pc->data));
}
2. 添加联系人

通过 AddNewContact 函数,我们可以添加新的联系人信息。每次添加一个联系人,我们都会提示用户输入联系人姓名、年龄、性别、电话和地址,并将其存储到 data 数组中:

void AddNewContact(contact* pc) {
    assert(pc);
    if (pc->sz == MAX_NUM) {
        printf("联系人已满,无法添加!\n");
        return;
    }
    printf("请输入姓名>");
    scanf("%19s", pc->data[pc->sz].name);
    printf("请输入年龄>");
    scanf("%d", &(pc->data[pc->sz].age));
    printf("请输入性别>");
    scanf("%9s", pc->data[pc->sz].gender);
    printf("请输入电话号码>");
    scanf("%12s", pc->data[pc->sz].tele);
    printf("请输入地址>");
    scanf("%29s", pc->data[pc->sz].addr);

    pc->sz++;
}
3. 查找联系人

通过姓名查找联系人,我们可以使用 FindName 函数在通讯录中查找对应的联系人。如果找到了,就打印出该联系人的详细信息:

int FindName(const char* str, const contact* pc) {
    assert(str);
    for (int i = 0; i < pc->sz; i++) {
        if (strcmp(str, pc->data[i].name) == 0) {
            return i;  // 找到联系人,返回其位置
        }
    }
    return -1;  // 未找到联系人
}
4. 删除联系人

通过 DeleContact 函数,我们可以删除指定的联系人。删除的过程是将该联系人的后续联系人前移一位,并减少联系人数量:

void DeleContact(contact* pc) {
    assert(pc);
    char name[MAX_NAME] = { 0 };
    printf("请输入要删除联系人的姓名>");
    scanf("%s", name);
    int pos = FindName(name, pc);
    if (pos == -1) {
        printf("没有该联系人!\n");
        return;
    }
    for (int i = pos; i < pc->sz - 1; i++) {
        pc->data[i] = pc->data[i + 1];
    }
    pc->sz--;
    printf("删除成功!\n");
}
5. 修改联系人

通过 ModifyContact 函数,我们可以修改联系人的信息。用户选择修改的字段后,程序会提示用户输入新的值并进行更新:

void ModifyContact(contact* pc) {
    assert(pc);
    char name[MAX_NAME] = { 0 };
    printf("请输入要修改联系人的姓名>");
    scanf("%s", name);
    int pos = FindName(name, pc);
    if (pos == -1) {
        printf("没有该联系人!\n");
        return;
    }
    int input = 0;
    do {
        menu2();  // 显示修改选项
        printf("请选择你要修改的项>");
        scanf("%d", &input);
        switch (input) {
            case 0: {
                printf("修改成功!\n");
                break;
            }
            case 1: {
                printf("请输入姓名>");
                scanf("%s", pc->data[pos].name);
                break;
            }
            case 2: {
                printf("请输入年龄>");
                scanf("%d", &(pc->data[pos].age));
                break;
            }
            case 3: {
                printf("请输入性别>");
                scanf("%s", pc->data[pos].gender);
                break;
            }
            case 4: {
                printf("请输入电话号码>");
                scanf("%s", pc->data[pos].tele);
                break;
            }
            case 5: {
                printf("请输入地址>");
                scanf("%s", pc->data[pos].addr);
                break;
            }
            default: {
                printf("数据错误,请重新输入!\n");
                break;
            }
        }
    } while (input);
}
6. 显示所有联系人

通过 DisplayContact 函数,我们可以显示当前所有联系人的信息:

void DisplayContact(const contact* pc) {
    printf("%-20s\t%-4s\t%-5s\t%-20s\t%-12s\n", "名字", "年龄", "性别", "地址", "电话");
    for (int i = 0; i < pc->sz; i++) {
        printf("%-20s\t%-4S\t%-5d\t%-20s\t%-12s\n", pc->data[i].name,
               pc->data[i].gender,
               pc->data[i].age,
               pc->data[i].addr,
               pc->data[i].tele);
    }
}
7. 清空所有联系人

如果我们想要清空通讯录中的所有联系人,可以通过 ClearContact 函数实现:

void ClearContact(contact* pc) {
    assert(pc);
    InitContact(pc);  // 重置通讯录,清空所有联系人
    printf("所有联系人已清空!\n");
}
8. 按名字排序联系人

最后,我们通过 qsort 函数按姓名排序所有联系人:

int CmpByName(const void* e1, const void* e2) {
    return strcmp(((info*)e1)->name, ((info*)e2)->name);
}

void SortContact(contact* pc) {
    assert(pc);
    qsort(pc->data, pc->sz, sizeof(pc->data[0]), CmpByName);
    printf("排序成功!\n");
}

易错点与建议

  1. 输入安全性:使用 scanf 时没有限制输入长度,可能会导致缓冲区溢出。可以在读取字符串时限制输入长度,比如 scanf("%19s", name)
  2. 内存越界检查:在访问数组和指针时,应该时刻注意数组越界的问题,避免读取非法内存。
  3. 模块化设计:目前我们将所有功能集中在一个文件中,可以考虑将各个功能拆分成多个文件,提高代码的可维护性。

完整代码

我将该项目拆分为了3个文件:

  • test.c
  • contact.h
  • contact.c
contact.h — 头文件

头文件通常包含全局变量、宏定义、结构体声明、函数原型等。在这个文件中,我们将声明通讯录相关的所有结构体和函数原型。

#pragma once
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>

// 常量定义
#define MAX_NAME 20
#define MAX_GENDER 10
#define MAX_TELE 13
#define MAX_NUM 1000
#define MAX_ADDR 30

// 联系人信息结构体
typedef struct info {
    char name[MAX_NAME];
    int age;
    char gender[MAX_GENDER];
    char tele[MAX_TELE];
    char addr[MAX_ADDR];
} info;

// 通讯录结构体
typedef struct contact {
    info data[MAX_NUM];  // 存储联系人的信息
    int sz;  // 当前已存放联系人的个数
} contact;

// 函数声明
void InitContact(contact* pc);
void AddNewContact(contact* pc);
void FindContact(const contact* pc);
void DisplayContact(const contact* pc);
void DeleContact(contact* pc);
void ModifyContact(contact* pc);
void ClearContact(contact* pc);
void SortContact(contact* pc);
int FindName(const char* str, const contact* pc);
int CmpByName(const void* e1, const void* e2);
void menu();
void menu2();
void test();
contact.c — 主要功能实现

在这个文件中,我们实现了通讯录的具体功能,包括联系人信息的增删改查、排序等。

#include "contact.h"

// 初始化联系人
void InitContact(contact* pc) {
    assert(pc);
    pc->sz = 0;
    memset(pc->data, 0, sizeof(pc->data));
}

// 添加联系人
void AddNewContact(contact* pc) {
    assert(pc);
    if (pc->sz == MAX_NUM) {
        printf("联系人已满,无法添加!\n");
        return;
    }
    printf("请输入姓名>");
    scanf("%s", pc->data[pc->sz].name);
    printf("请输入年龄>");
    scanf("%d", &(pc->data[pc->sz].age));
    printf("请输入性别>");
    scanf("%s", pc->data[pc->sz].gender);
    printf("请输入电话号码>");
    scanf("%s", pc->data[pc->sz].tele);
    printf("请输入地址>");
    scanf("%s", pc->data[pc->sz].addr);
    pc->sz++;
}

// 查找联系人
int FindName(const char* str, const contact* pc) {
    assert(str);
    for (int i = 0; i < pc->sz; i++) {
        if (strcmp(str, pc->data[i].name) == 0) {
            return i;
        }
    }
    return -1;
}

void FindContact(const contact* pc) {
    assert(pc);
    char name[MAX_NAME] = { 0 };
    printf("请输入要查找联系人的姓名>");
    scanf("%s", name);
    int pos = FindName(name, pc);
    if (pos == -1) {
        printf("没有该联系人!\n");
        return;
    }
    printf("%-20s\t%-4s\t%-5s\t%-20s\t%-12s\n", "名字", "年龄", "性别", "地址", "电话");
    printf("%-20s\t%-4d\t%-5s\t%-20s\t%-12s\n", pc->data[pos].name,
           pc->data[pos].age,
           pc->data[pos].gender,
           pc->data[pos].addr,
           pc->data[pos].tele);
}

// 显示联系人
void DisplayContact(const contact* pc) {
    assert(pc);
    printf("%-20s\t%-4s\t%-5s\t%-20s\t%-12s\n", "名字", "年龄", "性别", "地址", "电话");
    for (int i = 0; i < pc->sz; i++) {
        printf("%-20s\t%-4d\t%-5s\t%-20s\t%-12s\n", pc->data[i].name,
               pc->data[i].age,
               pc->data[i].gender,
               pc->data[i].addr,
               pc->data[i].tele);
    }
}

// 删除联系人
void DeleContact(contact* pc) {
    assert(pc);
    char name[MAX_NAME] = { 0 };
    printf("请输入要删除联系人的姓名>");
    scanf("%s", name);
    int pos = FindName(name, pc);
    if (pos == -1) {
        printf("没有该联系人!\n");
        return;
    }
    for (int i = pos; i < pc->sz - 1; i++) {
        pc->data[i] = pc->data[i + 1];
    }
    pc->sz--;
    printf("删除成功!\n");
}

// 修改联系人
void ModifyContact(contact* pc) {
    assert(pc);
    char name[MAX_NAME] = { 0 };
    printf("请输入要修改联系人的姓名>");
    scanf("%s", name);
    int pos = FindName(name, pc);
    if (pos == -1) {
        printf("没有该联系人!\n");
        return;
    }
    int input = 0;
    do {
        menu2();  // 显示修改选项
        printf("请选择你要修改的项>");
        scanf("%d", &input);
        switch (input) {
            case 1:
                printf("请输入姓名>");
                scanf("%s", pc->data[pos].name);
                break;
            case 2:
                printf("请输入年龄>");
                scanf("%d", &(pc->data[pos].age));
                break;
            case 3:
                printf("请输入性别>");
                scanf("%s", pc->data[pos].gender);
                break;
            case 4:
                printf("请输入电话号码>");
                scanf("%s", pc->data[pos].tele);
                break;
            case 5:
                printf("请输入地址>");
                scanf("%s", pc->data[pos].addr);
                break;
            case 0:
                printf("修改完成!\n");
                break;
            default:
                printf("无效选项,请重新选择!\n");
        }
    } while (input != 0);
}

// 清空所有联系人
void ClearContact(contact* pc) {
    assert(pc);
    InitContact(pc);  // 重置通讯录
    printf("所有联系人已清空!\n");
}

// 排序联系人(按姓名)
int CmpByName(const void* e1, const void* e2) {
    return strcmp(((info*)e1)->name, ((info*)e2)->name);
}

void SortContact(contact* pc) {
    assert(pc);
    qsort(pc->data, pc->sz, sizeof(pc->data[0]), CmpByName);
    printf("排序成功!\n");
}
test.c — 主程序与测试

在这个文件中,我们将测试所有功能,用户可以选择相应的操作,程序会根据用户输入调用不同的函数。

#include "contact.h"

void menu() {
    printf("**************************************\n");
    printf("***** 0.退出        1.添加联系人 *****\n");
    printf("***** 2.查找联系人  3.显示联系人 *****\n");
    printf("***** 4.删除联系人  5.修改联系人 *****\n");
    printf("***** 6.清空联系人  7.排序联系人 *****\n");
    printf("**************************************\n");
}

void test() {
    int input = 0;
    contact con;
    InitContact(&con);  // 初始化通讯录
    do {
        menu();
        printf("请输入数字>");
        scanf("%d", &input);
        switch (input) {
            case 1:
                AddNewContact(&con);
                break;
            case 2:
                FindContact(&con);
                break;
            case 3:
                DisplayContact(&con);
                break;
            case 4:
                DeleContact(&con);
                break;
            case 5:
                ModifyContact(&con);
                break;
            case 6:
                ClearContact(&con);
                break;
            case 7:
                SortContact(&con);
                break;
            case 0:
                printf("退出成功!\n");
                break;
            default:
                printf("数据错误,请重新输入!\n");
                break;
        }
    } while (input != 0);
}

int main() {
    test();
    return 0;
}

通过这种方式,我们将项目分成了多个文件,使得代码更加模块化,便于维护和扩展。在实际开发中,将不同功能封装到不同的源文件中是很常见的做法,可以有效提高代码的可读性和可维护性。

评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值