对C语言的学习研究也有一段时间了,今天做一个小项目“通讯录”,来回顾之前所学。

文件结构

通讯录这个项目需要三个文件:

1.test.c 用于测试

2.contact.c 用于实现

3.contact.h 声明函数

需求描述

1.该通讯录要能存放1000个好友的信息

2.信息要包含:姓名、电话、性别、住址、年龄

3.增加好友信息

4.删除指定名字的好友信息

5.修改好友信息

6.打印好友信息

7.排序

。。。

实现

实现菜单界面

第一步我们先搭建一个框架。这个框架主要包括一些菜单的选择,类似于一个界面。

我们要在test.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.add  ********************\n");
    printf("***************************************\n");


}
int main(){
    int input=0;
    do {
        menu();
        printf("please select:>");
        scanf("%d",&input);
        switch (input) {
            case 1:
                break;
            case 2:
                break;
            case 3:
                break;
            case 4:
                break;
            case 5:
                break;
            case 6:
                break;
            case 0:
                printf("exit\n");
                break;
            default:
                printf("input error\n");
                break;
        }
    } while (input);
}

我们执行一次运行输入0试一下:

C语言小项目-通讯录_C

但是这上面代码是有些需要优化的。比如switch case语句。随着实现的功能增加,操作增加。那么这些case的数组会增加很多,代码的可读性就比较差,后续我们会对这里进行优化。

创建通讯录

创建通讯录需要记录每个通讯录中每个用户的信息。所以需要用到结构体。这个结构体声明我们放在contact.h中。

struct PeoInfo
{
    char name[20];
    int age;
    char sex[5];
    char tele[12];
    char addr[30];
};

然后再test.c中创建结构体变量

    //创建通讯录
    struct PeoInfo con[1000];

但是我们发现上面代码有很多数字,20 5 12 1000等等,这样后续修改不是很方便。所以我们用宏定义来替换。

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

struct PeoInfo
{
    char name[NMAE];
    int age;
    char sex[SEX];
    char tele[TELE];
    char addr[ADDR];
};


    //创建通讯录
    struct PeoInfo con[MAX];

这样代码可读性就好一点,也便于维护

接下来我们要有一些个函数、初始化通讯录、增加通讯录等等

//初始化通讯录
InitContact(con);
//增加
AddContact(con,&size);

但是这样我们发现,这个通讯录在增加成员删除成员的时候,不仅仅要传入通讯录还要传入当前通讯录有多少个信息,所以我们能不能把通讯录和当前存储的个数进行封装

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

放在contact.h中

初始化通讯录

在.h文件中声明InitContact

//声明函数
void InitContact(struct  Contact* ps);

然后在.c文件中实现这个函数

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

另外这里需要注意的是 test.c文件引用头文件不能直接引用 contact.h要去引用contact.c然后contact.c在去引用头部文件contact.h

这里我们调试一次,在初始化通讯录结构体之前,成员的值为:

C语言小项目-通讯录_结构体_02

看到并不是全部都是0,在执行了初始化函数InitContact之后,所有成员都变成了0;

C语言小项目-通讯录_通讯录_03

C语言小项目-通讯录_C_04

这也就意味着我们初始化功能写好了。

增加通讯录成员

还是先在.h文件中声明函数


void AddContact(struct  Contact* ps);

然后去.c文件中去实现

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");
    }
}

增加通讯录成员第一件事要判断通讯录是否满了。如果满了则返回一条信息。

如果未满则按照每个信息进行填写即可。最后要记得对size增加1 。

另外要注意在添加age的时候 因为是int类型所以一定要取到age的地址来添加。

那么这个写完我们运行一下看是否添加成功

执行这个添加之前我们看到成员都初始化好了:

C语言小项目-通讯录_C_05

输入完毕之后

C语言小项目-通讯录_结构体_06

我们看一下成员信息是否已经有了(变量视图)

C语言小项目-通讯录_C_07

这里我们可以看到我们输入的信息已经被记录进去了。这就说明增加功能没有问题了。

这里最好是增加完之后自动打印出来看一下最好,所以我们就实现打印功能。

打印输出通讯录

还是先在.h文件中声明函数

void ShowContact(struct  Contact* ps);

然后去.c文件中去实现


void ShowContact(struct  Contact* ps){
    if(ps->size==0){
        printf("contact null");
    } 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);
        }
    }
}

 注意,输出的时候%加数字是指保留多少宽度,二者中间的-指的是左对齐

随便写点测试一下:

C语言小项目-通讯录_通讯录_08

最下面就是输出的结果。可以看到像一个列表一样,都是左对齐。

优化 switch case语句

我们在上面实现菜单的时候,提到了纯数字的switch语句不方便阅读和维护。所以我们在这里对其进行优化,怎么优化呢?我们可以使用枚举类型。

我们可以在头文件中创建枚举类型

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

然后修改菜单中的switch case语句

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:
                break;
            case MODIFY:
                break;
            case SHOW:
                break;
            case SORT:
                break;
            case EXIT:
                 printf("exit\n");
                break;
            default:
                printf("input error\n");
                break;
        }
    } while (input);
}

这样一来代码可读性就好了,后期需要加什么操作,只需要继续往枚举类型加就行了。

根据姓名删除指定通讯录成员

还是先在.h文件中声明函数

void DelContact(struct  Contact* ps);

然后去.c文件中去实现

   void DelContact(struct  Contact* ps){
    char name[NAME];
    printf("please input del name");
    scanf("%s",name);
    //查找要删除的成员的位置
    int i;
    for (i=0; i < ps->size; ++i) {
        if(0==strcmp(ps->data[i].name,name)){
            break;
        }
    }
         //删除
    if(i==ps->size){
        printf("member is null");
    }else{
        int  j;
        for ( j = 0; j < ps->size-1; ++j) {
            ps->data[j]=ps->data[j+1];
        }
        ps->size--;
        printf("delete successfully");
    }
 
} 

当然删除的方法不止这一个,这个只是把要删除的成员的后一个成员将其覆盖掉,后面依次循环执行覆盖操作。还有个方法是直接将最后一个成员的位置覆盖掉要删除的位置的成员。只不过这样size的序号就乱了。

查找成员

在写这个方法的时候我们很容易发现,查找似乎我们刚刚才写过,对就是在删除指定成员的时候我们写过,因为删除就需要先去查找,当然除了删除,修改也需要。所以我们这里可以将查找功能提前封装出来,封装出一个单独的查找函数。

static int FindByNme(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");
    }
    //删除
}

修改通讯录成员

还是先在.h文件中声明函数

void ModifyContact(const struct  Contact* ps);

然后去.c文件中去实现

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);
    }

}

修改的话比较简单,根据输入的名字先执行查询,然后查询到的话 重新执行一遍插入的时候执行的代码即可,然后打印一下该结果。

排序(根据年龄升序)

还是先在.h文件中声明函数

void SortContact(struct  Contact* ps);

然后去.c文件中去实现

最简单的就是用冒泡排序即可


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);
}

最后排序完成在输出一下,其他的排序可以自行实现一下,包括排序算法也可以读着自己进行修改去实现。


全套代码

1.test.c 用于测试

//
// Created by leslieSu79 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);
}

2.contact.h 声明函数

//
// Created by leslieSu79 on 2023/11/30.
//

#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

3.contact.c 定义函数

//
// Created by leslieSu79 on 2023/11/30.
//

#include "contact.h"

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

}

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 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);
}


总计两百多行吧是个比较简单小程序。主要是复习回顾结构体和枚举类型的使用。