C语言代码实现通讯录管理系统

 更新时间:2022年06月15日 11:39:34   作者:ღ°九三ฅ՞  
这篇文章主要为大家详细介绍了C语言代码实现通讯录管理系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

本文实例为大家分享了C语言实现通讯录管理系统,供大家参考,具体内容如下

一、需求分析

运用C语言实现一个简单的通讯录管理系统,要求对数据有 增删改查清排显 等功能的实现(这里由于还没学到文件,所以下面所有的存储都是在内存中,也就是当程序结束的时候添加的信息都会清空掉 - 后续会加以改进的)

二、程序结构

在基本的程序设计中,都会将项目分为三个文件:

1、test.c - 主函数(负责调用各个功能的接口)
2、contact.c - 实现各个模块的功能
3、contact.h - 用于存放头文件、宏定义、函数声明等

三、头文件内容的介绍

#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>

#define MAX 1000
#define NAME_MAX 20
#define SEX_MAX 5
#define ADDR_MAX 50
#define TELE_MAX 12

#define DEFAULT_SZ 3

// 数组版本
//typedef struct PeoInfo
//{
//    char name[NAME_MAX];
//    int age;
//    char sex[SEX_MAX];
//    char addr[ADDR_MAX];
//    char tele[TELE_MAX];
//}PeoInfo;
//
//typedef struct Contact
//{
//    //PeoInfo data[MAX];  // 存放信息
//    PeoInfo data[MAX];
//    int sz; // 存放通讯录有几个人的信息
//}Contact;

// 每个人的基本信息
typedef struct PeoInfo
{
    char name[NAME_MAX];
    int age;
    char sex[SEX_MAX];
    char addr[ADDR_MAX];
    char tele[TELE_MAX];
}PeoInfo;

typedef struct Contact
{
    //PeoInfo data[MAX];  // 存放信息
    PeoInfo* data;
    int sz; // 存放通讯录有几个人的信息
    int capacity; // 记录当前通讯录的最大容量
}Contact;


void DestoryContact(Contact* pc);
void InitContact(Contact* pc);
void AddContact(Contact* pc);
void ShowContact(const Contact* pc);
void DeleteContact(Contact* pc);
void SearchContact(const Contact* pc);
void ModifyContact(Contact* pc);
void SortContact(Contact* pc);

里面被注释的代码则是之前的数组版本,规定死了通讯录大小;而下面我们会用动态增容来进行实现。头文件里面用宏定义主要为了之后我们更改对应的大小的时候更加的方便;结构体主要存放的就是三大部分:

1. 一个指向每个人基本信息的结构体指针;2. sz记录着通讯录的记录条数;3. capacity 存放通讯录的容量

四、模块化实现各个功能

(1)主函数实现

int main()
{
    int input = 0;
    Contact con; // 创建通讯录

    InitContact(&con);

    do
    {
        menu();
        printf("请选择:->");
        scanf("%d", &input);

        switch (input)
        {
        case ADD:
            AddContact(&con);
            break;
        case DEL:
            DeleteContact(&con);
            break;
        case SEARCH:
            SearchContact(&con);
            break;
        case MODIFY:
            ModifyContact(&con);
            break;
        case SORT:
            SortContact(&con);
            break;
        case SHOW:
            ShowContact(&con);
            break;
        case EXIT:
            DestoryContact(&con);
            printf("退出\n");
            break;
        default:
            printf("输入错误,请重新输入\n");
            break;
        }
    } while (input);

    return 0;
}

一般函数的主函数里面都可以用do ……while结构来控制程序的执行顺序,在case后面用的则是 枚举常量 ,这样能做到更好的见名知意。

(2)初始化通讯录

void InitContact(Contact* pc)
{
    assert(pc);
    pc->sz = 0;
    PeoInfo* tmp = (PeoInfo*)malloc(DEFAULT_SZ * sizeof(PeoInfo));
    if (tmp != NULL)
    {
        pc->data = tmp;
    }
    else
    {
        printf("InitContact()::%s\n", strerror(errno));
    }

    pc->capacity = DEFAULT_SZ;
}

这里设定了通讯录的起始大小,后面可以通过动态增容来扩大(而数组版本的话就没有这么灵活了)

(3)添加联系人信息

void check_capacity(Contact* pc)
{
    assert(pc);
    if (pc->sz == pc->capacity)
    {
        // 增容
        PeoInfo* tmp = (PeoInfo*)realloc(pc->data, (pc->capacity + 2)*sizeof(PeoInfo));
        if (tmp != NULL)
        {
            pc->data = tmp;
            pc->capacity += 2;
            printf("增容成功\n");
        }
        else
        {
            printf("InitContact()::%s\n", strerror(errno));
        }
    }
}

void AddContact(Contact* pc)
{
    assert(pc);
    check_capacity(pc);

    // 添加内容
    printf("名字:");
    scanf("%s", pc->data[pc->sz].name);
    printf("年龄:");
    scanf("%d", &(pc->data[pc->sz].age));
    printf("性别:");
    scanf("%s", pc->data[pc->sz].sex);
    printf("地址:");
    scanf("%s", pc->data[pc->sz].addr);
    printf("电话:");
    scanf("%s", pc->data[pc->sz].tele);
    
    pc->sz++;
    printf("添加成功\n");
}

这里实现了一个check_capacity 的接口,用来判断当前通讯录是否存满了;若满了则进行扩容(一般按2倍进行扩容),注意:添加成功后需要对 sz++

(4)删除联系人信息

int FindByName(const Contact* pc, char* name)
{
    int i = 0;
    for (i = 0; i < pc->sz; i++)
    {
        if (strcmp(pc->data[i].name, name) == 0)
        {
            return i;
        }
    }
    return -1;
}

void DeleteContact(Contact* pc)
{
    char name[NAME_MAX] = { 0 };
    assert(pc);

    if (pc->sz == 0)
    {
        printf("通讯录为空\n");
        return;
    }

    // 根据输入的人名进行删除
    printf("请输入要删除的人名:");
    scanf("%s", name);

    int pos = FindByName(pc, name);

    if (pos == -1)
    {
        printf("要删除的人不存在\n");
    }
    else
    {
        // 删除
        //int j = 0;
        //for (j = pos; j < pc->sz-1; j++)  // sz-1为了防止j+1非法访问
        //{
        //    pc->data[j] = pc->data[j + 1];
        //}

        //pc->sz--;
        //printf("删除成功\n");
        //if (pc->sz>1)
        memmove((pc->data) + pos, (pc->data) + pos + 1, (pc->sz - pos)*sizeof(PeoInfo));
        pc->sz--;
    }
}

这里我就只做了按照名字进行删除,你们也可以都尝试一下哟。下面写了两种删除方法(这里的按名字删除前需要进行查找,因此我们也可以直接使用我们下面要实现的查找函数,但如果查找里面进行了其他功能,那时不能直接使用的哟)

(5)查找联系人信息

void SearchContact(const Contact* pc)

{
    char name[NAME_MAX] = { 0 };
    assert(pc);

    if (pc->sz == 0)
    {
        printf("通讯录为空\n");
        return;
    }

    printf("请输入要查找的姓名:");
    scanf("%s", name);
    int i = FindByName(pc, name);

    if(-1 == i)
    {
        // 没有找到
        printf("你要查找的人不存在\n");
    }
    else
    {
        // 找到了
        printf("%-10s\t%-5s\t%-5s\t%-20s\t%-13s\n", 
                "姓名", "年龄", "性别", "地址", "电话");
        printf("%-10s\t%-5d\t%-5s\t%-20s\t%-13s\n",
            pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].addr, pc->data[i].tele);
    }
}

这里如果查找到了后,我打印显示了一下

(6)更改联系人信息

void ModifyContact(Contact* pc)
{
    char name[NAME_MAX] = { 0 };
    assert(pc);

    if (pc->sz == 0)
    {
        printf("通讯录为空\n");
        return;
    }

    printf("请输入要修改信息的姓名:");
    scanf("%s", name);

    int i = FindByName(pc, name);

    // 修改内容
    printf("新的名字:");
    scanf("%s", pc->data[i].name);
    printf("新的年龄:");
    scanf("%d", &(pc->data[i].age));
    printf("新的性别:");
    scanf("%s", pc->data[i].sex);
    printf("新的地址:");
    scanf("%s", pc->data[i].addr);
    printf("新的电话:");
    scanf("%s", pc->data[i].tele);

    printf("修改后的信息为:\n");

    printf("%-10s\t%-5s\t%-5s\t%-20s\t%-13s\n", "姓名", "年龄", "性别", "地址", "电话");
    printf("%-10s\t%-5d\t%-5s\t%-20s\t%-13s\n",
        pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].addr, pc->data[i].tele);
}

(7)显示所有联系人信息

void ShowContact(const Contact* pc)
{
    assert(pc);
    // 打印标题
    printf("%-10s\t%-5s\t%-5s\t%-20s\t%-13s\n", "姓名", "年龄", "性别", "地址", "电话");
    int i = 0;
    for (i = 0; i < pc->sz; i++)
    {
        printf("%-10s\t%-5d\t%-5s\t%-20s\t%-13s\n",
            pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].addr, pc->data[i].tele);
    }
}

直接遍历打印即可

(8)对联系人信息进行排序

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

int cmp_by_age(const void* e1, const void* e2)
{
    return ((PeoInfo*)e1)->age - ((PeoInfo*)e2)->age;
    // return strcmp(, ((PeoInfo*)e2)->name);
}

void SortContact(Contact* pc)
{
    int choice = 0;
    printf("请选择:(1、按姓名;2、按年龄)");
    scanf("%d", &choice);

    if (1 == choice)
    {
        qsort(pc->data, pc->sz, sizeof(pc->data[0]), cmp_by_name);

        printf("排序后的数据:\n");
        ShowContact(pc);
    }
    else if (2 == choice)
    {
        qsort(pc->data, pc->sz, sizeof(pc->data[0]), cmp_by_age);
        printf("排序后的数据:\n");
        ShowContact(pc);
    }
    else
    {
        printf("输入有误\n");
    }
}

这里使用qsort实现的排序,可以对 姓名 或 年龄进行排序的

(9)退出时销毁通讯录

void DestoryContact(Contact* pc)
{
    assert(pc);
    free(pc->data);
    pc->data = NULL;
}

直接将里面指向有效内容的 pc->data 进行free释放就行,因为它本来就是动态开辟的,后面要有个好习惯,free之后将其置为NULL,不然就为野指针了。

总结:

以上就是动态增容版本通讯录的全部代码,尽管用了动态增容,里面仍然会存在很多的空间浪费,后面等博主学了链表后,在进行改进。

希望这篇文章对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • 解析sizeof, strlen, 指针以及数组作为函数参数的应用

    解析sizeof, strlen, 指针以及数组作为函数参数的应用

    本篇文章是对sizeof, strlen, 指针以及数组作为函数参数的应用进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • 一个win32窗口创建示例

    一个win32窗口创建示例

    这篇文章主要介绍了一个win32窗口创建示例,需要的朋友可以参考下
    2014-04-04
  • C++代码实现贪吃蛇小游戏

    C++代码实现贪吃蛇小游戏

    这篇文章主要为大家详细介绍了C++贪吃蛇小游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-11-11
  • C++ 回调接口设计和二进制兼容详细

    C++ 回调接口设计和二进制兼容详细

    再开发视频编辑 SDK,SDK的回调接口设计成 C 风格,结构中放着一些函数指针,既然对外接口是 C++,为什么不直接使用 C++ 的虚函数?这篇文章便对这一问题做个详细介绍,需要的朋友可以参考一下
    2021-09-09
  • C++实现LeetCode(188.买卖股票的最佳时间之四)

    C++实现LeetCode(188.买卖股票的最佳时间之四)

    这篇文章主要介绍了C++实现LeetCode(188.买卖股票的最佳时间之四),本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-08-08
  • C++ 详细讲解stack与queue的模拟实现

    C++ 详细讲解stack与queue的模拟实现

    C++ Stack(堆栈) 是一个容器类的改编,为程序员提供了堆栈的全部功能,也就是说实现了一个先进后出(FILO)的数据结构,许多程序都使用了 queue 容器。queue 容器可以用来表示超市的结账队列或服务器上等待执行的数据库事务队列
    2022-04-04
  • VS2022调试通过海康摄像头烟火识别SDK的实现

    VS2022调试通过海康摄像头烟火识别SDK的实现

    本文主要介绍了VS2022调试通过海康摄像头烟火识别SDK的实现,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-02-02
  • C语言中的函数指针基础学习教程

    C语言中的函数指针基础学习教程

    这篇文章主要介绍了C语言中的函数指针基础学习教程,包括函数指针作为参数来传递等重要知识,需要的朋友可以参考下
    2016-04-04
  • C语言实现贪吃蛇代码

    C语言实现贪吃蛇代码

    这篇文章主要为大家详细介绍了C语言实现贪吃蛇代码,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-11-11
  • C++11语法之右值引用的示例讲解

    C++11语法之右值引用的示例讲解

    右值引用,一般是在深拷贝的类,实现移动构造和移动赋值,能够解决左值引用无法做到的传返回值的效率问题,下面跟随小编一起学习下C++11语法之右值引用的问题
    2022-04-04

最新评论