本篇博客涉及C语言知识点较广!大家如果喜欢就点个免费的赞、收藏和关注叭
1.通讯录可以实现添加联系人、删除联系人、修改联系人、查找联系人(多选项查找),打印通讯录列表(按姓首字母打印)、退出通讯录(退出的同时保存文件,下次打开还存在)
2.个人信息包括联系人的姓名、性别、电话、住址
3.利用函数、结构体、枚举常量(使数字意义化)、动态内存开辟(利用堆区空间,随联系人的人数增加而增加,不浪费空间)、goto语句、结构体指针、函数指针数组、文件指针等相关知识点
认真读完下面的代码简析,说不定能带给你对C语言更加全面的复习!!!✨✨
当我们准备实现各个选项能力之前我们首先就要创建一个项目,这样我们就可以将主函数和各个自己定义的函数、宏定义等分开,使函数代码思路更加的清晰,不显得那么臃肿!!!
先看一下该《通讯录》完整版含有主函数C源文件:
#define _CRT_SECURE_NO_WARNINGS 1
#include "statement.h"//自己定义的头文件
int main()
{
int Input = 0;
int i = 0;
int j = 0;
Number contact;//创建通讯录
Initalizing_address_book(&contact);//初始化通讯录
void (*arr[7])(Number*) = { NULL,Addacontact,deleteacontact,modifyingacontact,showcontacts,Findacontact,sortcontacts };//函数指针数组,减少代码量
do
{
if (i != 0)
{
menu_1();//是否清空屏幕选择菜单
printf("请选择:>");
scanf_s("%d", &j);
if (j == ADD)
{
system("pause");//给操作者考虑时间
system("cls");
}
}
i = 1;//从第二次循环开始,选择是否清空上面屏幕,方便观察
menu();//主菜单列表
printf("请选择:>");
scanf_s("%d", &Input);
if (Input >= 1 && Input <= 6)
{
(arr[Input])(&contact);
}
else if(Input == 0)
{
//保存通讯录
savecontact(&contact);//保存通讯录
destroymemory(&contact);//销毁通讯录在堆区开辟的内存
printf("退出通讯录成功!\n");
break;
}
else
{
printf("选择错误,请重新选择!\n");
}
} while (Input);
return 0;
}
上面代码的文件名:
当我们看到上面的代码,大家肯定是一头雾水!
我们先看这个源代码里面的头文件里面包含的信息吧:还是一段代码:
//头文件
#include
#include
#include
#include
//宏定义
#define MAX_NAME 10//姓名的最大长度
#define MAX_SEX 5//性别字数的最大长度
#define MAX_ADRESS 30//地址的最大长度
#define MAX_MIN 3//第一次开辟的最大储存个数(堆区)
#define MAX_ADD 2//第二、三......后开辟一次后增长个数
#define MAX_PHONE 15//电话的位数的最大长度
//个人信息
typedef struct Man
{
char name[MAX_NAME];//姓名
char sex[MAX_SEX];//性别
int age;//年龄
char phone[MAX_PHONE];//电话号码
char adress[MAX_ADRESS];//地址
}Man;
//通讯录人数
typedef struct Number
{
int num;//记录通讯录中人数
int memory;//方便在堆区开辟内存
Man *number;//当人数足够多,内存合理使用,放在堆区
}Number;
//枚举常量
//使选择有意义
enum Choose
{
EXIT,//0
ADD,//1
DELTE,//2
ALTER,//3
SHOW,//4
FIND,//5
SORT//6
};
//函数声明
void menu();//打印主菜单
void menu_1();//打印清屏选项菜单
void menu_2();//打印查找方式选项菜单
void Initalizing_address_book(Number* pr);//初始化通讯录为0
void Addacontact(Number* pr);//增加联系人
void showcontacts(Number* pr);//打印联系人列表
int findacontact(Number* pr);//搜索引擎
void Findacontact(Number* pr);//查找联系人
void deleteacontact(Number* pr);//删除联系人
void modifyingacontact(Number* pr);//修改联系人
void sortcontacts(Number* pr);//排序联系人
void destroymemory(Number* pr);//销毁内存
void savecontact(Number* pr);//保存通讯录信息
void capacityreduction(Number* pr);//判断是否减容
void add_capacity(Number* pr);//判断是否增容
void extractcontact(Number* pr);//读取文件信息
大家看完上面的代码我们便可以知道adress_list.c的那个源代码文件里面引的头文件便是statement.h这个文件,这样adress_list.c就可以使用statement.h里面的定义了。大家发现statement.h这个文件里面包含了许多的基本宏定义、结构体、函数声明(通讯录功能实现)、一些C语言的头文件。
《通讯录》利用函数指针数组:让代码更加的简介,更加直观,通过用户的选择直接跳转的相应的函数(要比是switch语句)更加简单明了直接!
只要我们上面定义一下,下面只需要根据用户选择跳转相应的函数即可!!!
是不是简单明了吖!❤️
看到这里大家肯定已经迫不及待的想知道这些函数实现的方法了吧:
这个时候我们就要再创建一个实现函数的C源文件,把我们的函数实现过程单独分装开!
我们函数实现的过程尽量在满足程序的同时,要把各个函数独立化,这样就可以在一个项目或者不同项目当中复制黏贴就行了!!!
第三个文件的名字:
提示:在VS编译器会认为scanf这个函数赋值字符串的时候,认为不安全,当然就会有警告!但为了各个平台的通用性(其他平台不一定承认scanf_s这个函数),我们就忽略这个警告!!!
第三个文件代码有点长!❣️❣️❣️
接下来我们就直接上上面各个函数实现过程的代码吧:
#define _CRT_SECURE_NO_WARNINGS 1
#include "statement.h"//引用第二个文件名字
//函数实现C源文件
//打印主菜单
void menu()
{
printf("**********************************************************\n");
printf("*********** 0. 退出程序 *****************\n");
printf("*********** 1. 增加联系人 *****************\n");
printf("*********** 2. 删除联系人 *****************\n");
printf("*********** 3. 修改联系人 *****************\n");
printf("*********** 4. 打印联系人 *****************\n");
printf("*********** 5. 查找联系人 *****************\n");
printf("*********** 6. 排序联系人 *****************\n");
printf("**********************************************************\n");
}
//清空屏幕操作菜单
void menu_1()
{
printf("**********************************************************\n");
printf("************* 1.清空屏幕 *****************\n");
printf("************* 选择其他任意键继续 *****************\n");
printf("**********************************************************\n");
}
//打印查找联系人方式菜单
void menu_2()
{
printf("**********************************************************\n");
printf("*************** 1.姓名搜索 ******************\n");
printf("*************** 2.电话号搜索 ******************\n");
printf("**********************************************************\n");
}
//排序联系人方式菜单
void menu_3()
{
printf("***********************************************************\n");
printf("****************** 1.年龄排序 ******************\n");
printf("****************** 2.性别排序 ******************\n");
printf("****************** 3.姓名排序 ******************\n");
printf("***********************************************************\n");
}
//初始化结构体
void Initalizing_address_book(Number* pr)
{
pr->num = 0;
pr->number = (Man*)malloc(MAX_MIN * sizeof(Man));//动态内存开辟空间
if (pr->number == NULL)//开辟失败后返回空指针
{
perror("Initalizing_address_book");//如果开辟失败报错
printf("内存开辟失败!\n");
return;
}
pr->memory = MAX_ADD;
extractcontact(pr);//调用文件指针将一开始保存的联系人导出,第一次会报一个警告,但无需理会,大家如果想优化可以去实现一下!
}
//添加联系人
void Addacontact(Number* pr)
{
add_capacity(pr);//增容函数
capacityreduction(pr);//减容函数
printf("开始添加!\n");
printf("请输入联系人姓名:>");
scanf("%s", pr->number[pr->num].name);
printf("请输入联系人性别:>");
scanf("%s", pr->number[pr->num].sex);
printf("请输入联系人的年龄:>");
scanf("%d",&(pr->number[pr->num].age));
printf("请输入联系人的电话:>");
scanf("%s", pr->number[pr->num].phone);
printf("请输入联系人的地址:>");
scanf("%s", pr->number[pr->num].adress);
pr->num++;
printf("联系人增加成功!\n");
}
//打印联系人列表
void showcontacts(Number* pr)
{
int i = 0;
printf("%-10s\t%-10s\t%-5s\t%-15s\t%-20s\n", "姓名", "性别", "年龄", "电话","地址");
for (i = 0; i < pr->num; i++)
{
printf("%-10s\t%-10s\t%-5d\t%-15s\t%-20s\n", pr->number[i].name, pr->number[i].sex, pr->number[i].age,pr->number[i].phone, pr->number[i].adress);
}
}
//搜索引擎
static int findacontact(Number* pr)//用static修饰以后同一个项目其他文件无法调用
{
//初始化查询信息
char name[10] = "0";
char number[15] = "0";
int option = 0;
int i = 0;
//第一次操作错误,goto重新循环操作
again:
menu_2();//查询方式菜单打印
printf("请输入方式:>");
scanf_s("%d", &option);
if (option == ADD)
{
printf("请输入的姓名:>");
scanf("%s", name);
for (i = 0; i < pr->num; i++)
{
if (strcmp(pr->number[i].name, name) == 0)
{
return i;//如果查询成功返回下标
}
}
return -1;//失败返回EOF(-1)
}
else if (option == DELTE)
{
printf("请输入要联系人的电话:>");
scanf("%s", number);
for (i = 0; i < pr->num; i++)
{
if (strcmp(pr->number[i].phone, number) == 0)
{
return i;//如果查询成功返回下标
}
}
return -1;//失败返回EOF(-1)
}
else
{
printf("选择错误,请重新选择:>\n");
goto again;
}
}
//查询联系人
void Findacontact(Number* pr)
{
if (pr->num == 0)
{
printf("通讯录为空!\n");
system("pause");//给操作者反应查阅时间
return;
}
//搜索后判断是否查询成功
int ret = findacontact(pr);
if (ret == -1)
{
printf("查询无此人!\n");
}
else
{
printf("查询成功!\n");
printf("%-10s\t%-10s\t%-5s\t%-15s\t%-20s\n", "姓名", "性别", "年龄", "电话", "地址");
printf("%-10s\t%-10s\t%-5d\t%-15s\t%-20s\n", pr->number[ret].name, pr->number[ret].sex, pr->number[ret].age, pr->number[ret].phone, pr->number[ret].adress);
}
}
//删除联系人
void deleteacontact(Number* pr)
{
int i = 0;
if (pr->num == 0)
{
printf("通讯录为空!\n");
system("pause");//给操作者反应查阅时间
return;
}
//先搜索再删除
int ret = findacontact(pr);//调用搜索引擎函数
if (ret == -1)
{
printf("查无此人,删除失败!\n");
}
else
{
for (i = ret; i < pr->num - 1; i++)
{
pr->number[i] = pr->number[i + 1];
}
printf("删除成功!\n");
pr->num--;
}
}
//修改联系人
void modifyingacontact(Number* pr)
{
if (pr->num == 0)
{
printf("通讯录为空,无需修改!\n");
system("pause");//给操作者反应查阅时间
return;
}
//先搜索在修改
int ret = findacontact(pr);
if (ret == -1)
{
printf("查无此人,联系人修改失败!\n");
return;
}
else
{
printf("查询成功,开始修改!\n");
printf("请输入联系人姓名:>");
scanf("%s", pr->number[ret].name);
printf("请输入联系人性别:>");
scanf("%s", pr->number[ret].sex);
printf("请输入联系人的年龄:>");
scanf("%d", &(pr->number[ret].age));
printf("请输入联系人的电话:>");
scanf("%s", pr->number[ret].phone);
printf("请输入联系人的地址:>");
scanf("%s", pr->number[ret].adress);
printf("联系人修改成功!\n");
}
}
//排序联系人
void sortcontacts(Number* pr)
{
int input = 0;
int i = 0;
int j = 0;
if (pr->num <= 1)
{
printf("无需排序!\n");
return;
}
//第一次操作错误,goto重新循环操作
again:
menu_3();
printf("请选择:>");
scanf("%d", &input);
if (input == ADD)
{
for (i = 0; i < pr->num - 1; i++)//年龄由小到大排序
{
for (j = i + 1; j < pr->num; j++)
{
if (pr->number[i].age > pr->number[j].age)
{
Man data = pr->number[i];
pr->number[i] = pr->number[j];
pr->number[j] = data;
}
}
}
printf("排序成功!\n");
}
else if (input == DELTE)//男女排序
{
for (i = 0; i < pr->num - 1; i++)
{
for (j = i + 1; j < pr->num; j++)
{
if (strcmp(pr->number[i].sex,pr->number[j].sex) > 0)
{
Man data = pr->number[i];
pr->number[i] = pr->number[j];
pr->number[j] = data;
}
}
}
printf("排序成功!\n");
}
else if (input == ALTER)//姓名排序
{
for (i = 0; i < pr->num - 1; i++)
{
for (j = i + 1; j < pr->num; j++)
{
if (strcmp(pr->number[i].name, pr->number[j].name) > 0)
{
Man data = pr->number[i];
pr->number[i] = pr->number[j];
pr->number[j] = data;
}
}
}
printf("排序成功!\n");
}
else
{
printf("选择错误,请重新选择!\n");
goto again;
}
}
//销毁开辟的堆区内存
void destroymemory(Number* pr)
{
free(pr->number);
pr->number = NULL;
pr->num = 0;
pr->memory = 0;
}
//保存通讯录信息
void savecontact(Number* pr)
{
FILE* pd = fopen("Contact1.dat", "w");
if (pd == NULL)
{
perror("savecontact");
printf("保存文件失败");
system("pause");
return;
}
int i = 0;
for (i = 0; i < pr->num; i++)
{
fwrite(pr->number + i, sizeof(Man), 1, pd);
}
fclose(pd);
pd = NULL;
}
//提取通讯录信息
void extractcontact(Number* pr)
{
FILE* pw = fopen("Contact1.dat", "r");
if (pw == NULL)
{
return;
}
//提取通讯录
Man ret = { 0 };
while (fread(&ret, sizeof(Man), 1, pw))//挨个将文件当中的信息写进程序
{
add_capacity(pr);
pr->number[pr->num] = ret;
pr->num++;
}
fclose(pw);//关闭文件,防止野指针
pw = NULL;
}
//判断是否增容
void add_capacity(Number* pr)
{
if (pr->num == pr->memory)
{
Man* ps = (Man*)realloc(pr->number, (MAX_MIN + pr->memory) * sizeof(Man));//动态内存增容
if (ps != NULL)
{
pr->number = ps;
pr->memory += MAX_ADD;
printf("增容成功!\n");
}
else//增容失败会返回NULL
{
perror("Addacontact");//减容失败报错
printf("增容失败!\n");
return;//直接结束函数进程
}
}
}
//判断是否减容
void capacityreduction(Number* pr)
{
if ((pr->num - pr->memory) > 4)
{
Man* pe = (Man*)realloc(pr->number, (pr->memory - MAX_MIN) * sizeof(Man));
if (pe != NULL)
{
pr->number = pe;
pr->memory -= MAX_MIN;
printf("减容成功!\n");
}
else
{
perror("Addacontact");//当减容失败的时候报错
return;
}
}
}
1.在void类型当中可以写返回return,其实就是什么也不返回,好处就是当触发条件的时候可以提前终止本函数!
2.当我们涉及动态内存开辟函数和文件打开和关闭的函数都要是成对出现的,不然在大型项目开发当中会出现不可预知的后果!!!【(malloc、realloc、calloc)和free】【fopen和fclose】
3.在我们第一次运行程序的时候会在列表上方出一行英文,因为第一次运行会提取文件里面联系人信息,但新的设备没有该文件,所以会写成文件打开失败的提示,当我们保存联系人以后再二次运行英文便没有了!!!大家也可以进行优化,让第一次就不出现英文,小编就不测了!
上面的三个代码段,便是《通讯录》项目的完整代码!!!
代码当中每一条语句都有讲解,所以大家不要觉得小编讲的少,代码注释小编已经非常努力的解释了!!!
代码大家需要可以直接复制带走,只希望大家可以给小编点个赞、收藏、关注!!!