前言
该程序不涉及文件读写,不涉及动态功能,但实现了分文件编写
,部分功能还有待完善和优化。程序主要运用以下知识点:
- 结构体
- 函数封装
- 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");
}
测试