之前写过一个版本的通讯录,当时还没有学习动态内存管理,所以只是简单实现了一版。传送门在这:

通讯录

在之前一段时间把动态内存管理学完之后,今天把这个小项目,重新捡起来,改一改,供大家参考。

之前写的时候是给了1000个人的信息,一旦生成.exe文件后,那么这个数量信息就没法改了。不太方便。今天主要是利用动态内存开辟,优化一下之前写的代码。改成动态增长的版本。


动态增长版本通讯录

设定:

默认可以存放3个人的信息,当发现当前通讯录满的时候,我们进行扩容,每次增加两个信息长度的空间。

(这里空间个数少,是方便我调试,大家可以直接多来点空间。)

contact.h

首先针对头文件中,我们的宏定义1000 肯定就没用了要注释掉,下面是原有版本:

#ifndef C_CONTACT_H
#define C_CONTACT_H
#include "stdio.h"
#include "string.h"

#define  MAX 1000
#define  NAME 20
#define  SEX 5
#define  TELE 12
#define  ADDR 30

struct PeoInfo
{
    char name[NAME];
    int age;
    char sex[SEX];
    char tele[TELE];
    char addr[ADDR];
};
struct Contact{
    struct  PeoInfo data[MAX];//存放一个信息
    int size;//记录当前个数
};

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

//声明函数
void InitContact(struct  Contact* ps);
void AddContact(struct  Contact* ps);
void ShowContact(const struct  Contact* ps);
void DelContact(struct  Contact* ps);
void ModifyContact(const struct  Contact* ps);
void SortContact(struct  Contact* ps);

#endif //C_CONTACT_H

我们把第6行注释掉:

//#define  MAX 1000

然后要修改结构体Contact

struct Contact{
    struct  PeoInfo * data;//存放一个信息
    int size;//记录当前个数
    int capacity;
};

当然这个地方也可以采取我之前文章中的柔性数组的方案,这里我就用指针来实现了,除了修改指针以外,还需要一个变量来记录当前空间的容量,size只是记录有几个,还需要有个变量记录总共空间能容纳几个。

之所以需要这个主要是方便我们判断空间进行扩容

那么这就是第一个文件需要改的地方。

test.c

原代码如下:

//
// Created by 提灯前行 on 2023/11/30.
//
#include "contact.c"

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("***************************************\n");


}
int main(){
    int input=0;
    //创建通讯录
    struct Contact con;//通讯录,包含1000个元素和size
    //初始化通讯录
    InitContact(&con);


    do {
        menu();
        printf("please select:>");
        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 SORT:
                SortContact(&con);
                break;
            case EXIT:
                 printf("exit\n");
                break;
            default:
                printf("input error\n");
                break;
        }
    } while (input);
}

我们首先聚焦于这里,

    //创建通讯录
    struct Contact con;//通讯录,包含1000个元素和size

既然使用动态开辟空间的方法, 那么这里在创建通讯录的时候,就不能是这样简单的像原来一样创建个对象了。里面包含的也不是1000个元素了和单独一个size了。

    //创建通讯录
    struct Contact con;//通讯录,包含data指针和size、capacity

所以这里初始化函数要改

    //初始化通讯录
    InitContact(&con);

我们进入到这个初始化函数。


contact.c

初始化函数InitContact的实现是在contact.c文件中的,我们要对其作修改。

原本初始化函数如下:

void InitContact(struct  Contact* ps){
    //置0
    memset(ps->data,0, sizeof (ps->data));
    ps->size=0;

}

修改为:

void InitContact(struct  Contact* ps){

    ps->data= (struct PeoInfo*)malloc(3*sizeof (struct PeoInfo));
    //空间开辟判断
    if (ps->data){
        ps->size=0;
        ps->capacity=DEFAULT_SIZE;
    }else{
        printf("空间开辟失败");
        return;
    }

}

这里就要使用malloc函数进行空间开辟了,当然我上面设定里说要开辟三个结构体大小的空间,咱们就3*结构体的大小,然后开辟完一定要记得判断是否开辟成功,还要在.h文件中引入该引得头文件。

#include "stdlib.h"

#define  DEFAULT_SIZE 3

紧接着我们讨论实现的操作,有哪些需要改。

通讯录增加

增加也需要修改,原代码如下:


void AddContact(struct  Contact* ps){
    //判断通讯录是否满
    if(ps->size==MAX){
        printf("error , full of contact");
    } else{
        printf("please input name");
        scanf("%s",ps->data[ps->size].name);
        printf("please input ege");
        scanf("%d",&(ps->data[ps->size].age));
        printf("please input sex");
        scanf("%s",ps->data[ps->size].sex);
        printf("please input tele");
        scanf("%s",ps->data[ps->size].tele);
        printf("please input addr");
        scanf("%s",ps->data[ps->size].addr);
        ps->size++;
        printf("add sucessfully\n");
        ShowContact(ps);
    }
}

修改为:

void AddContact(struct  Contact* ps){
        //判断通讯录是否满

         //1.如果满了,就增加空间
         //2.未满,不执行操作
        CheckCapacity(ps);
         printf("please input name");
        scanf("%s",ps->data[ps->size].name);
        printf("please input ege");
        scanf("%d",&(ps->data[ps->size].age));
        printf("please input sex");
        scanf("%s",ps->data[ps->size].sex);
        printf("please input tele");
        scanf("%s",ps->data[ps->size].tele);
        printf("please input addr");
        scanf("%s",ps->data[ps->size].addr);
            ps->size++;
        printf("add sucessfully\n");
        ShowContact(ps);

}

这里引入了一个新的函数,检查空间函数

void CheckCapacity(struct Contact *ps){
    if (ps->size==ps->capacity){
        //满了,增容
        struct PeoInfo *ptr =realloc(ps->data,(ps->capacity+2)*sizeof (struct PeoInfo));
        if (ptr){
            ps->data=ptr;
            ps->capacity+=2;
            printf("增加了两个空间的容量\n");
        } else{
            printf("空间开辟失败");
        }
    }
}

这个函数负责判断空间容量,如果满了就增加,如果不满就什么也不干。

到这里我们先测试一下:

C语言通讯录-动态版_C语言

看到现在我已经添加了三个信息了。

然后我们继续添加:

C语言通讯录-动态版_动态内存_02

会发现输出了一句:“增加了两个空间的容量”,这就说明我们之前改的思路是没问题的。

销毁空间

既然我们空间时动态开辟的,那么在最后使用完毕之后我们是不是还需要对空间进行释放啊,所以还需要增加上空间的释放。

我们去修改test.c中的菜单那块代码:

            case EXIT:
                //释放动态开辟的空间
                DestroyContact(&con);
                 printf("exit\n");
                break;

然后去.h中声明:

void DestroyContact( Contact* ps);

再去.c中去实现:

void DestroyContact( Contact* ps){
    free(ps->data);
    ps->data=NULL;
    printf("空间已回收\n");
}

最后测试一下:

C语言通讯录-动态版_C语言_03

那么到此未知,这个小项目的改进已经改进完毕了。


全部代码

最后还是附赠上全套的代码:

contac.h

//
// Created by 提灯前行 on 2023/11/30.
//

#ifndef C_CONTACT_H
#define C_CONTACT_H
#include "stdio.h"
#include "string.h"
#include "stdlib.h"

//#define  MAX 1000
#define  DEFAULT_SIZE 3

#define  NAME 20
#define  SEX 5
#define  TELE 12
#define  ADDR 30

typedef struct PeoInfo
{
    char name[NAME];
    int age;
    char sex[SEX];
    char tele[TELE];
    char addr[ADDR];
}PeoInfo;
typedef struct Contact{
    struct  PeoInfo * data;//存放一个信息
    int size;//记录当前个数
    int capacity;
}Contact;

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

//声明函数
void InitContact(struct  Contact* ps);
void AddContact(struct  Contact* ps);
void ShowContact(const struct  Contact* ps);
void DelContact(struct  Contact* ps);
void ModifyContact(const struct  Contact* ps);
void SortContact(struct  Contact* ps);
void DestroyContact( Contact* ps);

#endif //C_CONTACT_H

contact.c

//
// Created by 提灯前行 on 2023/11/30.
//

#include "contact.h"

void InitContact(struct  Contact* ps){

    ps->data= (struct PeoInfo*)malloc(3*sizeof (struct PeoInfo));
    //空间开辟判断
    if (ps->data){
        ps->size=0;
        ps->capacity=DEFAULT_SIZE;
    }else{
        printf("空间开辟失败");
        return;
    }

}
void CheckCapacity(struct Contact *ps){
    if (ps->size==ps->capacity){
        //满了,增容
        struct PeoInfo *ptr =realloc(ps->data,(ps->capacity+2)*sizeof (struct PeoInfo));
        if (ptr){
            ps->data=ptr;
            ps->capacity+=2;
            printf("增加了两个空间的容量\n");
            ptr=NULL;
        } else{
            printf("空间开辟失败");
        }
    }
}


void AddContact(struct  Contact* ps){
        //判断通讯录是否满

         //1.如果满了,就增加空间
         //2.未满,不执行操作
        CheckCapacity(ps);
         printf("please input name");
        scanf("%s",ps->data[ps->size].name);
        printf("please input ege");
        scanf("%d",&(ps->data[ps->size].age));
        printf("please input sex");
        scanf("%s",ps->data[ps->size].sex);
        printf("please input tele");
        scanf("%s",ps->data[ps->size].tele);
        printf("please input addr");
        scanf("%s",ps->data[ps->size].addr);
            ps->size++;
        printf("add sucessfully\n");
        ShowContact(ps);

}

void ShowContact(const struct  Contact* ps){
    if(ps->size==0){
        printf("contact null\n");
    } else{
//        int i=0;
        printf("%-20s\t%-4s\t%-5s\t%-12s\t%-20s\n","name","age","sex","tele","addr");
        for (int j = 0; j < ps->size; ++j) {

            printf("%-20s\t%-4d\t%-5s\t%-12s\t%-20s\n",
                   ps->data[j].name,
                   ps->data[j].age,
                   ps->data[j].sex,
                   ps->data[j].tele,
                   ps->data[j].addr);
        }
    }
}

static int FindByNme(const struct  Contact* ps,char name[NAME]){
        int i;
    for (i=0; i < ps->size; ++i) {
        if(0==strcmp(ps->data[i].name,name)){
            return i;
        }
    }
    //找不到的情况
    return -1;
}

void DelContact(struct  Contact* ps){
    char name[NAME];
    printf("please input del name");
    scanf("%s",name);
    //查找要删除的成员的位置
    //找到了返回名字所在元素下标
    //找不到返回-1
    int pos= FindByNme(ps,name);
//    int i;
//    for (i=0; i < ps->size; ++i) {
//        if(0==strcmp(ps->data[i].name,name)){
//            break;
//        }
//    }
    if(pos==-1){
        printf("member is null");
    }else{
        int  j;
        for ( j = pos; j < ps->size-1; ++j) {
            ps->data[j]=ps->data[j+1];
        }
        ps->size--;
        printf("delete successfully\n");
        ShowContact(ps);
    }
    //删除
}

void SearchContact(struct  Contact* ps){
    char name[NAME];
    printf("please input name for select:>");
    scanf("%s",name);
    int pos= FindByNme(ps,name);
    if(pos==-1) {
        printf("member is null");
    } else{
        printf("%-20s\t%-4s\t%-5s\t%-12s\t%-20s\n","name","age","sex","tele","addr");
        //打印出来查找到的结果
        printf("%-20s\t%-4d\t%-5s\t%-12s\t%-20s\n",
               ps->data[pos].name,
               ps->data[pos].age,
               ps->data[pos].sex,
               ps->data[pos].tele,
               ps->data[pos].addr);
    }

}

void ModifyContact(const struct  Contact* ps){
    char name[NAME];
    printf("please input name for modify:>");
    scanf("%s",name);
    int pos= FindByNme(ps,name);
    if(pos==-1) {
        printf("member is null");
    } else{
        printf("please input modify name");
        scanf("%s",ps->data[pos].name);
        printf("please input modify ege");
        scanf("%d",&(ps->data[pos].age));
        printf("please input modify sex");
        scanf("%s",ps->data[pos].sex);
        printf("please input modify tele");
        scanf("%s",ps->data[pos].tele);
        printf("please input modify addr");
        scanf("%s",ps->data[pos].addr);
        printf("modify sucessfully\n");

        printf("%-20s\t%-4s\t%-5s\t%-12s\t%-20s\n","name","age","sex","tele","addr");
        //打印出来查找到的结果
        printf("%-20s\t%-4d\t%-5s\t%-12s\t%-20s\n",
               ps->data[pos].name,
               ps->data[pos].age,
               ps->data[pos].sex,
               ps->data[pos].tele,
               ps->data[pos].addr);
    }

}

void SortContact(struct  Contact* ps){
    for (int i = 0; i < ps->size; ++i) {
        for (int j = i+1; j < ps->size; ++j) {
            if(ps->data[i].age>ps->data[j].age){
                //交换位置
                struct  PeoInfo tmp;
                tmp=ps->data[i];
                ps->data[i]=ps->data[j];
                ps->data[j]=tmp;
            }
        }
    }
    printf("sort successfully");
    ShowContact(ps);
}


void DestroyContact( Contact* ps){
    free(ps->data);
    ps->data=NULL;
    printf("空间已回收\n");
}

test.c

//
// Created by 提灯前行 on 2023/11/30.
//
#include "contact.c"

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("***************************************\n");


}
int main(){
    int input=0;
    //创建通讯录
    struct Contact con;//通讯录,包含data指针和size、capacity
    //初始化通讯录
    InitContact(&con);


    do {
        menu();
        printf("please select:>");
        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 SORT:
                SortContact(&con);
                break;
            case EXIT:
                //释放动态开辟的空间
                DestroyContact(&con);
                 printf("exit\n");
                break;
            default:
                printf("input error\n");
                break;
        }
    } while (input);
}