C语言 - 制作一个可以容纳一千人的本地通讯录

本章目录


前言

        本期文章教大家制作一个属于自己的本地通讯录,对通讯录的要求有:

        1、可以容纳1000人的信息,储存信息的空间要随着存储数量的增加而增大。

        2、存储的信息可以保存在本地,当程序运行时可以自动读取本地信息文件。

        3、可以对信息进行增、删、改、查、排序、销毁本地信息文件等功能。


一、菜单制作

        首先,我们需要制作一个菜单来方便我们选择功能

enum Option
{
    Exit,
    Add,
    Del,
    Search,
    Modify,
    Sort,
    Print,
    Destroy
};

void Menu()
{
    printf("————————————————————————————————\n");
    printf("————————————————————————————————\n");
    printf("————————1、增加   2、删除———————\n");
    printf("————————3、查找   4、改正———————\n");
    printf("————————5、排序   6、打印———————\n");
    printf("————————7、销毁   0、退出———————\n");
    printf("————————————————————————————————\n");
    printf("————————————————————————————————\n");
}

void Choice()
{
    int input;
    do
    {
        Menu();
        printf("请选择:>");
        scanf("%d", &input);
        switch (input)
        {
            case Add:break;
            case Del:break;
            case Search:break;
            case Modify:break;
            case Sort:break;
            case Print:break;
            case Destroy:break;
            case Exit:break;
            default:printf("选择错误, 请重新选择!\n");break;
        }
    } while (input);
}

        此时,利用枚举和Switch就可以制作出一个可以供我们选择的菜单了。

二、创建通讯录

1、创建人员信息结构体

        在创建通讯录之前,我们应该知道我们的通讯录结构。

        通讯录应该包含:1、人员名字    2、性别    3、年龄    4、电话号码    5、地址等类型

struct PeoInfo
{
    char name[10];
    char sex[2];
    int age;
    long long tele[11];
    char addr[50];
} PeoInfo;

         我们这里就很好的创建出了通讯录的结构变量。


        但是问题出现了,如果这样把数组的大小写死了,那么到后期维护时,需要更改大小,那么将很麻烦

        所以我们应该使用 #define 为他们定义标识符

        并且把PeoInfo结构体重新起一个新的名字,这样在后期写代码时就更加方便

#define MAX_NAME 20
#define MAX_SEX 5
#define MAX_TELE 11
#define MAX_ADDR 30

typedef struct PeoInfo //对结构体重命名
{
    char name[MAX_NAME];
    char sex[MAX_SEX];
    int age;
    long long tele[MAX_TELE];
    char addr[MAX_ADDR];
} PeoInfo;

2、创建完整通讯录结构体

        当我们创建好上面的结构体之后呢,我们还需要创建一个结构体,用来记录人员信息结构体。

typedef struct Contact
{
    PeoInfo *data;

    int sz;

    int capacity;

} Contact;

        PeoInfo *data  ---   用来指向为通讯录动态申请的空间(后面会讲到)

        int sz   ---   用来记录当前通讯录人员有效信息,下一次人员存放为在sz+1的位置

        int capacity   ---   记录当前空间最多能存储多少信息,因为当空间 sz == capacity时,则当前空间已满。

        接下来,我们就可以在Choice函数中添加一句,来创建通讯录了。

    Contact con; 

 3、初始化通讯录

        因为我们只是在上面创建了通讯录,但是还没初始化,所以接下来我们需要写一个初始化函数。

#define DEFAULT_SZ 3
#define INC_SZ 2

void InitContact(Contact *pc)
{
    pc->data = (PeoInfo *)malloc(DEFAULT_SZ * sizeof(PeoInfo));
    if (pc->data == NULL)
    {
        perror("InitContact");
        return;
    }

    pc->sz = 0;

    pc->capacity = DEFAULT_SZ;

}

        1、需要定义两个标识符, 

                DEFAULT_SZ  ---  为通讯录的初始化大小,我们这里初始化为3

                INC_SZ   ---   作为通讯录空间不够时,为通讯录新开辟的空间大小,每次开辟2个空间

        2、因为我们之前已经创建好了data变量,而我们现在用data维护malloc函数开辟的空间。(malloc首先开辟3个PeoInfo大小的空间,然后强制转换为PeoInfo类型,在交给data指针维护)

        3、当data为NULL时,则开辟失败(有可能为堆空间不足)

        4、将sz初始化为0,因为此时还没有人员信息

        5、将capacity初始化为通讯录初始大小,也就是3个人员的空间大小

         当我们初始化好通讯录之后,就可以开始往里面存入数据了。

4、存放数据

        在存放数据之前,需要大家了解C语言的文件函数的使用和其原理。接下来就是使用文件函数来存放数据和读入数据

void LoadContact(Contact *pc)
{
    //打开文件
    FILE *pf = fopen("contact.dat", "r");
    if (pf == NULL)
    {
        perror("LoadContact:");
        return;
    }

    //读文件
    PeoInfo tmp = {0};
    while (fread(&tmp, sizeof(PeoInfo), 1, pf))
    {
        //考虑是否需要整容
        CheckCapacity(pc);
        pc->data[pc->sz] = tmp;
        pc->sz++;
    }

    //关闭文件
    fclose(pf);
    pf = NULL;
}

        1、利用fopen文件操作函数,可以打开文件(如果文件不存在则返回NULL,这里设置如果为NULL就退出这个函数,所以就不影响后面的操作)

        2、当不等于NUL,那就说明已经成功打开了文件。

        3、我们需要创建一个临时的结构体变量tmp用于存放从文件中读取的信息。

        4、我们直接可以在while循环中加入表达式,从 pf指针 指向的空间中读取一个PeoInfo大小的文件,然后存储在tmp变量中。当fread函数从文件中读取不到信息时,则返回0,循环则结束

        5、在存放到data之前,需要判断当前的空间够不够我们存放(函数在下面)

        6、当存放完成后,我们需要关闭文件,并且将 指针置为NULL,防止内存泄漏

5、判断空间容量

        因为本文章使用动态存储,所以一开始并不可能开辟很大的空间,所以当空间存满之后,必然需要开辟一块新的空间,所以这部分要求大家需要内存函数的基础接下来是教大家写一个函数开辟空间。

void CheckCapacity(Contact *pc)
{
    if (pc->sz == pc->capacity)
    {
        PeoInfo *tmp = (PeoInfo *)realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(PeoInfo));

        if (tmp != NULL)
        {
            pc->data = tmp;
            pc->capacity += INC_SZ;
            printf("已增加2位空间!\n");
        }
        else
        {
            perror("AddContct");
            printf("增加失败!\n");
            return;
        }
    }
}

        1、在前文我们可以了解到,sz 和 capacity 这两个变量一个是记录我们 通讯录有效信息的,另一个为 记录当前空间最大容量 

        2、所以当两个变量相等时,就意味着当前空间已经存满了,所以我们必须扩容了

        3、首先,先创建一个 PeoInfo *类型的 tmp 变量用于临时存储使用realloc函数开辟的空间地址。

        (这一段的含义为:使用 realloc函数 为pc->data开辟一块 pc->capacity(初始化大小为3) + INC_SZ(定义的为2) 再乘上 PeoInfo 大小的空间 , 所以就意味着每次增加2个PeoInfo大小的空间 然后再转为(PeoInfo *)类型,再将地址给tmp存储)

        4、当空间开辟完成时,如果tmp为NULL,那么就开辟失败

        5、如果不为NULL时,那么就开辟成功,这时就需要将地址再转给 pc->data 管理

        6、最大容量(capacity)也需要增加两个位置

三、各功能的实现

1、增加人员信息的功能

        当我们做好上面的准备工作时,此时就需要开始写功能函数了,首先我们先写增加功能。

void AddContct(Contact *pc) //增加
{
    CheckCapacity(pc);

    //增加一个信息
    printf("\n请输入姓名:> ");
    scanf("%s", pc->data[pc->sz].name);

    printf("请输入性别:> ");
    scanf("%s", pc->data[pc->sz].sex);

    printf("请输入年龄:> ");
    scanf("%d", &pc->data[pc->sz].age);

    printf("请输入手机号:> ");
    scanf("%lld", pc->data[pc->sz].tele);

    printf("请输入地址:> ");
    scanf("%s", pc->data[pc->sz].addr);

    pc->sz++;

    printf("\n增加成功!\n");
}

        1、跟之前一样,增加一个人员信息时,需要先判断空间是否足够大。

        2、然后就使用 scanf函数 将输入的信息存入对应的位置就行

        3、输入完成后 sz++ 下次就可以直接存放到下一个空间了

2、按名字查找的功能(内部使用)

        在写其他的删、改、查、排序等功能时,需要一个前提,在删除某个人员信息时,是需要找到该人员信息的,所以我们需要先写一个查找人员的函数,但是与 查 功能不同,此函数是用于内部查找的,只是给电脑使用的,所以是不需要打印出来的。

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

        1、因为只是在该文件中调用的,所以可以加上static修饰一下

        2、 这里就是使用最简单的暴力求解了,没有涉及什么算法

        3、当每一次找到名字的时候就使用 strcmp函数 和输入的名字对比一下,如果一样 strcmp函数就会返回0,那代表我们找到了

        4、找到之后就会返回名字对应的下标位置,如果没找到就返回-1

3、删除人员信息的功能

        写完了必要的查找名字功能,这时我们就可以写删除功能了

void DelContct(Contact *pc)
{
    char Name[MAX_NAME] = {0};
    if (pc->sz == 0)
    {
        printf("当前通讯录为空, 无需删除!\n");
        return;
    }

    printf("请输入要删除人的名字:> ");
    scanf("%s", Name);

    // 查找要删除的人
    int pos = FindByName(pc, Name);

    // 没有 - 退出
    if (pos == -1)
    {
        printf("查无此人!\n");
        return;
    }

    // 有 - 删除 - 往前挪
    for (size_t i = pos; i < pc->sz - 1; i++)
    {
        pc->data[i] = pc->data[i + 1];
    }
    pc->sz--;

    printf("删除成功!\n");
}

        1、创建一个 字符数组Name 用于存放scanf输入的名字,以便我们好查找这个名字

        2、当 sz为0时,就代表着当前通讯录是空的所以不需要删除

        3、当Name接收到名字后,此时我们前面的查找名字的函数就起了作用,使用该函数就可以很轻松的得到该名字的下标。

        4、找到之后,我们只需要将后面的数据全部往前挪,把该信息覆盖掉,再将 sz有效信息-- ,就等于删除了这个人员的信息

4、更改人员信息的功能 

        更改人员信息的功能与其他功能大同小异。

void ModifyContct(Contact *pc)
{
    char Name[MAX_NAME] = {0};
    printf("请输入要修改人的名字:> ");
    scanf("%s", Name);
    int pos = FindByName(pc, Name);
    if (pos == -1)
        printf("查无此人!\n");
    else
    {
        printf("\n请输入姓名:> ");
        scanf("%s", pc->data[pos].name);

        printf("请输入性别:> ");
        scanf("%s", pc->data[pos].sex);

        printf("请输入年龄:> ");
        scanf("%d", &pc->data[pos].age);

        printf("请输入手机号:> ");
        scanf("%lld", pc->data[pos].tele);

        printf("请输入地址:> ");
        scanf("%s", pc->data[pos].addr);

        printf("修改成功!\n");
        printf("%-0s\t%7s\t%7s\t%7s\t%15s\t%15s\n", "序号", "姓名", "性别", "年龄", "手机号", "地址");
        printf("%2d\t%5s\t%6s\t%5d\t%13lld\t%15s\n", pos + 1,
               pc->data[pos].name,
               pc->data[pos].sex,
               pc->data[pos].age,
               pc->data[pos].tele[0],
               pc->data[pos].addr);
    }
}

        1、首先对要改的名字进行查找,获取下标

        2、再重新输入信息,覆盖之前的信息

        3、完成后,再打印一遍

5、查找指定联系人

        增删改查中,我们还剩个查的功能,在上面我们已经实现过一遍了,这次我们只需要加入打印功能即可

void SearchContct(Contact *pc)
{
    char Name[MAX_NAME] = {0};
    printf("请输入要查找人的名字:> ");
    scanf("%s", Name);

    int pos = FindByName(pc, Name);
    if (pos == -1)
    {
        printf("查无此人!\n");
    }
    else
    {
        printf("%-0s\t%7s\t%7s\t%7s\t%15s\t%15s\n", "序号", "姓名", "性别", "年龄", "手机号", "地址");

        printf("\n%2d\t%5s\t%6s\t%5d\t%13lld\t%15s\n", pos + 1,
               pc->data[pos].name,
               pc->data[pos].sex,
               pc->data[pos].age,
               pc->data[pos].tele[pos],
               pc->data[pos].addr);
    }
}

        1、因为上面已经写过一个查找名字的功能了,所以我们这只需要调用该函数即可

        2、将获取的名字传入 FindByName函数 中,如果返回-1就代表查找失败

        3、如果不是-1,那么就直接打印出来

6、按照人员姓名排序的功能 

        接下来教大家如何制作排序的功能,我们需要写一个自己的qsort函数,所以要求有qsort函数的基础(在我的前几章博客中有写)

int MyStrcmp(const char *s1, const char *s2) // MyStrcmp
{
    assert(s1 && s2);
    while (*s1 == *s2)
    {
        if (*s1 == '\0')
        {
            return 0;
        }
        s1++;
        s2++;
    }

    return *s1 > *s2 ? 1 : -1;
}

void Swap(char *e1, char *e2, int width) // MyQsort - 冒泡排序
{
    for (int i = 0; i < width; i++)
    {
        char tmp = *e1;
        *e1 = *e2;
        *e2 = tmp;
        e1++;
        e2++;
    }
}

void MyQsort(void *base, int ret, int width, int (*cmp)(const void *e1, const void *e2)) // MyQsort
{
    //趟数
    for (int i = 0; i < ret - 1; i++)
    {
        //一趟的排序
        for (int j = 0; j < ret - 1 - i; j++)
        {
            if (cmp((char *)base + j * (width), (char *)base + (j + 1) * (width)) > 0)
            {
                //升序交换
                Swap((char *)base + j * (width), (char *)base + (j + 1) * (width), width);
            }
        }
    }
}

int SortByname(const void *e1, const void *e2) // MyQsort - 升序
{
    return MyStrcmp(((PeoInfo *)e1)->name, (((PeoInfo *)e2)->name));
}

void SortContct(Contact *pc) //排序功能
{
    //使用qsort函数进行排序
    MyQsort(pc->data, pc->sz, sizeof(pc->data[0]), SortByname);
    
    printf("排序成功!\n");
}

        1、qsort库函数使用的快速排序,我们模拟的MyQsort函数使用的是冒泡排序,也是最简单的

        2、qsort的核心思想是需要我们自己写出一个对比两个数函数,因为qsort不知道我们对比的是什么类型的数据

        3、所以我们需要创建一个对比名字大小的函数,为SortByname,因为名字是字符串,而不可以直接使用大于小于号对比,所以函数内部使用Strcmp函数进行对比,这里我们也写出一个我们自己的MyStrcmp

        4、写好后,我们将 数组的地址、数组的元素个数、每个元素的大小、对比名字大小的函数 都传入进去

        5、当程序走完时,也代表人员已经按照名字排好序了

7、销毁人员信息的功能

        因为按照要求,我们写的是有自动保存的功能,当人员信息多了起来后,一个一个删除太麻烦,所以我们需要一个一键销毁的功能。

void DestroyContct(Contact *pc)
    FILE *pf = fopen("Contact.dat", "w");
    if (pf == NULL)
    {
        perror("SaveContct:");
        return;
    }
    pc->sz = pc->capacity = 0;
    printf("销毁成功!\n");
}

        1、当 fopen函数 使用 ' w ' 形式打开文件时,如果文件里有数据,那么会直接清空,利用这个性质,我们就可以清空文件的人员信息了

        2、当文件信息清除后,sz 和 capacity 变量也需要我们置为0,当下次打开文件后,就是一个全新的文件了

8、退出保存的功能

        接下来就是写退出保存的功能了,需要大家有 文件操作函数 的基础


void SaveContct(Contact *pc) //保存
{
    FILE *pf = fopen("Contact.dat", "w");
    if (pf == NULL)
    {
        perror("SaveContct:");
        return;
    }

    //写文件
    for (int i = 0; i < pc->sz; i++)
    {
        fwrite(pc->data + i, sizeof(PeoInfo), 1, pf);
    }

    //关闭文件
    fclose(pf);
    pf = NULL;
    printf("保存成功,");
}

        1、原理也很简单,文件以 ' w ' 形式打开,如果没有则自己创建一个名字为 " Contact.dat " ,当然也可以想改什么名也行

        2、当 pf变量 不等于NULL时,那么就说明打开了该文件,文明此时就可以往里面写数据了

        3、因为此时我们使用增加功能写的数据还保存在内存的数组中,所以我们需要一个一个的往里面写入数组的数据

        (使用 fwrite文件操作函数 就可以从 ps->data 往 pf指向的文件中 写入 PeoInfo大小的数据 )

        4、写入完成后,我们需要关闭文件,并将pf置为NULL

         当然,保存完后,我们还有一步需要完成,那就是退出功能

void ExitContct(Contact *pc)
{
    free(pc->data);
    pc->data = NULL;

    pc->sz = pc->capacity = 0;
    printf("已退出!\n");
}

        1、这一步也很重要,退出整个工程前,我们需要将 pc->data指向的空间 free,不然会存在内存泄漏,接着将 data置为NULL

        2、sz 和 capacity 变量也需要我们置为0

四、添加功能

        我们制作完功能后,还没有添加到菜单中,此时选择菜单还是没有功能的,所以我们需要添加上去

void Choice()
{
    int input;
    Contact con;
    InitContact(&con);

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

        switch (input)
        {
        case Add:
            AddContct(&con);
            break;
        case Del:
            DelContct(&con);
            break;
        case Search:
            SearchContct(&con);
            break;
        case Modify:
            ModifyContct(&con);
            break;
        case Sort:
            SortContct(&con);
            break;
        case Print:
            PrintContct(&con);
            break;
        case Destroy:
            DestroyContct(&con);
            break;
        case Exit:
            SaveContct(&con);
            ExitContct(&con);
            break;
        default:
            printf("选择错误, 请重新选择!\n");
            break;
        }

    } while (input);
}

         此时,我们就可以在正常的使用整个工程了。

五、完整代码 

        已经合为一个文件了

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

#define MAX_NAME 50
#define MAX_SEX 10
#define MAX_TELE 11
#define MAX_ADDR 30

#define DEFAULT_SZ 3 //初始化大小
#define INC_SZ 2     //每次增加大小

typedef struct PeoInfo //对结构体重命名
{
    char name[MAX_NAME];
    char sex[MAX_SEX];
    int age;
    long long tele[MAX_TELE];
    char addr[MAX_ADDR];
} PeoInfo;

typedef struct Contact
{
    // data 指向动态申请的空间,用来存放联系人的信息
    PeoInfo *data;

    //记录人员的数量
    int sz;

    //记录当前通讯录的最大容量
    int capacity;

} Contact;

void CheckCapacity(Contact *pc) // 检测容量
{
    if (pc->sz == pc->capacity) //当两个相等时,就需要增加容量
    {
        PeoInfo *tmp = (PeoInfo *)realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(PeoInfo));

        if (tmp != NULL)
        {
            pc->data = tmp;
            pc->capacity += INC_SZ;
            printf("已增加2位空间!\n");
        }
        else
        {
            perror("AddContct");
            printf("增加失败!\n");
            return;
        }
    }
}

void LoadContact(Contact *pc) //加载文件到通讯录
{
    FILE *pf = fopen("contact.dat", "r");
    if (pf == NULL)
    {
        perror("LoadContact:");
        return;
    }

    //读文件
    PeoInfo tmp = {0};
    while (fread(&tmp, sizeof(PeoInfo), 1, pf)) //当fread读不到时,就会返回0;
    {
        //考虑是否需要增容
        CheckCapacity(pc);
        pc->data[pc->sz] = tmp;
        pc->sz++;
    }

    //关闭文件
    fclose(pf);
    pf = NULL;
}

void InitContact(Contact *pc)
{
    pc->data = (PeoInfo *)malloc(DEFAULT_SZ * sizeof(PeoInfo));
    if (pc->data == NULL)
    {
        perror("InitContact");
        return;
    }

    pc->sz = 0;

    pc->capacity = DEFAULT_SZ;

    //加载文件
    LoadContact(pc);
}

void AddContct(Contact *pc) //增加
{
    CheckCapacity(pc);
    //增加一个信息
    printf("\n请输入姓名:> ");
    scanf("%s", pc->data[pc->sz].name);

    printf("请输入性别:> ");
    scanf("%s", pc->data[pc->sz].sex);

    printf("请输入年龄:> ");
    scanf("%d", &pc->data[pc->sz].age);

    printf("请输入手机号:> ");
    scanf("%lld", pc->data[pc->sz].tele);

    printf("请输入地址:> ");
    scanf("%s", pc->data[pc->sz].addr);

    pc->sz++;

    printf("\n增加成功!\n");
}

static int FindByName(Contact *pc, char name[]) //按照名字查找
{
    for (size_t i = 0; i < pc->sz; i++)
    {
        if (strcmp(pc->data[i].name, name) == 0)
        {
            return i;
        }
    }
    return -1;
}

void DelContct(Contact *pc) //删除功能
{
    char Name[MAX_NAME] = {0};
    if (pc->sz == 0)
    {
        printf("当前通讯录为空, 无需删除!\n");
        return;
    }

    printf("请输入要删除人的名字:> ");
    scanf("%s", Name);

    // 1. 查找要删除的人
    int pos = FindByName(pc, Name);
    // 没有 - 退出
    if (pos == -1)
    {
        printf("查无此人!\n");
        return;
    }

    // 有 - 删除 - 往前挪
    for (size_t i = pos; i < pc->sz - 1; i++)
    {
        pc->data[i] = pc->data[i + 1];
    }
    pc->sz--;

    printf("删除成功!\n");
}

void SearchContct(Contact *pc) //查找指定联系人
{
    char Name[MAX_NAME] = {0};
    printf("请输入要查找人的名字:> ");
    scanf("%s", Name);
    int pos = FindByName(pc, Name);
    if (pos == -1)
    {
        printf("查无此人!\n");
    }
    else
    {
        printf("%-0s\t%7s\t%7s\t%7s\t%15s\t%15s\n", "序号", "姓名", "性别", "年龄", "手机号", "地址");

        printf("\n%2d\t%5s\t%6s\t%5d\t%13lld\t%15s\n", pos + 1,
               pc->data[pos].name,
               pc->data[pos].sex,
               pc->data[pos].age,
               pc->data[pos].tele[pos],
               pc->data[pos].addr);
    }
}

void ModifyContct(Contact *pc) //修改指定联系人
{
    char Name[MAX_NAME] = {0};
    printf("请输入要修改人的名字:> ");
    scanf("%s", Name);
    int pos = FindByName(pc, Name);
    if (pos == -1)
    {
        printf("查无此人!\n");
    }
    else
    {
        printf("\n请输入姓名:> ");
        scanf("%s", pc->data[pos].name);

        printf("请输入性别:> ");
        scanf("%s", pc->data[pos].sex);

        printf("请输入年龄:> ");
        scanf("%d", &pc->data[pos].age);

        printf("请输入手机号:> ");
        scanf("%lld", pc->data[pos].tele);

        printf("请输入地址:> ");
        scanf("%s", pc->data[pos].addr);

        printf("修改成功!\n");

        printf("%-0s\t%7s\t%7s\t%7s\t%15s\t%15s\n", "序号", "姓名", "性别", "年龄", "手机号", "地址");

        printf("%2d\t%5s\t%6s\t%5d\t%13lld\t%15s\n", pos + 1,
               pc->data[pos].name,
               pc->data[pos].sex,
               pc->data[pos].age,
               pc->data[pos].tele[0],
               pc->data[pos].addr);
    }
}

int MyStrcmp(const char *s1, const char *s2) // MyStrcmp
{
    assert(s1 && s2);
    while (*s1 == *s2)
    {
        if (*s1 == '\0')
        {
            return 0;
        }
        s1++;
        s2++;
    }

    return *s1 > *s2 ? 1 : -1;
}

void Swap(char *e1, char *e2, int width) // MyQsort - 冒泡排序
{
    for (int i = 0; i < width; i++)
    {
        char tmp = *e1;
        *e1 = *e2;
        *e2 = tmp;
        e1++;
        e2++;
    }
}

void MyQsort(void *base, int ret, int width, int (*cmp)(const void *e1, const void *e2)) // MyQsort
{
    //趟数
    for (int i = 0; i < ret - 1; i++)
    {
        //一趟的排序
        for (int j = 0; j < ret - 1 - i; j++)
        {
            if (cmp((char *)base + j * (width), (char *)base + (j + 1) * (width)) > 0)
            {
                //升序交换
                Swap((char *)base + j * (width), (char *)base + (j + 1) * (width), width);
            }
        }
    }
}

int Sort_By_name(const void *e1, const void *e2) // MyQsort - 升序
{
    return MyStrcmp(((PeoInfo *)e1)->name, (((PeoInfo *)e2)->name));
}

void SortContct(Contact *pc) //排序功能
{
    //使用qsort函数进行排序
    MyQsort(pc->data, pc->sz, sizeof(pc->data[0]), Sort_By_name);
    // printf("%d", sizeof(pc->data->name[0]));
    printf("排序成功!\n");
}

void PrintContct(const Contact *pc) //打印所有联系人
{
    if (pc->sz == 0)
    {
        printf("抱歉,暂无联系人!\n");
        return;
    }
    printf("%-0s\t%7s\t%7s\t%7s\t%15s\t%15s\n", "序号", "姓名", "性别", "年龄", "手机号", "地址");
    for (size_t i = 0; i < pc->sz; i++)
    {
        printf("\n%2d\t%7s\t%6s\t%5d\t%15lld\t%15s\n", i + 1,
               pc->data[i].name,
               pc->data[i].sex,
               pc->data[i].age,
               pc->data[i].tele[0],
               pc->data[i].addr);
    }
    printf("\n");
}

void SaveContct(Contact *pc) //保存
{
    FILE *pf = fopen("Contact.dat", "w"); //以w打开,数据文件清零,然后因为arr里面本身就有数据,再写入
    if (pf == NULL)
    {
        perror("SaveContct:");
        return;
    }

    //写文件
    for (int i = 0; i < pc->sz; i++)
    {
        fwrite(pc->data + i, sizeof(PeoInfo), 1, pf);
    }

    //关闭文件
    fclose(pf);
    pf = NULL;
    printf("保存成功,");
}

void ExitContct(Contact *pc) //退出
{
    free(pc->data);
    pc->data = NULL;

    pc->sz = 0;
    pc->capacity = 0;
    printf("已退出!\n");
}

void DestroyContct(Contact *pc) //销毁
{
    FILE *pf = fopen("Contact.dat", "w"); // w打开文件会清零
    if (pf == NULL)
    {
        perror("SaveContct:");
        return;
    }
    pc->sz = pc->capacity = 0; //这里将数组的下标置为0之后,退出时就不会把数组内的数据保存进去
    printf("销毁成功!\n");
}

enum Option
{
    Exit,
    Add,
    Del,
    Search,
    Modify,
    Sort,
    Print,
    Destroy
};

void Menu()
{
    printf("————————————————————————————————\n");
    printf("————————————————————————————————\n");
    printf("————————1、增加   2、删除———————\n");
    printf("————————3、查找   4、改正———————\n");
    printf("————————5、排序   6、打印———————\n");
    printf("————————7、销毁   0、退出———————\n");
    printf("————————————————————————————————\n");
    printf("————————————————————————————————\n");
}

void Choice()
{
    int input;

    Contact con; //创建通讯录

    InitContact(&con); //给data在堆上申请一块空间 , sz = 0;capacity = 当前容量

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

        switch (input)
        {
        case Add:
            AddContct(&con);
            break;
        case Del:
            DelContct(&con);
            break;
        case Search:
            SearchContct(&con);
            break;
        case Modify:
            ModifyContct(&con);
            break;
        case Sort:
            SortContct(&con);
            break;
        case Print:
            PrintContct(&con);
            break;
        case Destroy:
            DestroyContct(&con);
            break;
        case Exit:
            //保存
            SaveContct(&con);
            //销毁
            ExitContct(&con);
            break;
        default:
            printf("选择错误, 请重新选择!\n");
            break;
        }

    } while (input);
}

int main()
{

    Choice();
    return 0;
}

六、总结

        本次文章只是一个刚入门不久的新手做的一次总结,还有很多因数没有考虑到,不是特别的全面,如果哪里有问题,欢迎在评论区斧正( ̄▽ ̄)~*

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值