C语言项目——静态通讯录系统从0到1实现


前言

该程序不涉及文件读写,不涉及动态功能,但实现了分文件编写,部分功能还有待完善和优化。程序主要运用以下知识点:

  • 结构体
  • 函数封装
  • memset
  • qsort
  • strcmp

一、程序介绍

设计一款基础的通讯录系统,实现功能包括以下:

  • 添加好友信息
  • 查看好友信息
  • 删除好友信息
  • 查找具体好友
  • 修改好友信息
  • 好友年龄排序

在这里插入图片描述

二、面向结果

在具体实现之前,先思考程序整个执行流程是怎样的,首先是弹出帮助菜单,菜单中包含需求功能,菜单前面有唯一的编号,提示用户选择,用户通过输入编号进行相应的操作,操作结束后,继续弹出菜单供用户选择,从而进行不同的操作,循环这一过程,直到用户选择退出操作,整个程序停止。
在这里插入图片描述

三、程序实现

1. 框架搭建

1.1 思路

首先建立三个文件:mian.c、contact.h、contact.c
根据面向结果,在用户选择退出之前,规范输入时,需要一直进行弹出菜单->选择提示->键入指令->用户操作这一循环,这里用do while()搭配switch case来实现这一循环,当用户输入0时, while()接收这一变量,正好停止循环实现了退出操作;当用户输入其它选择后,scanf接收这一选择,程序根据不同的选择来执行相应case下的功能,选择什么指令,就让它走什么功能。在程序刚开始运行的时候,要让菜单显示出来。

  • 外部文件引用
  • do while搭建
  • 菜单封装
  • 提示输入
  • 用户键入
  • switch case匹配

1.2 实现

#include "game.h"

void menu()
{
    printf("-------------通讯录-----------------\n");
    printf("*****1.增加          2.删除*********\n");
    printf("*****3.查找          4.修改*********\n");
    printf("*****5.查看          6.排序*********\n");
    printf("*************0.退出*****************\n");
    printf("------------------------------------\n");
}

int main(int argc, char const *argv[])
{
    int input;	//接收用户输入
   // Contact con;//定义通讯录变量
   // Init_Contact(&con);//初始化通讯录
    do
    {
        menu();
        printf("请选择:>\n");
        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("已退出!\n");
            break;
        default:
            printf("非法输入!!!\n");
            break;
        }
    } while (input);

你可以在case下添加printf语句来代替功能,进行初步的测试。到这里,代码雏形搭建完毕,开始填内容。

2. 类型定义

2.1 思路

首先,通讯录每一个成员信息都要包含姓名,年龄,性别,电话,住址,所以我们要用到结构体数组,这是毋庸置疑的,但是定义数组定义为多少呢?这个数组会用在很多地方,如果你不想以后更改的时候麻烦,建议将数组大小进行define定义
其次,需要思考一个问题,就是当我添加好友时,通讯录是怎么清楚地知道放到数组哪个位置呢?如果在添加之前,我能知道当前系统里存放了多少信息,那么通讯录是不是就知道存放位置了,我可以把它放到总信息数后面的位置,所以我们需要一个变量count来记录通讯录里信息的数量,最开始是0,当增加信息时,count+1,删除信息时,count-1
最后一点(看个人),数组既然和count绑到一起了,在进行增加删除等操作传参时,我既要传递数组名,也要传递count变量,不如我把它们封装到一起,也就是再定义一个结构体,用来存放结构体数组和count

重定义一个好友结构体,存放好友的信息。
重定义一个通讯录结构体,用来存放信息名单和数量。
学过java的可以理解为这里定义了两个类

2.2 实现

这里我将结构体成员的大小也进行了定义。

#include <stdio.h>

#define MAX_NAME 20
#define MAX_GENDER 10
#define MAX_TELE 20
#define MAX_ADDRESS 50
#define MAX_V 100
// people类
typedef struct PeopleInfo
{
    char name[MAX_NAME];	//姓名
    int age;				//年龄
    char gender[MAX_GENDER];	//性别
    char tele[MAX_TELE];		//电话
    char address[MAX_ADDRESS];	//地址
}PeopleInfo;

// 通讯录类
typedef struct  Contact
{
    PeopleInfo data[MAX_V];//定义结构体数组
    int count;		//记录数量变化
}Contact;

3. 具体实现

3.1 初始化通讯录

思路

首先,我们要定义一个初始化的通讯录变量con,在进行初始化时,因为要改变内容,所以是地址传递。创建初始化函数Init_Contact(),实参传递&con,形参用同类型指针接收Contact* pc
为什么要初始化通讯录?如果不初始化,结构体成员和count的值都是随机数,而我们希望count的初始大小为0成员的初始值也为0,那怎么初始化呢?data是一块大的内存空间,要将这块空间内容都改为0,直接用memset搞定,将这块空间全部置0,空间大小为sizeof(pc->data)

main.c
	int input;
    Contact con;//定义通讯录变量
    Init_Contact(&con);//初始化通讯录
    do
    ....
contact.h
#include <string.h>		//	memset头文件
void Init_Contact(Contact* pc);
contact.c
void Init_Contact(Contact* pc)
{
    pc->count = 0;
// memset(需要清空空间的首地址,(设置空间值, 默认写0,设置其它不准确), 需要清空的空间字节大小)
    memset(pc->data, 0, sizeof(pc->data));
}

3.2 添加好友信息

思路

首先要做的就是判断通讯录有没有满,如果满了要提示用户通讯录已满;如果没满则可以正常添加。在scanf接收值时,对于字符数组,只需传数组名,对于int变量,需要传入地址。在添加时,通过pc->data[pc->count]就可以获取到好友结构体,然后通过.就可以获取到数组里的成员,那么这[]里为什么要传入pc->count呢,请自行领会其中奥妙。记得添加后count要++

main.c
case 1:
      Add_Contact(&con);//添加人员信息
      break;
contact.h
void Add_Contact(Contact* pc);
contact.c
void Add_Contact(Contact* pc)
{
    // 判断通讯录是否已满
    if (pc->count == MAX_V)
    {
        printf("通讯录已满,无法添加\n");
        return;
    }
    // 不走如果则代表没有满,可以正常添加
    printf("请输入姓名:");
    scanf("%s", pc->data[pc->count].name);//这数组名即是地址,所以不用取地址符
    printf("请输入年龄:");
    scanf("%d", &pc->data[pc->count].age);//这里年龄定义的是int,一定别忘了取地址
    printf("请输入性别:");
    scanf("%s", pc->data[pc->count].gender);
    printf("请输入电话:");
    scanf("%s", pc->data[pc->count].tele);
    printf("请输入地址:");
    scanf("%s", pc->data[pc->count].address);
    // 添加后count要加1
    pc->count++;
    printf("添加成功!\n");
}

3.3 查看好友信息

思路

添加信息后,添加是否准确,我们需要查看一下,所以它是第二个要实现的功能。
查看信息无非就是遍历通讯录列表,那么循环条件我们怎么设置呢?i < pc->count
在输出格式上,C语言默认是左对齐,而添加了宽度后,默认变为右对齐,如果想让它左对齐,需要在%后加-

main.c
case 5:
      Show_Contact(&con);//输入一个信息后,展示一下看效果
      break;
contact.h
void Show_Contact(const Contact* pc);	//这里可以添加个const,意为无法对pc更改
contact.c
void Show_Contact(const Contact* pc)
{
    printf("\n");  
    // 姓名/年龄/性别/电话/地址
    printf("\t%-5s\t%-3s\t%-5s\t%-13s\t%-20s\n", "姓名", "年龄", "性别", "电话", "地址");
    for (int i = 0; i < pc->count; i++)
    {
        printf("\t%-5s\t%-3d\t%-5s\t%-13s\t%-20s\n", pc->data[i].name,
                                                   	 pc->data[i].age,
                                                     pc->data[i].gender,
                                                     pc->data[i].tele,
                                                     pc->data[i].address);                                            
    }
    printf("\n"); 
}

3.4 删除好友信息

思路

在删除时,首先要判断通讯录是否为空,空则提示用户;不为空,则通过设立查找函数find_by_name,遍历名单,用strcmp()来判断和输入值相同的name,没有返回-1提示用户;有则返回所在位置,进行删除操作。删除思路为:要删除的位置后的所有数组成员往前移一个单元,覆盖前面数据。,删除完毕后count--

main.c
case 2:
      Del_Contact(&con);//删除人员信息
      break;
contact.h
void Del_Contact(Contact* pc);
contact.c
// 查找思路:遍历整个data的名字,判断是否有=用户输入的
// 有的话返回下标,没有返回-1
int find_by_name(Contact* pc, char name[])
{
    for (int i = 0; i < pc->count; i++)
    {
        if (0 == strcmp(pc->data[i].name, name))
        {
            return i;
        }    
    }
    return -1;
}
// 删除人员信息
// 判断通讯录是否为空,空则提示用户没有信息可删除
// 判断该人员是否存在,不存在,提示不存在
// 存在则进行删除操作
// count减1
// 删除操作:1.确定查找依据。2.查找是否有这个人否。2.有的话返回下标。3.没有提示用户,返回-1
// 查找操作:find_by_name()
void Del_Contact(Contact* pc)
{
    char name[MAX_NAME] = {0};//接收用户输入
    int ret;//接收查找后的返回值
    int i;
    // 判断通讯录是否为空
    if (pc->count == 0)
    {
        printf("通讯录为空,没有信息可以删除!\n");
        return;
    }
    // 用户输入查找依据-这里通过姓名进行查找
    printf("请输入要删除的姓名:\n");
    scanf("%s", name);
    ret = find_by_name(pc, name);//要在pc里找name
    if (-1 == ret)//返回-1提示不存在
    {
        printf("要删除的人不存在!\n");
    }
    // 不等于-1代表存在,进行删除操作
    // 删除思路:将所有的人员信息往前挪一位
    for (i = ret; i < pc->count - 1; i++)
    {
        pc->data[i] = pc->data[i+1];
    }
    pc->count--;
    printf("删除成功!\n");
}

3.5 查找具体好友

思路

依旧是判断通讯录是否为空,为空提示用户,不为空提示用户输入要查找的名字,然后将名字传给find_by_name里进行查找,如果返回-1提示用户没找到,如果返回的是位置,则将其输出显示。

main.c
case 3:
      Search_Contact(&con);//查找指定人员信息
      break;
contact.h
void Search_Contact(Contact* pc);
contact.c
// 查找指定人员信息
// 根据名字查找
void Search_Contact(Contact* pc)
{
    char name[MAX_NAME] = {0};
    int ret;//接收查找后的返回值
    // 判断通讯录是否为空
    if (pc->count == 0)
    {
        printf("通讯录为空,无法查找!\n");
        return;
    }
    // 然后可以进行搜索啦
    printf("请输入要查找的姓名:\n");
    scanf("%s", name);
    ret = find_by_name(pc, name);//获取查取到的下标
    if (ret == -1)
    {
        printf("没有找到!\n");
        return;
    }
    printf("\n");
    printf("\t%-5s\t%-3s\t%-5s\t%-13s\t%-20s\n", "姓名", "年龄", "性别", "电话", "地址");
    printf("\t%-5s\t%-3d\t%-5s\t%-13s\t%-20s\n", pc->data[ret].name,
                                                 pc->data[ret].age,
                                                 pc->data[ret].gender,
                                                 pc->data[ret].tele,
                                                 pc->data[ret].address);
    printf("\n");                                               
}

3.6 修改好友信息

思路
  • 先判断通讯录是否为空,为空提示信息
  • 不为空再判断要修改的人员在不在,通过名字在find_by_name进行索引
  • 名字在,提示用户修改
main.c
case 4:
      Modify_Contact(&con);//修改指定人员信息
      break;
contact.h
void Modify_Contact(Contact* pc);
contact.c
void Modify_Contact(Contact* pc)
{
    char name[MAX_NAME] = {0};
    int ret;//接收查找后的返回值
    // 判断通讯录是否为空
    if (pc->count == 0)
    {
        printf("通讯录为空,无法修改!\n");
        return;
    }
    printf("请输入要修改的姓名:\n");
    scanf("%s", name);
    //判断要修改的人员在不在 
    ret = find_by_name(pc, name);
    if (ret == -1)
    {
        printf("要修改的信息不存在!\n");
        return;
    }
    // 判断完前面两个就可以进行修改啦
    printf("请输入姓名:");
    scanf("%s", pc->data[ret].name);//这数组名即是地址,所以不用取地址符
    printf("请输入年龄:");
    scanf("%d", &pc->data[ret].age);//这里年龄定义的是int,一定别忘了取地址
    printf("请输入性别:");
    scanf("%s", pc->data[ret].gender);
    printf("请输入电话:");
    scanf("%s", pc->data[ret].tele);
    printf("请输入地址:");
    scanf("%s", pc->data[ret].address);
    printf("修改成功!\n");
}

3.7 好友信息排序

思路

引入头文件:#include <stdlib.h>
qsort排序函数:用来快速排序,升序是e1-e2(即e1<e2),降序是e2-e1(即e2<e1),具体看注释。

main.c
case 6:
      Sort_Contact(&con);//对名单进行排序(年龄)
      break;
contact.h
void Sort_Contact(Contact* pc);
contact.c
// void*:无具体类型的指针   能够接收任意类型的地址,需要进行强制类型转换
int sort_peo_by_age(const void* e1, const void* e2)
{
    // 如果e1的年龄<e2的年龄,会返回负数,返回负数则把e1排在e2的左面(前面)
    return ((PeopleInfo*)e1)->age - ((PeopleInfo*)e2)->age;
}
// 根据年龄进行排序
void Sort_Contact(Contact* pc)
{
    // (要排序的数组首地址, 数组元素个数, 数组成员的大小, 排序函数(自己声明))
    qsort(pc->data, pc->count, sizeof(PeopleInfo), sort_peo_by_age);
    printf("排序成功!\n");
}

到这里就全部结束啦~~~一罐啤酒,一篇文章

四、全部代码

main.c

#include "contact.h"

void menu()
{
    printf("-------------通讯录-----------------\n");
    printf("*****1.增加          2.删除*********\n");
    printf("*****3.查找          4.修改*********\n");
    printf("*****5.查看          6.排序*********\n");
    printf("*************0.退出*****************\n");
    printf("------------------------------------\n");
}

int main(int argc, char const *argv[])
{
    int input;
    Contact con;//定义通讯录变量
    Init_Contact(&con);//初始化通讯录
    do
    {
        menu();
        printf("请选择:>\n");
        scanf("%d", &input);
        
        switch (input)
        {
        case 1:
            Add_Contact(&con);//添加人员信息
            break;    
        case 2:
            Del_Contact(&con);//删除人员信息
            break;
        case 3:
            Search_Contact(&con);//查找指定人员信息
            break;
        case 4:
            Modify_Contact(&con);//修改指定人员信息
            break;
        case 5:
            Show_Contact(&con);//输入一个信息后,展示一下看效果
            break;
        case 6:
            Sort_Contact(&con);//对名单进行排序(年龄)
            break;
        case 0:
            printf("已退出!\n");
            break;
        default:
            printf("非法输入!!!\n");
            break;
        }
    } while (input);
    
    return 0;
}

contact.h

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

#define MAX_NAME 20
#define MAX_GENDER 10
#define MAX_TELE 20
#define MAX_ADDRESS 50
#define MAX_V 100
// people类
typedef struct PeopleInfo
{
    char name[MAX_NAME];	//姓名
    int age;				//年龄
    char gender[MAX_GENDER];	//性别
    char tele[MAX_TELE];		//电话
    char address[MAX_ADDRESS];	//地址
}PeopleInfo;

// 通讯录类
typedef struct  Contact
{
    PeopleInfo data[MAX_V];//定义结构体数组
    int count;		//记录数量变化
}Contact;



void Init_Contact(Contact* pc);
void Add_Contact(Contact* pc);
void Show_Contact(const Contact* pc);
void Del_Contact(Contact* pc);
void Search_Contact(Contact* pc);
void Modify_Contact(Contact* pc);
void Sort_Contact(Contact* pc);

contact.c

#include "contact.h"

// 清空pc->data的空间并将通讯录的数置为0
void Init_Contact(Contact* pc)
{
    pc->count = 0;
// memset(需要清空空间的首地址,(设置空间值, 默认写0,设置其它不准确), 需要清空的空间字节大小)
// 返回值:清空空间后首地址
    memset(pc->data, 0, sizeof(pc->data));
}
//添加之前要先判断通讯录是否已满
// 满了提示,提示删除已有联系人?或扩容?
// 没满则正常添加
void Add_Contact(Contact* pc)
{
    // 判断通讯录是否已满
    if (pc->count == MAX_V)
    {
        printf("通讯录已满,无法添加\n");
        return;
    }
    // 不走如果则代表没有满,可以正常添加
    printf("请输入姓名:");
    scanf("%s", pc->data[pc->count].name);//这数组名即是地址,所以不用取地址符
    printf("请输入年龄:");
    scanf("%d", &pc->data[pc->count].age);//这里年龄定义的是int,一定别忘了取地址
    printf("请输入性别:");
    scanf("%s", pc->data[pc->count].gender);
    printf("请输入电话:");
    scanf("%s", pc->data[pc->count].tele);
    printf("请输入地址:");
    scanf("%s", pc->data[pc->count].address);
    // 添加后count要加1
    pc->count++;
    printf("添加成功!\n");
}
// 展示信息
// 正常展示输出即可,搞一搞格式
// 须遍历整个data
// MAX_NAME 20
// MAX_GENDER 10
// MAX_TELE 20
// MAX_ADDRESS 50
// MAX_V 10
void Show_Contact(const Contact* pc)
{
    printf("\n");  
    // 姓名/年龄/性别/电话/地址
    // C语言默认是左对齐,而添加了宽度后,默认变为右对齐,如果想让它左对齐,需要在`%后加-`
    printf("\t%-5s\t%-3s\t%-5s\t%-13s\t%-20s\n", "姓名", "年龄", "性别", "电话", "地址");
    for (int i = 0; i < pc->count; i++)
    {
        printf("\t%-5s\t%-3d\t%-5s\t%-13s\t%-20s\n", pc->data[i].name,
                                                   	 pc->data[i].age,
                                                     pc->data[i].gender,
                                                     pc->data[i].tele,
                                                     pc->data[i].address);
                                                  
    }
    printf("\n"); 
}
// 查找思路:遍历整个data的名字,判断是否有=用户输入的
// 有的话返回下标,没有返回-1
int find_by_name(Contact* pc, char name[])
{
    for (int i = 0; i < pc->count; i++)
    {
        if (0 == strcmp(pc->data[i].name, name))
        {
            return i;
        }    
    }
    return -1;
}
// 删除人员信息
// 判断通讯录是否为空,空则提示用户没有信息可删除
// 判断该人员是否存在,不存在,提示不存在
// 存在则进行删除操作
// count减1
// 删除操作:1.确定查找依据。2.查找是否有这个人否。2.有的话返回下标。3.没有提示用户,返回-1
// 查找操作:find_by_name()
void Del_Contact(Contact* pc)
{
    char name[MAX_NAME] = {0};
    int ret;//接收查找后的返回值
    int i;
    // 判断通讯录是否为空
    if (pc->count == 0)
    {
        printf("通讯录为空,没有信息可以删除!\n");
        return;
    }
    // 用户输入查找依据-这里通过姓名进行查找
    printf("请输入要删除的姓名:\n");
    scanf("%s", name);
    ret = find_by_name(pc, name);
    if (-1 == ret)
    {
        printf("要删除的人不存在!\n");
    }
    // 不等于-1代表存在,进行删除操作
    // 删除思路:将所有的人员信息往前挪一位
    for (i = ret; i < pc->count - 1; i++)
    {
        pc->data[i] = pc->data[i+1];
    }
    pc->count--;
    printf("删除成功!\n");
}

// 查找指定人员信息
// 根据名字查找
void Search_Contact(Contact* pc)
{
    char name[MAX_NAME] = {0};
    int ret;//接收查找后的返回值
    // 判断通讯录是否为空
    if (pc->count == 0)
    {
        printf("通讯录为空,无法查找!\n");
        return;
    }
    // 然后可以进行搜索啦
    printf("请输入要查找的姓名:\n");
    scanf("%s", name);
    ret = find_by_name(pc, name);//获取查取到的下标
    if (ret == -1)
    {
        printf("没有找到!\n");
        return;
    }
    printf("\n");
    printf("\t%-5s\t%-3s\t%-5s\t%-13s\t%-20s\n", "姓名", "年龄", "性别", "电话", "地址");
    printf("\t%-5s\t%-3d\t%-5s\t%-13s\t%-20s\n", pc->data[ret].name,
                                                 pc->data[ret].age,
                                                 pc->data[ret].gender,
                                                 pc->data[ret].tele,
                                                 pc->data[ret].address);
    printf("\n");                                               
}

//修改指定人员的信息
// 先判断通讯录是否为空
// 不为空,在判断要修改的人员在不在,通过名字进行索引
// 名字在,提示用户修改
void Modify_Contact(Contact* pc)
{
    char name[MAX_NAME] = {0};
    int ret;//接收查找后的返回值
    // 判断通讯录是否为空
    if (pc->count == 0)
    {
        printf("通讯录为空,无法修改!\n");
        return;
    }
    printf("请输入要修改的姓名:\n");
    scanf("%s", name);
    //判断要修改的人员在不在 
    ret = find_by_name(pc, name);
    if (ret == -1)
    {
        printf("要修改的信息不存在!\n");
        return;
    }
    // 判断完前面两个就可以进行修改啦
    printf("请输入姓名:");
    scanf("%s", pc->data[ret].name);//这数组名即是地址,所以不用取地址符
    printf("请输入年龄:");
    scanf("%d", &pc->data[ret].age);//这里年龄定义的是int,一定别忘了取地址
    printf("请输入性别:");
    scanf("%s", pc->data[ret].gender);
    printf("请输入电话:");
    scanf("%s", pc->data[ret].tele);
    printf("请输入地址:");
    scanf("%s", pc->data[ret].address);
    printf("修改成功!\n");
}


int sort_peo_by_age(const void* e1, const void* e2)
{
    // 如果e1的年龄<e2的年龄,会返回负数,返回负数则把e1排在e2的左面(前面)
    return ((PeopleInfo*)e1)->age - ((PeopleInfo*)e2)->age;
}
// 根据年龄进行排序
void Sort_Contact(Contact* pc)
{
    // (要排序的数组首地址, 数组大小, 数组成员的大小, 排序函数(自己声明))
    qsort(pc->data, pc->count, sizeof(PeopleInfo), sort_peo_by_age);
    printf("排序成功!\n");
}

测试

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值