说明

阅读此文章默认你了解《C语言自定义类型》C语言动态内存管理以及即将要发布的文章《C语言文件操作》

简介

通讯录,想必根本不用我废话...

通讯录一般指在日常生活中用笔记录,也在手机,电脑,电子字典等电子产品中拥有这个功能。通讯录作为通讯录地址的书本,当今的通讯录可以涵盖多项内容。

源文件



Tools

VS2019
VScode
Typora
PicGo

此篇文章讲述相关内容

C语言自定义类型结构体、枚举应用

C语言动态内存管理

C语言文件操作

实现一个通讯录,以及使用动态内存

  1. 存放1000个人的信息:名字+性别+年龄+电话+住址
  2. 增加联系人
  3. 显示联系人
  4. 删除联系人
  5. 修改联系人
  6. 查找联系人
  7. 排序(暂无,以后补充更新)
  8. 动态内存化通讯录
  9. 存文件

比较重要的一点,除了普通的通讯录,还要有能够动态内存分配的通讯录,实现可以动态增长空间的通讯录。

概览

通讯录

分成3个模块来写。

test.c - 测试通讯录各个功能

contact.c - 通讯录的实现

contact.h - 通讯录的声明

1.菜单

菜单就直接无脑写

void menu()
{
    printf("***********************************\n");
    printf("*****     1.add     2.del     *****\n");
    printf("*****     3.search  4.modify  *****\n");
    printf("*****     5.show    6.sort    *****\n");
    printf("*****          0.exit         *****\n");
    printf("*****      by mywifeasuna     *****\n");
    printf("***********************************\n");
}

2.功能框架

具体功能首先写出来

这个地方就可以使用枚举来列出功能,然后使用do while和switch来将大体的框架先搞定。

//选项
enum Option
{
    EXIT,
    ADD,
    DEL,
    SEARCH,
    MODIFY,
    SHOW,
    SORT
};

大体框架

int main()
{
    int input = 0;
    do
    {
        menu();
        printf("请选择:");
        scanf("%d", &input);
        switch (input)
        {
        case ADD:
            break;
        case DEL:
            break;
        case SHOW:
            break;
        case EXIT:
            printf("退出通讯录\n");
            break;
        default:
            printf("选择错误\n");
            break;
        }
    } while (input);
    return 0;
}

框架有了,其实已经就可以运行了,只不过没有内容罢了。

后续补充功能就可以一步一步查看了。

3.一个人的信息

一个人的信息有名字+性别+年龄+电话+住址。这里应该用结构体,而且还不能定死,所以可以定义MAX值,然后写入结构体。这样如果以后要变化的话也方便,好管理。

#pragma once

#define NAME_MAX 20
#define SEX_MAX 5
#define TELE_MAX 12
#define ADDR_MAX 30

//描述人的信息
struct PeoInfo
{
    char name[NAME_MAX];
    int age;
    char sex[SEX_MAX];
    char tele[TELE_MAX];
    char addr[ADDR_MAX];
};

4.1000个人的信息

通讯录肯定不能只有一个人。

所以需要一个数组。同样定义一个MAX。

#define MAX 1000

//1000个人的数据存放在data数组中
struct PeoInfo data[MAX];

这里有一个问题,虽然有了能存放1000个人的数组,但是当前存放了多少个人的信息是不知道的。

所以还需要一个sz来存放当前通讯录有几个人的信息。也就是有效信息的个数。

那么把1000个人的信息和当前存放的有效信息个数合在一起,就是通讯录。

通讯录结构体声明

//通讯录
struct Contact
{
    //1000个人的数据存放在data数组中
    struct PeoInfo data[MAX];
    int sz;//记录当前通讯录有效信息个数
};

5.创建并初始化通讯录

这时我们使用通讯录的结构体创建一个通讯录。当然还需要初始化,因为刚刚创建里面都是随机值嘛。

int main()
{
    //创建一个通讯录
    struct Contact con;
    //初始化通讯录
    InitContact(&con);//传地址过去,否则改不了
    return 0;
}

初始化通讯录

这里使用了一个函数来初始化通讯录里面的内容。

首先通讯录肯定默认没有信息,sz就是0。

使用memset把data数组初始化。

可以看到全部都是随机值c

声明

//初始化通讯录
void InitContact(struct Contact* pc);

实现

void InitContact(struct Contact* pc)
{
    pc->sz = 0;//默认没有信息
    //memset(pc->data, 0, MAX * sizeof(struct PeoInfo));
    memset(pc->data, 0, sizeof(pc->data));
}

现在可以看到全部初始化成0

6.增加联系人

终于可以开始实现第一个功能了,通讯录肯定得先增加个人来才行。

那必然是在框架内的switch里面来写第一个case。后续我就直接省略了(务必注意)

int main()
{
    int input = 0;
    //创建一个通讯录
    struct Contact con;
    //初始化通讯录
    InitContact(&con);
    do
    {
        menu();
        printf("请选择:");
        scanf("%d", &input);
        switch (input)
        {
        case ADD:
            AddContact(&con);//和初始化函数一样,传地址过去
            break;
        case DEL:
            break;
        case SHOW:
            break;
        case EXIT:
            printf("退出通讯录\n");
            break;
        default:
            printf("选择错误\n");
            break;
        }
    } while (input);
    return 0;
}

声明

//增加联系人
void AddContact(struct Contact* pc);

实现

通讯录如果满了肯定就放不下了,那么首先要确保空间。

没满的话才会往里面放。录入名字+性别+年龄+电话+住址。

那么存放的位置。如果从第一个开始录入,是不是就是从0下标开始,而sz正好也从0开始。

存放到下标为1的时候,通讯录里已经存放的数量sz就是1。以此类推。

我们用指针*pc来接收。

void AddContact(struct Contact* pc)
{
    if (pc->sz == MAX)
    {
        printf("通讯录已满!\n");
    }
    else
    {
        printf("请输入名字:\n");
        scanf("%s", pc->data[pc->sz].name);
        printf("请输入年龄:\n");
        scanf("%d", &pc->data[pc->sz].age);
        printf("请输入性别:\n");
        scanf("%s", pc->data[pc->sz].sex);
        printf("请输入电话:\n");
        scanf("%s", pc->data[pc->sz].tele);
        printf("请输入地址:\n");
        scanf("%s", pc->data[pc->sz].addr);
        printf("添加成功\n");
        pc->sz++;//添加一个,sz++
    }
}

7.显示联系人

既然存放了联系人,通讯录就肯定得显示吧

ShowContact(&con);//注意这里也传了地址

不传地址其实也行,但是从结构体传参的角度来看还是传一下比较好。

声明

//显示所有的联系人
void ShowContact(struct Contact* pc);

实现

打印其实就简单了,有什么打印什么就行了。

哦对,给个标题,不然打出列表出来不知道都是什么信息。

void ShowContact(struct Contact* pc)
{
    int i = 0;
    //打印标题
    printf("%15s\t%5s\t%8s\t%15s\t%30s\n\n", "name", "age", "sex", "tele", "addr");
    for (i = 0; i < pc->sz; i++)
    {
        //打印每一个数据
        printf("%15s\t%5d\t%8s\t%15s\t%30s\n", pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].tele, pc->data[i].addr);
    }
}

简单测试

8.删除联系人

删除肯定是删除指定的联系人,从通讯录的结构体中

DelContact(&con);

声明

void DelContact(struct Contact* pc);

实现

删除的话肯定是通讯录里存在的人,不在的话当然没法删除。

而且,修改联系人等后面也肯定需要查找。所以,这里直接写一个查找联系人的函数。

查找联系人

int FindContactByName(const struct 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;
}

找到返回下标,找不到返回-1。而且就算有重名,也是只先处理第一个。

删除

接着来删除。删除一个元素的话,把后面的元素都向前移动一次,覆盖过去。

void DelContact(struct Contact* pc)
{
    if (pc->sz == 0)
    {
        printf("通讯录为空,无法删除!\n");
        return;
    }
    char name[NAME_MAX] = { 0 };
    printf("请输入要删除联系人的名字:");
    scanf("%s", name);
    //查找
    int pos = FindContactByName(pc, name);
    //找不到返回-1
    if (pos == -1)
    {
        printf("指定的联系人不存在!\n");
    }
    //找到了就删除
    else
    {
        //删除,遍历覆盖
        int i = 0;
        {
            for (i = pos; i < pc->sz - 1; i++)
            {
                pc->data[i] = pc->data[i + 1];
            }
            pc->sz--;
            printf("删除成功!\n");
        }
    }
}

9.查找联系人

查找的话使用前面写好的查找联系人函数就很容易的能写出来了,顺便查找到后打印一下所查找的联系人信息。正好,打印信息可以直接用显示联系人的打印代码。

声明

//查找指定联系人
void SearchContact(const struct Contact* pc);

实现

void SearchContact(const struct Contact* pc)
{
    char name[NAME_MAX] = { 0 };
    printf("输入要查找人的名字:");
    scanf("%s", name);
    int pos = FindContactByName(pc, name);
    //找不到返回-1
    if (-1 == pos)
    {
        printf("查无此人\n");
    }
    //找到了就打印
    else
    {
        printf("%15s\t%5s\t%8s\t%15s\t%30s\n\n", "name", "age", "sex", "tele", "addr");
        printf("%15s\t%5d\t%8s\t%15s\t%30s\n", pc->data[pos].name, pc->data[pos].age, pc->data[pos].sex, pc->data[pos].tele, pc->data[pos].addr);
    }
}

10.修改联系人

有了查找联系人和添加联系人的基础,修改也就很简单了。查找到就输入新的信息。

声明

//修改指定联系人
void ModifyContact(struct Contact* pc);

实现

void ModifyContact(struct Contact* pc)
{
    char name[NAME_MAX] = { 0 };
    printf("输入要修改人的名字:");
    scanf("%s", name);
    int pos = FindContactByName(pc, name);
    //找不到返回-1
    if (-1 == pos)
    {
        printf("要修改的人不存在\n");
    }
    //找到了就录入新的信息
    else
    {
        printf("请输入新的名字:\n");
        scanf("%s", pc->data[pos].name);
        printf("请输入新的年龄:\n");
        scanf("%d", &pc->data[pos].age);
        printf("请输入新的性别:\n");
        scanf("%s", pc->data[pos].sex);
        printf("请输入新的电话:\n");
        scanf("%s", pc->data[pos].tele);
        printf("请输入新的地址:\n");
        scanf("%s", pc->data[pos].addr);
        printf("修改成功\n");
    }
}

13.contact.h代码

#define _CRT_SECURE_NO_WARNINGS 1

#include <assert.h>

#include <math.h>

#include <stddef.h> 

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <time.h>

#include <windows.h>

#pragma once

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

//描述人的信息
struct PeoInfo
{
    char name[NAME_MAX];
    int age;
    char sex[SEX_MAX];
    char tele[TELE_MAX];
    char addr[ADDR_MAX];
};

//通讯录
struct Contact
{
    //1000个人的数据存放在data数组中
    struct PeoInfo data[MAX];
    int sz;//记录当前通讯录有效信息个数
};

//初始化通讯录
void InitContact(struct Contact* pc);

//增加联系人
void AddContact(struct Contact* pc);

//显示所有的联系人
void ShowContact(struct Contact* pc);

//删除指定联系人
void DelContact(struct Contact* pc);

//查找指定联系人
void SearchContact(const struct Contact* pc);

//修改指定联系人
void ModifyContact(struct Contact* pc);

12.test.c代码

#define _CRT_SECURE_NO_WARNINGS 1

#include <assert.h>

#include <math.h>

#include <stddef.h> 

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <time.h>

#include <windows.h>

#include "contact.h"

void menu()
{
    printf("***********************************\n");
    printf("*****     1.add     2.del     *****\n");
    printf("*****     3.search  4.modify  *****\n");
    printf("*****     5.show    6.sort    *****\n");
    printf("*****          0.exit         *****\n");
    printf("*****      by mywifeasuna     *****\n");
    printf("***********************************\n");
}

//选项
enum Option
{
    EXIT,
    ADD,
    DEL,
    SEARCH,
    MODIFY,
    SHOW,
    SORT
};

int main()
{
    int input = 0;
    //创建一个通讯录
    struct Contact con;
    //初始化通讯录
    InitContact(&con);
    do
    {
        menu();
        printf("请选择:");
        scanf("%d", &input);
        switch (input)
        {
        case ADD:
            AddContact(&con);
            break;
        case DEL:
            DelContact(&con);
            break;
        case SEARCH:
            SearchContact(&con);
            break;
        case MODIFY:
            ModifyContact(&con);
            break;
        case SHOW:
            ShowContact(&con);
            break;
        case EXIT:
            printf("退出通讯录\n");
            break;
        default:
            printf("选择错误\n");
            break;
        }
    } while (input);
    return 0;
}

13.contact.c代码

#define _CRT_SECURE_NO_WARNINGS 1

#include <assert.h>

#include <math.h>

#include <stddef.h> 

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <time.h>

#include <windows.h>

#include "contact.h"

void InitContact(struct Contact* pc)
{
    pc->sz = 0;//默认没有信息
    //memset(pc->data, 0, MAX * sizeof(struct PeoInfo));
    memset(pc->data, 0, sizeof(pc->data));
}

//void AddContact(struct Contact* pc)
//{
//    if (pc->sz == MAX)
//    {
//        printf("通讯录已满!\n");
//    }
//    else
//    {
//        printf("请输入名字:\n");
//        scanf("%s", pc->data[pc->sz].name);
//        printf("请输入年龄:\n");
//        scanf("%d", &(pc->data[pc->sz].age));
//        printf("请输入性别:\n");
//        scanf("%s", pc->data[pc->sz].sex);
//        printf("请输入电话:\n");
//        scanf("%s", pc->data[pc->sz].tele);
//        printf("请输入地址:\n");
//        scanf("%s", pc->data[pc->sz].addr);
//        printf("添加成功\n");
//        pc->sz++;//添加一个,sz++
//    }
//}

void AddContact(struct Contact* pc)
{
    struct PeoInfo tmp = { 0 };
    if (pc->sz == MAX)
    {
        printf("通讯录已满!\n");
    }
    else
    {
        printf("请输入名字:\n");
        scanf("%s", tmp.name);
        printf("请输入年龄:\n");
        scanf("%d", &(tmp.age));
        printf("请输入性别:\n");
        scanf("%s", tmp.sex);
        printf("请输入电话:\n");
        scanf("%s", tmp.tele);
        printf("请输入地址:\n");
        scanf("%s", tmp.addr);
        pc->data[pc->sz] = tmp;
        printf("添加成功\n");
        pc->sz++;//添加一个,sz++
    }
}

void ShowContact(struct Contact* pc)
{
    int i = 0;
    //打印标题
    printf("%15s\t%5s\t%8s\t%15s\t%30s\n\n", "name", "age", "sex", "tele", "addr");
    for (i = 0; i < pc->sz; i++)
    {
        //打印每一个数据
        printf("%15s\t%5d\t%8s\t%15s\t%30s\n", pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].tele, pc->data[i].addr);
    }
}

int FindContactByName(struct Contact* pc, char name[])
//int FindContactByName(struct Contact* pc, const 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 DelContact(struct Contact* pc)
{
    if (pc->sz == 0)
    {
        printf("通讯录为空,无法删除!\n");
        return;
    }
    char name[NAME_MAX] = { 0 };
    printf("请输入要删除联系人的名字:");
    scanf("%s", name);
    //查找
    int pos = FindContactByName(pc, name);
    if (pos == -1)
    {
        printf("指定的联系人不存在!\n");
    }
    else
    {
        //删除
        int i = 0;
        {
            for (i = pos; i < pc->sz - 1; i++)
            {
                pc->data[i] = pc->data[i + 1];
            }
            pc->sz--;
            printf("删除成功!\n");
        }
    }
}

void SearchContact(const struct Contact* pc)
{
    char name[NAME_MAX] = { 0 };
    printf("输入要查找人的名字:");
    scanf("%s", name);
    int pos = FindContactByName(pc, name);
    if (-1 == pos)
    {
        printf("查无此人\n");
    }
    else
    {
        printf("%15s\t%5s\t%8s\t%15s\t%30s\n\n", "name", "age", "sex", "tele", "addr");
        printf("%15s\t%5d\t%8s\t%15s\t%30s\n", pc->data[pos].name, pc->data[pos].age, pc->data[pos].sex, pc->data[pos].tele, pc->data[pos].addr);
    }
}

void ModifyContact(struct Contact* pc)
{
    char name[NAME_MAX] = { 0 };
    printf("输入要修改人的名字:");
    scanf("%s", name);
    int pos = FindContactByName(pc, name);
    if (-1 == pos)
    {
        printf("要修改的人不存在\n");
    }
    else
    {
        printf("请输入新的名字:\n");
        scanf("%s", pc->data[pos].name);
        printf("请输入新的年龄:\n");
        scanf("%d", &pc->data[pos].age);
        printf("请输入新的性别:\n");
        scanf("%s", pc->data[pos].sex);
        printf("请输入新的电话:\n");
        scanf("%s", pc->data[pos].tele);
        printf("请输入新的地址:\n");
        scanf("%s", pc->data[pos].addr);
        printf("修改成功\n");
    }
}

动态内存增长通讯录

比较重要的,动态内存分配,实现可以动态增长空间的通讯录。

在内存中如果我们直接开辟1000个联系人的空间,当我们完全没有那么多联系人的时候,其实是很浪费的。又或者超出了这个范围,还得增加空间。再或者需要几条几条的增加,空间的利用率就会很低。

那么,利用动态内存就可以很好的实现

在前面已经写好的普通通讯录的基础上,修改亿下

1.动态增长通讯录结构体声明

使用一个指针data来指向有效空间的首元素地址

//动态增长的通讯录
struct Contact
{
    struct PeoInfo* data;
    int sz;//通讯录中当前有效元素的个数
    int capacity;//通讯录的当前最大容量
};

当然静态的版本也会保留。后面同样。

2.初始化动态增长通讯录

我们需要默认一个大小来确定容量空间。

#define DEFAULT_SZ 3

那么动态版本的初始化

//动态的版本
void InitContact(struct Contact* pc)
{
    pc->sz = 0;
    pc->data = (struct PeoInfo*)malloc(DEFAULT_SZ * sizeof(struct PeoInfo));
    pc->capacity = DEFAULT_SZ;
}

3.动态增加联系人

当我们删除联系人的时候,空间也就不删除了。

但是增加联系人的时候,空间不够就要增加。

//动态的增加联系人
void AddContact(struct Contact* pc)
{
    if (pc->sz == pc->capacity)
    {
        //如果已经存放的联系人和现在的容量相等了
        //那么就增加容量,然后添加联系人
        struct PeoInfo* ptr = (struct PeoInfo*)realloc(pc->data, (pc->capacity + 2) * sizeof(struct PeoInfo));
        if (ptr != NULL)
        {
            pc->data = ptr;
            pc->capacity += 2;
            printf("增容成功\n");
        }
        else
        {
            return;
        }
    }
    //录入新联系人信息
    printf("请输入名字:\n");
    scanf("%s", pc->data[pc->sz].name);
    printf("请输入年龄:\n");
    scanf("%d", &(pc->data[pc->sz].age));
    printf("请输入性别:\n");
    scanf("%s", pc->data[pc->sz].sex);
    printf("请输入电话:\n");
    scanf("%s", pc->data[pc->sz].tele);
    printf("请输入地址:\n");
    scanf("%s", pc->data[pc->sz].addr);
    printf("添加成功\n");
    pc->sz++;//添加一个,sz++
}

到此时,就可以测试一下了

现在空间的利用率提升了。

注意,还要释放

4.销毁通讯录

当我们使用完动态内存增长的空间后,我们需要释放。

这时退出通讯录,我们应该有一个销毁的操作。

DestroyContact(&con);

声明

//销毁通讯录
void DestroyContact(struct Contact* pc);

实现

//销毁通讯录
void DestroyContact(struct Contact* pc)
{
    free(pc->data);
    pc->data = NULL;
    pc->sz = 0;
    pc->capacity = 0;    
}

此时,通讯录就被改造成了动态增长的版本

5.contact.h完整代码

#define _CRT_SECURE_NO_WARNINGS 1

#include <assert.h>

#include <math.h>

#include <stddef.h> 

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <time.h>

#include <windows.h>

#pragma once

#define NAME_MAX 20
#define SEX_MAX 5
#define TELE_MAX 12
#define ADDR_MAX 30
#define MAX 1000
#define DEFAULT_SZ 3

//描述人的信息
struct PeoInfo
{
    char name[NAME_MAX];
    int age;
    char sex[SEX_MAX];
    char tele[TELE_MAX];
    char addr[ADDR_MAX];
};

//通讯录-静态版本
//struct Contact
//{
//    //1000个人的数据存放在data数组中
//    struct PeoInfo data[MAX];
//    int sz;//记录当前通讯录有效信息个数
//};

//动态增长的通讯录
struct Contact
{
    struct PeoInfo* data;
    int sz;//通讯录中当前有效元素的个数
    int capacity;//通讯录的当前最大容量
};

//初始化通讯录
void InitContact(struct Contact* pc);

//销毁通讯录
void DestroyContact(struct Contact* pc);

//增加联系人
void AddContact(struct Contact* pc);

//显示所有的联系人
void ShowContact(struct Contact* pc);

//删除指定联系人
void DelContact(struct Contact* pc);

//查找指定联系人
void SearchContact(const struct Contact* pc);

//修改指定联系人
void ModifyContact(struct Contact* pc);

6.test.c完整代码

#define _CRT_SECURE_NO_WARNINGS 1

#include <assert.h>

#include <math.h>

#include <stddef.h> 

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <time.h>

#include <windows.h>

#include "contact.h"

void menu()
{
    printf("***********************************\n");
    printf("*****     1.add     2.del     *****\n");
    printf("*****     3.search  4.modify  *****\n");
    printf("*****     5.show    6.sort    *****\n");
    printf("*****          0.exit         *****\n");
    printf("*****      by mywifeasuna     *****\n");
    printf("***********************************\n");
}

//选项
enum Option
{
    EXIT,
    ADD,
    DEL,
    SEARCH,
    MODIFY,
    SHOW,
    SORT
};

int main()
{
    int input = 0;
    //创建一个通讯录
    struct Contact con;
    //初始化通讯录
    InitContact(&con);
    do
    {
        menu();
        printf("请选择:");
        scanf("%d", &input);
        switch (input)
        {
        case ADD:
            AddContact(&con);
            break;
        case DEL:
            DelContact(&con);
            break;
        case SEARCH:
            SearchContact(&con);
            break;
        case MODIFY:
            ModifyContact(&con);
            break;
        case SHOW:
            ShowContact(&con);
            break;
        case EXIT:
            //销毁通讯录
            DestroyContact(&con);
            printf("退出通讯录\n");
            break;
        default:
            printf("选择错误\n");
            break;
        }
    } while (input);
    return 0;
}

7.contact.c完整代码

#define _CRT_SECURE_NO_WARNINGS 1

#include <assert.h>

#include <math.h>

#include <stddef.h> 

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <time.h>

#include <windows.h>

#include "contact.h"

//静态的初始化通讯录版本
//void InitContact(struct Contact* pc)
//{
//    pc->sz = 0;//默认没有信息
//    //memset(pc->data, 0, MAX * sizeof(struct PeoInfo));
//    memset(pc->data, 0, sizeof(pc->data));
//}

//动态的初始化通讯录版本
void InitContact(struct Contact* pc)
{
    pc->sz = 0;
    pc->data = (struct PeoInfo*)malloc(DEFAULT_SZ * sizeof(struct PeoInfo));
    pc->capacity = DEFAULT_SZ;
}

//静态的增加联系人版本1
//void AddContact(struct Contact* pc)
//{
//    if (pc->sz == MAX)
//    {
//        printf("通讯录已满!\n");
//    }
//    else
//    {
//        printf("请输入名字:\n");
//        scanf("%s", pc->data[pc->sz].name);
//        printf("请输入年龄:\n");
//        scanf("%d", &(pc->data[pc->sz].age));
//        printf("请输入性别:\n");
//        scanf("%s", pc->data[pc->sz].sex);
//        printf("请输入电话:\n");
//        scanf("%s", pc->data[pc->sz].tele);
//        printf("请输入地址:\n");
//        scanf("%s", pc->data[pc->sz].addr);
//        printf("添加成功\n");
//        pc->sz++;//添加一个,sz++
//    }
//}

//静态的增加联系人版本2
//void AddContact(struct Contact* pc)
//{
//    struct PeoInfo tmp = { 0 };
//    if (pc->sz == MAX)
//    {
//        printf("通讯录已满!\n");
//    }
//    else
//    {
//        printf("请输入名字:\n");
//        scanf("%s", tmp.name);
//        printf("请输入年龄:\n");
//        scanf("%d", &(tmp.age));
//        printf("请输入性别:\n");
//        scanf("%s", tmp.sex);
//        printf("请输入电话:\n");
//        scanf("%s", tmp.tele);
//        printf("请输入地址:\n");
//        scanf("%s", tmp.addr);
//        pc->data[pc->sz] = tmp;
//        printf("添加成功\n");
//        pc->sz++;//添加一个,sz++
//    }
//}

//动态的增加联系人
void AddContact(struct Contact* pc)
{
    if (pc->sz == pc->capacity)
    {
        //如果已经存放的联系人和现在的容量相等了
        //那么就增加容量,然后添加联系人
        struct PeoInfo* ptr = (struct PeoInfo*)realloc(pc->data, (pc->capacity + 2) * sizeof(struct PeoInfo));
        if (ptr != NULL)
        {
            pc->data = ptr;
            pc->capacity += 2;
            printf("增容成功\n");
        }
        else
        {
            return;
        }
    }
    //录入新联系人信息
    printf("请输入名字:\n");
    scanf("%s", pc->data[pc->sz].name);
    printf("请输入年龄:\n");
    scanf("%d", &(pc->data[pc->sz].age));
    printf("请输入性别:\n");
    scanf("%s", pc->data[pc->sz].sex);
    printf("请输入电话:\n");
    scanf("%s", pc->data[pc->sz].tele);
    printf("请输入地址:\n");
    scanf("%s", pc->data[pc->sz].addr);
    printf("添加成功\n");
    pc->sz++;//添加一个,sz++
}

//销毁通讯录
void DestroyContact(struct Contact* pc)
{
    free(pc->data);
    pc->data = NULL;
    pc->sz = 0;
    pc->capacity = 0;    
}

void ShowContact(struct Contact* pc)
{
    int i = 0;
    //打印标题
    printf("%15s\t%5s\t%8s\t%15s\t%30s\n\n", "name", "age", "sex", "tele", "addr");
    for (i = 0; i < pc->sz; i++)
    {
        //打印每一个数据
        printf("%15s\t%5d\t%8s\t%15s\t%30s\n", pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].tele, pc->data[i].addr);
    }
}

int FindContactByName(struct Contact* pc, char name[])
//int FindContactByName(struct Contact* pc, const 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 DelContact(struct Contact* pc)
{
    if (pc->sz == 0)
    {
        printf("通讯录为空,无法删除!\n");
        return;
    }
    char name[NAME_MAX] = { 0 };
    printf("请输入要删除联系人的名字:");
    scanf("%s", name);
    //查找
    int pos = FindContactByName(pc, name);
    if (pos == -1)
    {
        printf("指定的联系人不存在!\n");
    }
    else
    {
        //删除
        int i = 0;
        {
            for (i = pos; i < pc->sz - 1; i++)
            {
                pc->data[i] = pc->data[i + 1];
            }
            pc->sz--;
            printf("删除成功!\n");
        }
    }
}

void SearchContact(const struct Contact* pc)
{
    char name[NAME_MAX] = { 0 };
    printf("输入要查找人的名字:");
    scanf("%s", name);
    int pos = FindContactByName(pc, name);
    if (-1 == pos)
    {
        printf("查无此人\n");
    }
    else
    {
        printf("%15s\t%5s\t%8s\t%15s\t%30s\n\n", "name", "age", "sex", "tele", "addr");
        printf("%15s\t%5d\t%8s\t%15s\t%30s\n", pc->data[pos].name, pc->data[pos].age, pc->data[pos].sex, pc->data[pos].tele, pc->data[pos].addr);
    }
}

void ModifyContact(struct Contact* pc)
{
    char name[NAME_MAX] = { 0 };
    printf("输入要修改人的名字:");
    scanf("%s", name);
    int pos = FindContactByName(pc, name);
    if (-1 == pos)
    {
        printf("要修改的人不存在\n");
    }
    else
    {
        printf("请输入新的名字:\n");
        scanf("%s", pc->data[pos].name);
        printf("请输入新的年龄:\n");
        scanf("%d", &pc->data[pos].age);
        printf("请输入新的性别:\n");
        scanf("%s", pc->data[pos].sex);
        printf("请输入新的电话:\n");
        scanf("%s", pc->data[pos].tele);
        printf("请输入新的地址:\n");
        scanf("%s", pc->data[pos].addr);
        printf("修改成功\n");
    }
}

文件存储通讯录

现在基本实现了通讯录的所有功能。那么问题来了,记录下的通讯录再次打开,因为没有保存数据还是会归零。所以

接下来实现一下可以存储数据到文件的通讯录

学习过文件操作后,其实发现真的不难。

1.保存信息

首先,我们应该在退出前就把数据保存到文件中。而且肯定是先写才能读对吧。没有数据,看个寂寞呢?

//保存信息
SaveContact(&con);
//销毁通讯录
DestroyContact(&con);

接下来我们实现保存信息的函数

声明

//保存通讯录信息到文件中
void SaveContact(struct Contact* pc);

实现

我们把数据保存到contact.txt中

void SaveContact(struct Contact* pc)
{
    //打开文件
    FILE* pf = fopen("contact.txt", "wb");
    if (NULL == pf)
    {
        perror("SaveContact::fopen");
        return;
    }
    //写数据
    int i = 0;
    for (i = 0; i < pc->sz; i++)
    {
        //fwrite(&(pc->data[i]), sizeof(struct PeoInfo), 1, pf);
        fwrite(pc->data + i, sizeof(struct PeoInfo), 1, pf);
    }
    //关闭文件
    fclose(pf);
    pf = NULL;
}

这一块其实就没什么难点了,只要按部就班保存就行了。可以看一下保存后的文件。

可以看到已经保存进去了。都是二进制数据,看不懂,不过机器能看懂就行。

2.加载信息

这一块就很重要了,信息保存了进去,我们该如何读取出来呢?

读取函数,应该放在初始化通讯录中,我们的通讯录默认初始化能放3个信息,空间不够一次增容2个空间。

将加载信息函数放在初始化通讯录函数中:

//加载文件信息到通讯录中
LoadContact(pc);

声明

//加载文件信息到通讯录中
void LoadContact(struct Contact* pc);

实现

重点来了,首先我们应该把函数实现放在初始化前面。

然后需要考虑,如果我们保存的文件数据要比初始化的空间大,我们就应该先检测空间,不够的话应该增容。

这里可以把之前动态内存写过的增容函数直接放到最前面,这样的话就可以把加载时需要的增容和填入数据时不够需要的增容全部包含进去。当然也不用声明了。

//一上来就先检测,容量够不够。后续不够,也进入这个函数。
void CheckCapacity(struct Contact* pc)
{
    if (pc->sz == pc->capacity)
    {
        //增加容量
        struct PeoInfo* ptr = (struct PeoInfo*)realloc(pc->data, (pc->capacity + 2) * sizeof(struct PeoInfo));
        if (ptr != NULL)
        {
            pc->data = ptr;
            pc->capacity += 2;
            printf("增容成功\n");
        }
        else
        {
            perror("通讯录增容失败");
            exit(1);//异常结束函数
        }
    }
}

void LoadContact(struct Contact* pc)
{
    //打开文件
    FILE* pf = fopen("contact.txt", "rb");
    if (NULL == pf)
    {
        perror("LoadContact::fopen");
        return;
    }
    //读文件 - 这里是关键了,该怎么读呢?
    //使用fread,这个函数能读几个读几个,设定读10,只能读5个就返回5个。
    struct PeoInfo tmp = { 0 };
    //设定信息放到tmp,大小是struct PeoInfo,一次读一个人的信息,从pf里去读。
    while (fread(&tmp, sizeof(struct PeoInfo), 1, pf))
        //如果发现返回了0,那就读完了。
    {
        //注意一开始只有3个人的空间,空间如果不够,得增加空间
        CheckCapacity(pc);//检测
        pc->data[pc->sz] = tmp;
        pc->sz++;
        //注意一开始只有3个人的空间,空间如果不够,得增加空间
    }
    //关闭文件
    fclose(pf);
    pf = NULL;
}

输入数据增容

那么我们之前动态增容的函数也就可以使用检测函数了

//动态的增加联系人
void AddContact(struct Contact* pc)
{
    //if (pc->sz == pc->capacity)
    //{
    //    //如果已经存放的联系人和现在的容量相等了
    //    //那么就增加容量,然后添加联系人
    //    struct PeoInfo* ptr = (struct PeoInfo*)realloc(pc->data, (pc->capacity + 2) * sizeof(struct PeoInfo));
    //    if (ptr != NULL)
    //    {
    //        pc->data = ptr;
    //        pc->capacity += 2;
    //        printf("增容成功\n");
    //    }
    //    else
    //    {
    //        return;
    //    }
    //}
    CheckCapacity(pc);//配合文件操作我们直接封装成一个检测增容函数
    //录入新联系人信息
    printf("请输入名字:\n");
    scanf("%s", pc->data[pc->sz].name);
    printf("请输入年龄:\n");
    scanf("%d", &(pc->data[pc->sz].age));
    printf("请输入性别:\n");
    scanf("%s", pc->data[pc->sz].sex);
    printf("请输入电话:\n");
    scanf("%s", pc->data[pc->sz].tele);
    printf("请输入地址:\n");
    scanf("%s", pc->data[pc->sz].addr);
    printf("添加成功\n");
    pc->sz++;//添加一个,sz++
}

最终效果

1.输入数据增容

2.保存数据

可以看到重新打开程序显示增容成功,说明检测到文件的数据大于初始化开辟的数据。查看一下保存的数据,正常显示。

最终代码

1.contact.h完整代码

#define _CRT_SECURE_NO_WARNINGS 1

#include <assert.h>

#include <math.h>

#include <stddef.h> 

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <time.h>

#include <windows.h>

#pragma once

#define NAME_MAX 20
#define SEX_MAX 5
#define TELE_MAX 12
#define ADDR_MAX 30
#define MAX 1000
#define DEFAULT_SZ 3

//描述人的信息
struct PeoInfo
{
    char name[NAME_MAX];
    int age;
    char sex[SEX_MAX];
    char tele[TELE_MAX];
    char addr[ADDR_MAX];
};

//通讯录-静态版本
//struct Contact
//{
//    //1000个人的数据存放在data数组中
//    struct PeoInfo data[MAX];
//    int sz;//记录当前通讯录有效信息个数
//};

//动态增长的通讯录
struct Contact
{
    struct PeoInfo* data;
    int sz;//通讯录中当前有效元素的个数
    int capacity;//通讯录的当前最大容量
};

//初始化通讯录
void InitContact(struct Contact* pc);

//销毁通讯录
void DestroyContact(struct Contact* pc);

//增加联系人
void AddContact(struct Contact* pc);

//显示所有的联系人
void ShowContact(struct Contact* pc);

//删除指定联系人
void DelContact(struct Contact* pc);

//查找指定联系人
void SearchContact(const struct Contact* pc);

//修改指定联系人
void ModifyContact(struct Contact* pc);

//保存通讯录信息到文件中
void SaveContact(struct Contact* pc);

//加载文件信息到通讯录中
void LoadContact(struct Contact* pc);

2.test.c完整代码

#define _CRT_SECURE_NO_WARNINGS 1

#include <assert.h>

#include <math.h>

#include <stddef.h> 

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <time.h>

#include <windows.h>

#include "contact.h"

void menu()
{
    printf("***********************************\n");
    printf("*****     1.add     2.del     *****\n");
    printf("*****     3.search  4.modify  *****\n");
    printf("*****     5.show    6.sort    *****\n");
    printf("*****          0.exit         *****\n");
    printf("*****      by mywifeasuna     *****\n");
    printf("***********************************\n");
}

//选项
enum Option
{
    EXIT,
    ADD,
    DEL,
    SEARCH,
    MODIFY,
    SHOW,
    SORT
};

int main()
{
    int input = 0;
    //创建一个通讯录
    struct Contact con;
    //初始化通讯录
    InitContact(&con);
    //最多可以放3个人的信息了
    //空间不够可以增容
    //可以在初始化的时候,加载文件信息
    do
    {
        menu();
        printf("请选择:");
        scanf("%d", &input);
        switch (input)
        {
        case ADD:
            AddContact(&con);
            break;
        case DEL:
            DelContact(&con);
            break;
        case SEARCH:
            SearchContact(&con);
            break;
        case MODIFY:
            ModifyContact(&con);
            break;
        case SHOW:
            ShowContact(&con);
            break;
        case EXIT:
            //保存信息
            SaveContact(&con);
            //销毁通讯录
            DestroyContact(&con);
            printf("退出通讯录\n");
            break;
        default:
            printf("选择错误\n");
            break;
        }
    } while (input);
    return 0;
}

//注意 
//关于通讯录 我们要写文件的版本
//1.退出通讯录的时候,保存信息到文件中
//2.运行起来通讯录的时候,加载文件中的信息到通讯录中

3.contact.c完整代码

#define _CRT_SECURE_NO_WARNINGS 1

#include <assert.h>

#include <math.h>

#include <stddef.h> 

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <time.h>

#include <windows.h>

#include "contact.h"

//一上来就先检测,容量够不够。后续不够,也进入这个函数。
void CheckCapacity(struct Contact* pc)
{
    if (pc->sz == pc->capacity)
    {
        //增加容量
        struct PeoInfo* ptr = (struct PeoInfo*)realloc(pc->data, (pc->capacity + 2) * sizeof(struct PeoInfo));
        if (ptr != NULL)
        {
            pc->data = ptr;
            pc->capacity += 2;
            printf("增容成功\n");
        }
        else
        {
            perror("通讯录增容失败");
            exit(1);//异常结束函数
        }
    }
}

void LoadContact(struct Contact* pc)
{
    //打开文件
    FILE* pf = fopen("contact.txt", "rb");
    if (NULL == pf)
    {
        perror("LoadContact::fopen");
        return;
    }
    //读文件 - 这里是关键了,该怎么读呢?
    //使用fread,这个函数能读几个读几个,设定读10,只能读5个就返回5个。
    struct PeoInfo tmp = { 0 };
    //设定信息放到tmp,大小是struct PeoInfo,一次读一个人的信息,从pf里去读。
    while (fread(&tmp, sizeof(struct PeoInfo), 1, pf))
        //如果发现返回了0,那就读完了。
    {
        //注意一开始只有3个人的空间,空间如果不够,得增加空间
        CheckCapacity(pc);//检测
        pc->data[pc->sz] = tmp;
        pc->sz++;
        //注意一开始只有3个人的空间,空间如果不够,得增加空间
    }
    //关闭文件
    fclose(pf);
    pf = NULL;
}

//静态的初始化通讯录版本
//void InitContact(struct Contact* pc)
//{
//    pc->sz = 0;//默认没有信息
//    //memset(pc->data, 0, MAX * sizeof(struct PeoInfo));
//    memset(pc->data, 0, sizeof(pc->data));
//}

//动态的初始化通讯录版本
void InitContact(struct Contact* pc)
{
    pc->sz = 0;
    pc->data = (struct PeoInfo*)malloc(DEFAULT_SZ * sizeof(struct PeoInfo));
    pc->capacity = DEFAULT_SZ;
    //加载文件信息到通讯录中
    LoadContact(pc);
}

//静态的增加联系人版本1
//void AddContact(struct Contact* pc)
//{
//    if (pc->sz == MAX)
//    {
//        printf("通讯录已满!\n");
//    }
//    else
//    {
//        printf("请输入名字:\n");
//        scanf("%s", pc->data[pc->sz].name);
//        printf("请输入年龄:\n");
//        scanf("%d", &(pc->data[pc->sz].age));
//        printf("请输入性别:\n");
//        scanf("%s", pc->data[pc->sz].sex);
//        printf("请输入电话:\n");
//        scanf("%s", pc->data[pc->sz].tele);
//        printf("请输入地址:\n");
//        scanf("%s", pc->data[pc->sz].addr);
//        printf("添加成功\n");
//        pc->sz++;//添加一个,sz++
//    }
//}

//静态的增加联系人版本2
//void AddContact(struct Contact* pc)
//{
//    struct PeoInfo tmp = { 0 };
//    if (pc->sz == MAX)
//    {
//        printf("通讯录已满!\n");
//    }
//    else
//    {
//        printf("请输入名字:\n");
//        scanf("%s", tmp.name);
//        printf("请输入年龄:\n");
//        scanf("%d", &(tmp.age));
//        printf("请输入性别:\n");
//        scanf("%s", tmp.sex);
//        printf("请输入电话:\n");
//        scanf("%s", tmp.tele);
//        printf("请输入地址:\n");
//        scanf("%s", tmp.addr);
//        pc->data[pc->sz] = tmp;
//        printf("添加成功\n");
//        pc->sz++;//添加一个,sz++
//    }
//}

//动态的增加联系人
void AddContact(struct Contact* pc)
{
    //if (pc->sz == pc->capacity)
    //{
    //    //如果已经存放的联系人和现在的容量相等了
    //    //那么就增加容量,然后添加联系人
    //    struct PeoInfo* ptr = (struct PeoInfo*)realloc(pc->data, (pc->capacity + 2) * sizeof(struct PeoInfo));
    //    if (ptr != NULL)
    //    {
    //        pc->data = ptr;
    //        pc->capacity += 2;
    //        printf("增容成功\n");
    //    }
    //    else
    //    {
    //        return;
    //    }
    //}
    CheckCapacity(pc);//配合文件操作我们直接封装成一个检测增容函数
    //录入新联系人信息
    printf("请输入名字:\n");
    scanf("%s", pc->data[pc->sz].name);
    printf("请输入年龄:\n");
    scanf("%d", &(pc->data[pc->sz].age));
    printf("请输入性别:\n");
    scanf("%s", pc->data[pc->sz].sex);
    printf("请输入电话:\n");
    scanf("%s", pc->data[pc->sz].tele);
    printf("请输入地址:\n");
    scanf("%s", pc->data[pc->sz].addr);
    printf("添加成功\n");
    pc->sz++;//添加一个,sz++
}

//销毁通讯录
void DestroyContact(struct Contact* pc)
{
    free(pc->data);
    pc->data = NULL;
    pc->sz = 0;
    pc->capacity = 0;    
}

void ShowContact(struct Contact* pc)
{
    int i = 0;
    //打印标题
    printf("%15s\t%5s\t%8s\t%15s\t%30s\n\n", "name", "age", "sex", "tele", "addr");
    for (i = 0; i < pc->sz; i++)
    {
        //打印每一个数据
        printf("%15s\t%5d\t%8s\t%15s\t%30s\n", pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].tele, pc->data[i].addr);
    }
}

int FindContactByName(struct Contact* pc, char name[])
//int FindContactByName(struct Contact* pc, const 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 DelContact(struct Contact* pc)
{
    if (pc->sz == 0)
    {
        printf("通讯录为空,无法删除!\n");
        return;
    }
    char name[NAME_MAX] = { 0 };
    printf("请输入要删除联系人的名字:");
    scanf("%s", name);
    //查找
    int pos = FindContactByName(pc, name);
    if (pos == -1)
    {
        printf("指定的联系人不存在!\n");
    }
    else
    {
        //删除
        int i = 0;
        {
            for (i = pos; i < pc->sz - 1; i++)
            {
                pc->data[i] = pc->data[i + 1];
            }
            pc->sz--;
            printf("删除成功!\n");
        }
    }
}

void SearchContact(const struct Contact* pc)
{
    char name[NAME_MAX] = { 0 };
    printf("输入要查找人的名字:");
    scanf("%s", name);
    int pos = FindContactByName(pc, name);
    if (-1 == pos)
    {
        printf("查无此人\n");
    }
    else
    {
        printf("%15s\t%5s\t%8s\t%15s\t%30s\n\n", "name", "age", "sex", "tele", "addr");
        printf("%15s\t%5d\t%8s\t%15s\t%30s\n", pc->data[pos].name, pc->data[pos].age, pc->data[pos].sex, pc->data[pos].tele, pc->data[pos].addr);
    }
}

void ModifyContact(struct Contact* pc)
{
    char name[NAME_MAX] = { 0 };
    printf("输入要修改人的名字:");
    scanf("%s", name);
    int pos = FindContactByName(pc, name);
    if (-1 == pos)
    {
        printf("要修改的人不存在\n");
    }
    else
    {
        printf("请输入新的名字:\n");
        scanf("%s", pc->data[pos].name);
        printf("请输入新的年龄:\n");
        scanf("%d", &pc->data[pos].age);
        printf("请输入新的性别:\n");
        scanf("%s", pc->data[pos].sex);
        printf("请输入新的电话:\n");
        scanf("%s", pc->data[pos].tele);
        printf("请输入新的地址:\n");
        scanf("%s", pc->data[pos].addr);
        printf("修改成功\n");
    }
}

void SaveContact(struct Contact* pc)
{
    //打开文件
    FILE* pf = fopen("contact.txt", "wb");
    if (NULL == pf)
    {
        perror("SaveContact::fopen");
        return;
    }
    //写数据
    int i = 0;
    for (i = 0; i < pc->sz; i++)
    {
        //fwrite(&(pc->data[i]), sizeof(struct PeoInfo), 1, pf);
        fwrite(pc->data + i, sizeof(struct PeoInfo), 1, pf);
    }
    //关闭文件
    fclose(pf);
    pf = NULL;
}

总结

其实可以发现,通讯录的实现利用现在的知识就可以很简单的实现,当然还有排序的功能需要实现,这里就先暂不更新。不过区区一个排序,再高级一点的话也就不使用这样挫的代码了。

小头图版权:
《原神一周年派蒙》by QuAn_ 2021年9月28日下午12点32分 pid:93076323

广告位招租
最后修改:2021 年 10 月 12 日 05 : 05 AM
如果觉得我的文章对你有用,请喂饱我!(理直气壮)