当前位置: 首页 > article >正文

C语言程序设计:可存储的通讯录

前言:本章没有使用链表!

C语言从入门到精通(鹏哥带你C语言从入门到精通,谭浩强C语言教程C语言程序设计C语言修仙C语言考研计算机二级专升本C语言期末突软考二级C语言考研C语言C语言)_哔哩哔哩_bilibili

本文很长,看不懂可以去B站看鹏哥的,讲的非常好,但细节需要自己完善,加油💪💪

通讯录的每个联系人是由

名字、性别、年龄、电话、住址等等

再由每个联系人组成一张列表完成通讯录

注:文中都是拆分的,文尾才是源代码!!

一、基本框架

完成框架:

因为要多次操作,所以需要循环控制操作方法

通讯录的操作方法有很多(增删查改最基础)

1.增加 2.删除 3.查找 4.修改 5.排序 6.打印

所以在头文件中用枚举常量很合适,因为枚举常量可以对应输入数值

 然后就是主函数的框架部分

void menu()
{//菜单printf("*****************************\n");printf("****      通讯录v1.0    ****\n");printf("****   1.增加   2.删除   ****\n");printf("****   3.查找   4.修改   ****\n");printf("****   5.排序   6.打印   ****\n");printf("****        0.退出       ****\n");printf("*****************************\n");
}int main()
{int input = 0;do{menu();					//菜单printf("请输入:>");scanf("%d", &input);getchar();				//吸收换行(\n)switch (input){case Add:				//增加printf("增加\n");break;case Del:				//删除printf("删除\n");break;case Find:				//...printf("查找\n");break;case Change:printf("修改\n");break;case Sort:printf("排序\n");break;case Print:printf("打印\n");break;case Exit:printf("退出\n");break;default:printf("输入错误\n");break;}} while (input);
}

完成主要框架后,可以进行封装函数了

首先建立联系人的结构体,方便寻找成员,然后枚举几个最大值方便以后维护

然后建立通讯录结构体来存储联系人联系人个数

因为我不知道你有多少个联系人,所以采用动态开辟内存

来构造通讯录所占空间,因此通讯录中还需要存储一个变量

来确定当前通讯录的容量以便于扩容

(1) 封装第一个函数:初始化通讯录(记得在头文件声明)

因为要动态开辟内存,所以首先需要初始化通讯录函数来进行开辟空间

记得引用 <string.h>头文件

然后就可以声明通讯录并且初始化了。。

 (2)封装增加联系人函数

因为是动态开辟内存,所以可能会存满需要扩容的情况,所以

先把编写扩容函数,用于判断是否需要扩容的情况,再考虑增加

联系人。realloc 函数可以重新开辟一块空间并且可以将原空间的值

拷贝到新开辟的空间,然后释放原空间,所以重新开辟空间用这个很合适。

 接着封装增加联系人,增加一个继续增加的判断条件,这一部分整体还是很简单的。

 (3)封装打印函数

有了增加方法,那就把打印函数写出来,就可以进行查看是否写入成功

 尝试输入一个联系人

 尝试输入四个联系人

 看来,开辟空间成功了但是这个通讯录的格式很丑,全是右对齐的,

所以我全给搞成左对齐了(其实就是把"%20s"改成"%-20s")

 (4)封装删除联系人函数

删除联系人,首先你得有联系人,所以需要判断通讯录是否还有联系人

然后你还得找到你想删掉的联系人,然后将他删掉。

我加了一个判断防止不小心按到的情况。

因为需要找到联系人,但是后边修改、查找都需要寻找联系人

所以封装一个寻找联系人的函数,返回值为该联系人的下标

接着封装删除联系人函数

//删除联系人
void DeleteData(list* pc)
{if (pc->sz == 0)						//当通讯录没有任何联系人时,直接返回{printf("还没有联系人呢!\n");return;}char check = 0;							//判断是否误操作printf("确定要进行删除操作吗(N取消):>");scanf("%c", &check);CheckCapcity(pc);if (check != 'n' && check != 'N'){char name[MAX_NAME] = { 0 };		//获得删除的人名printf("请输入要删除的联系人:>");scanf("%s", name);int pos = FindContact(pc, name);	//接收返回值if (pos == -1){printf("通讯录中没有这位!\n");	//检查是否存在该联系人return;}for (int i = pos; i < pc->sz -1; i++)	//最后一位不需要移位,防止越界访问{pc->data[i] = pc->data[i + 1];		//让后面一位覆盖前面一位}pc->sz--;								//让最后一位联系人不能访问printf("删除成功\n");}else{printf("取消删除\n");return;}}

这是目前的通讯录,现在要把“我是例外”删除 看看能不能成功

删除成功了,那试试能不能删除“王五”,毕竟通讯录里可没有王五 

 可行!

 (5)封装查找联系人函数

查找函数前面写了,那直接找到并打印出来即可(这个最简单)

因为前面都写过了,复制过来稍微修改即可

 这是当前通讯录的人员,我现在想找到“2”,就试试

可以找到,那看看能不能找到“4”

 

看来出了一点点小问题 ,但问题不大,修改后继续。。

(6)封装修改联系人函数

要修改联系人前需要先找到要修改的联系人,然后再选择修改方式

再进行修改

//修改联系人
void ChangeData(list* pc)
{char check = 0;printf("确定要修改联系人吗(N取消):>");scanf("%c", &check);if (check == 'n' || check == 'N'){printf("取消修改\n");return;}//寻找联系人char name[MAX_NAME] = { 0 };printf("请输入要修改的联系人名字:>");scanf("%s", name);int pos = FindContact(pc, name);if (pos == -1){printf("通讯录中没有这位\n");return;}//修改联系人int ch = 0;		//修改标志位printf("请输入修改类别:1.姓名 2.性别 3.年龄 4.电话 5.地址:>");scanf("%d", &ch);if (ch == 1){printf("请输入修改后的姓名:>");scanf("%s", pc->data[pos].name);}else if (ch == 2){printf("请输入修改后的性别:>");scanf("%s", pc->data[pos].sex);}else if (ch == 3){printf("请输入修改后的年龄:>");scanf("%d",&(pc->data[pos].age));		//注意 取地址(&)}else if (ch == 4){printf("请输入修改后的电话:>");scanf("%s", pc->data[pos].phone);}else if (ch == 5){printf("请输入修改后的地址:>");scanf("%s", pc->data[pos].address);}else{printf("输入错误!\n");return;}printf("修改成功\n");}

试试行不行

 不小心把张三性别写成女了,看看能不能修改

 看来可以修改

(7)封装排序联系人函数

排序可以按照名字排序,也可以按照性别排序,也可以按照年龄排序

也可以按照电话排序,和按照地址排序。

但这有点多(我懒),所以借助<stdlib.h>头文件中的qsort函数来排序吧

 选自:C 库函数 – qsort() | 菜鸟教程 (runoob.com)

 其中重要的就是compar(两个元素间的比较函数),其实就是用一个函数

传两个参数,返回他们的差值,观察差值是大于零还是等于零或者小于零

选择:qsort - C++ Reference (cplusplus.com) 

所以我们需要先写出5种不同的函数指针,用于比较不同的结构体成员

接着封装排序联系人函数

看看能不能行

目前通讯录,接着按名字排序

 可以看到李四跑上去了,因为strcmp是根据首字母的ASCII码值排序的。

李四的拼音首字母是“l”,王五的拼音首字母是“w”,l是小于w的,所以李四在上面

接着按性别排序

 可以看到李四跑到底下去了,是因为男的拼音首字母 “n” 是小于女的拼音首字母也是“n”

所以比较第二个字母nan中的“a”、nv中的“v”,显然a小于v

所以李四在后面。接着排序年龄

 比较整形就没什么可以说的了,那个小,那个在前面

接着比较电话

 注意不要把电话当作整形,这里设置的是字符串,所以还是按照strcmp的比较方式比较。

接着比较地址

还是按照strcmp的比较方式比较 。

到此基本完成了通讯录,剩下一些细节修修补补

比如删除联系人到多少的时候,重新开辟空间,节省空间浪费

又比如在修改联系人的时候,提示选中的是那个联系人

或者修饰一下提示语,让它更显眼。

然后。。

就是关闭时保存数据,下次启动时继续对数据操作,这才是通讯录

二、完善框架

(1)维护内存防止浪费

首先在退出程序时,释放动态开辟的内存空间,防止内存泄漏

所以写一个退出通讯录的函数

 然后是删除联系人过多时,重新开辟空间

其实就是把CheckCapcity(扩容函数)函数稍微修改一下

多加一个条件,当容量-数量>8,也就是容量空余了8个,就重新开辟一块 

空间,当然也可以增加一些宏变量方便以后维护

(2)细节修饰

先将要修改的人提示出来然后修饰

先从网上拷一点制表符

/*
┌ ─ ┐│   │└ ─ ┘
*/

然后将每一个有取消、没有等提示语句加上,例如

        printf("┌────────┐\n");printf("│取消退出│\n");printf("└────────┘\n");printf("┌───────────────┐\n");printf("│还没有联系人呢!│\n");printf("└───────────────┘\n");

 (3)退出后保存

现在程序还是运行在内存上,程序退出后,系统就会回收内存空间。

所以想要保存数据,就需要将数据转移到硬盘上去。

所以在要退出前,将数据保存到文件中,在下次初始化时读出来即可。

写入操作

 读取操作

 以下为gif演示

三、代码展示

源代码展示:

1.Contact.h 文件

#define _CRT_SECURE_NO_WARNINGS 1
#pragma once/******头文件定义*******/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>/******头文件定义*******//********全局变量*******/
enum Contact_tem
{MAX_NAME = 20,			//最大姓名字符数MAX_SEX = 10,			//最大性别字符数MAX_PHONE = 15,		//最大电话字符数MAX_ADDRESS = 20		//最大地址字符数
};enum Input
{Exit,		//退出 值0Add,		//增加 值1Del,		//删除 值2Find,		//查找 值3Change,		//修改 值4Sort,		//排序 值5Print		//打印 值6
};/********全局变量*******//*******结构体定义*******/
typedef struct Contact
{char name[MAX_NAME];		//姓名char sex[MAX_SEX];			//性别int age;					//年龄char phone[MAX_PHONE];		//电话char address[MAX_ADDRESS];	//地址
}Contact;typedef struct PhoneBook
{Contact* data;				//联系人int sz;						//联系人个数int Capcity;				//当前容量
}list;/*******结构体定义*******//*******函数声明********///初始化通讯录
void InitData(list* pc);//增加联系人
void AddData(list* pc);//打印
void PrintData(list* pc);//删除联系人
void DeleteData(list* pc);//查找联系人
void FindData(list* pc);//修改联系人
void ChangeData(list* pc);//排序联系人
void SortData(list* pc);//退出通讯录
void ExitData(list* pc);//保存数据
void SaveData(list* pc);
/*******函数声明********/

2.Contact.c 文件

#include"Contact.h"/**************备用函数区****************///扩容
void CheckCapcity(list* pc)
{Contact* ptr = NULL;						//用于接收重新获得的空间if (pc->Capcity == pc->sz){ptr = (Contact*)realloc(pc->data,(pc->Capcity + 2) * sizeof(Contact));	//重新开辟空间if (ptr == NULL){perror("CheckCapcity");				//开辟失败时报错并退出return;}pc->data = ptr;			//没有错误时将新开辟的空间地址赋予pc->datapc->Capcity += 2;		//重新计算当前容量}if (pc->Capcity - pc->sz > 8)				//删除扩容{ptr = (Contact*)realloc(pc->data, (pc->sz + 2) * sizeof(Contact));		//重新开辟空间为pc->sz+2 个联系人的空间if (ptr == NULL){perror("CheckCapcity");return;}pc->data = ptr;pc->Capcity = pc->sz + 2;			//重新开辟内存容量为pc->sz+2个}}//查找联系人       通讯录      需要寻找的联系人
int FindContact(list* pc, char* str)
{for (int i = 0; i < pc->sz; i++)			//遍历通讯录{if (!(strcmp(pc->data[i].name, str)))	//两个字符串对比使用strcmp函数{return i;							//存在该联系人返回i}}return -1;									//不存在返回-1}//结构体排序 -- 姓名
int cmp_by_name(const void* e1, const void* e2)
{//void*指针不能解引用操作,所以需要转换成需要的类型才能继续return strcmp(((Contact*)e1)->name, ((Contact*)e2)->name);		//字符串的比较方式需要用strcmp函数实现
}
//结构体排序 -- 性别
int cmp_by_sex(const void* e1, const void* e2)
{//void*指针不能解引用操作,所以需要转换成需要的类型才能继续return strcmp(((Contact*)e1)->sex, ((Contact*)e2)->sex);
}
//结构体排序 -- 年龄
int cmp_by_age(const void* e1, const void* e2)
{//void*指针不能解引用操作,所以需要转换成需要的类型才能继续return ((Contact*)e1)->age - ((Contact*)e2)->age;		//整形相减即可
}
//结构体排序 -- 电话
int cmp_by_phone(const void* e1, const void* e2)
{//void*指针不能解引用操作,所以需要转换成需要的类型才能继续return strcmp(((Contact*)e1)->phone, ((Contact*)e2)->phone);
}
//结构体排序 -- 地址
int cmp_by_address(const void* e1, const void* e2)
{//void*指针不能解引用操作,所以需要转换成需要的类型才能继续return strcmp(((Contact*)e1)->address, ((Contact*)e2)->address);
}/**************备用函数区****************//*
┌ ─ ┐│   │└ ─ ┘
*//**************主要函数区****************/
//初始化通讯录
void InitData(list* pc)
{pc->sz = 0;												//初始化时没有联系人pc->Capcity = 3;										//初始容量为3pc->data = (Contact*)malloc(sizeof(Contact) * 3);		//开辟一块内存为3个联系人大小的通讯录if (pc->data == NULL)									//开辟失败返回并报错{perror("InitData");return;}FILE* pf = fopen("text.dat", "a+");						//将文件以可读可写的方式打开,若文件不存在,则新建一个文件if (pf == NULL){perror("fopen");									//当文件打开失败时报错并退出return;}Contact tem = { 0 };									//定义一个变量来变相读取fread的返回值\//		  接收变量   变量大小     接收个数  文件流	fread每读取一次就会返回剩下多少个元素while (fread(&tem, sizeof(Contact), 1, pf)){CheckCapcity(pc);									//判断是否需要扩容pc->data[pc->sz] = tem;								//将原数据覆盖pc->sz++;											//元素个数增加}fclose(pf);												//关闭文件
}/*printf("┌────────┐\n");printf("│取消增加│\n");printf("└────────┘\n");
*/void AddData(list* pc)
{char check = 0;							//判断是否继续添加printf("确定要添加联系人吗(N取消):>");scanf("%c", &check);if (check == 'n' || check == 'N'){//取消添加printf("┌────────┐\n");printf("│取消增加│\n");printf("└────────┘\n");return;}//继续添加CheckCapcity(pc);					//判断扩容printf("请输入姓名:>");scanf("%s", pc->data[pc->sz].name);		//姓名printf("请输入性别:>");scanf("%s", pc->data[pc->sz].sex);			//性别printf("请输入年龄:>");scanf("%d", &(pc->data[pc->sz].age));		//年龄printf("请输入电话:>");scanf("%s", pc->data[pc->sz].phone);		//电话printf("请输入住址:>");scanf("%s", pc->data[pc->sz].address);		//地址pc->sz++;}void PrintData(list* pc)
{printf("%-20s %-10s %-6s %-15s %-20s\n", "姓名", "性别", "年龄", "电话", "地址");	//分类printf("-------------------------------------------------------------------------\n");for (int i = 0; i < pc->sz; i++){printf("%-20s %-10s %-6d %-15s %-20s\n",	pc->data[i].name,		//姓名pc->data[i].sex,		//性别pc->data[i].age,		//年龄pc->data[i].phone,		//电话pc->data[i].address		//地址);printf("-------------------------------------------------------------------------\n");}
}//删除联系人
void DeleteData(list* pc)
{if (pc->sz == 0)						//当通讯录没有任何联系人时,直接返回{printf("┌───────────────┐\n");printf("│还没有联系人呢!│\n");printf("└───────────────┘\n");return;}char check = 0;							//判断是否误操作printf("确定要进行删除操作吗(N取消):>");scanf("%c", &check);CheckCapcity(pc);if (check != 'n' && check != 'N'){char name[MAX_NAME] = { 0 };		//获得删除的人名printf("请输入要删除的联系人:>");scanf("%s", name);int pos = FindContact(pc, name);	//接收返回值if (pos == -1){printf("┌──────────────────┐\n");printf("│通讯录中没有这位!│\n");	//检查是否存在该联系人printf("└──────────────────┘\n");return;}for (int i = pos; i < pc->sz -1; i++)	//最后一位不需要移位,防止越界访问{pc->data[i] = pc->data[i + 1];		//让后面一位覆盖前面一位}pc->sz--;								//让最后一位联系人不能访问printf("┌────────┐\n");printf("│删除成功│\n");printf("└────────┘\n");}else{printf("┌────────┐\n");printf("│取消删除│\n");printf("└────────┘\n");return;}}//查找联系人
void FindData(list* pc)
{char check = 0;printf("确定要查找联系人吗(N取消):>");scanf("%c", &check);if (check == 'n' || check == 'N'){printf("┌────────┐\n");printf("│取消查找│\n");printf("└────────┘\n");return;}//寻找联系人char name[MAX_NAME] = { 0 };printf("请输入要查找的联系人:>");scanf("%s", name);int pos = FindContact(pc,name);if (pos == -1){printf("┌──────────────────┐\n");printf("│通讯录中没有这位!│\n");	//检查是否存在该联系人printf("└──────────────────┘\n");return;}//打印联系人printf("%-20s %-10s %-6s %-15s %-20s\n", "姓名", "性别", "年龄", "电话", "地址");	//分类printf("-------------------------------------------------------------------------\n");printf("%-20s %-10s %-6d %-15s %-20s\n",pc->data[pos].name,		//姓名pc->data[pos].sex,		//性别pc->data[pos].age,		//年龄pc->data[pos].phone,		//电话pc->data[pos].address		//地址);printf("-------------------------------------------------------------------------\n");}//修改联系人
void ChangeData(list* pc)
{char check = 0;printf("确定要修改联系人吗(N取消):>");scanf("%c", &check);if (check == 'n' || check == 'N'){printf("┌────────┐\n");printf("│取消修改│\n");printf("└────────┘\n");return;}//寻找联系人char name[MAX_NAME] = { 0 };printf("请输入要修改的联系人名字:>");scanf("%s", name);int pos = FindContact(pc, name);if (pos == -1){printf("┌──────────────────┐\n");printf("│通讯录中没有这位!│\n");	//检查是否存在该联系人printf("└──────────────────┘\n");return;}printf("当前选中:  < %s >\n", pc->data[pos].name);//修改联系人int ch = 0;		//修改标志位printf("请输入修改类别:1.姓名 2.性别 3.年龄 4.电话 5.地址:>");scanf("%d", &ch);if (ch == 1){printf("请输入修改后的姓名:>");scanf("%s", pc->data[pos].name);}else if (ch == 2){printf("请输入修改后的性别:>");scanf("%s", pc->data[pos].sex);}else if (ch == 3){printf("请输入修改后的年龄:>");scanf("%d",&(pc->data[pos].age));		//注意 取地址(&)}else if (ch == 4){printf("请输入修改后的电话:>");scanf("%s", pc->data[pos].phone);}else if (ch == 5){printf("请输入修改后的地址:>");scanf("%s", pc->data[pos].address);}else{printf("┌────────┐\n");printf("│输入错误│\n");printf("└────────┘\n");return;}printf("┌────────┐\n");printf("│修改成功│\n");printf("└────────┘\n");}//排序联系人
void SortData(list* pc)
{char check = 0;printf("确定要排序吗(N取消):>");scanf("%c", &check);if (check == 'n' || check == 'N'){printf("┌────────┐\n");printf("│取消排序│\n");printf("└────────┘\n");return;}//排序//函数指针数组,将每一个函数指针组成数组,有需要请自行百度int (*pf[6])(const void*, const void*) ={ NULL,cmp_by_name,cmp_by_sex,cmp_by_age,cmp_by_phone,cmp_by_address};int tmp = 0;printf("请选择排序方式:1.姓名 2.性别 3.年龄 4.电话 5.地址:>");scanf("%d", &tmp);//    排序位置	元素个数		单个元素字节数			元素对比方式qsort(pc->data, pc->sz, sizeof(pc->data[0]), pf[tmp]);	//排序函数printf("┌────────┐\n");printf("│排序成功│\n");printf("└────────┘\n");}void ExitData(list* pc)
{char check = 0;printf("确定要退出吗(N取消):>");scanf("%c", &check);if (check == 'N' || check == 'n'){printf("┌────────┐\n");printf("│取消退出│\n");printf("└────────┘\n");return;}free(pc->data);			//释放内存printf("┌────┐\n");printf("│退出│\n");printf("└────┘\n");}//保存数据
void SaveData(list* pc)
{FILE* pf = fopen("text.dat", "w");			//以只写的形式打开文件,如果文件不存在,创建一个新的文件if (pf == NULL){perror("fopen");						//打开失败时报错return;}//		写入数据		单个数据大小	  写入多少	文件流fwrite(pc->data, sizeof(Contact), pc->sz, pf);fclose(pf);									//关闭文件
}
/**************主要函数区****************/

3.text.c 文件

#include"Contact.h"void menu()
{//菜单printf("*****************************\n");printf("****      通讯录v1.0    ****\n");printf("****   1.增加   2.删除   ****\n");printf("****   3.查找   4.修改   ****\n");printf("****   5.排序   6.打印   ****\n");printf("****        0.退出       ****\n");printf("*****************************\n");
}int main()
{int input = 0;list con;			//定义通讯录InitData(&con);		//初始化通讯录do{menu();					//菜单printf("请输入:>");scanf("%d", &input);getchar();				//吸收换行(\n)switch (input){case Add:				//增加AddData(&con);break;case Del:				//删除DeleteData(&con);break;case Find:				//...FindData(&con);break;case Change:ChangeData(&con);break;case Sort:SortData(&con);break;case Print:PrintData(&con);break;case Exit:SaveData(&con);ExitData(&con);break;default:printf("输入错误\n");break;}} while (input);
}

如有错误,敬请指出,多谢🙏

http://www.lryc.cn/news/2416200.html

相关文章:

  • 计算机网络二轮强化(三个重要的表)
  • 搭建自己的个人博客(保姆级教程),服务器、域名、网站全篇
  • 基于springboot+vue.js+uniapp的高校班级同学录网站附带文章源码部署视频讲解等
  • 详解AC97和HD声卡前置音频接口的连接跳线
  • Windows服务的创建
  • 【C++网络编程】Socket基础:网络通讯程序入门级教程
  • 传统服务业/零售业的电商O2O之道
  • 搞笑QQ资料
  • DropDownList的绑定方法
  • smartupload实现简单的文件上传
  • 基于 Zen 创建一个 Drupal 7 的主题(模板) ,一份简单的Drupal模板教程
  • 计算机视频教程大全
  • Java游戏开发 —— 俄罗斯方块
  • 脚本报错 未结束的字符串常量 可能导致的原因
  • Wave音频格式解析
  • linux常用命令—— 磁盘管理(十二)
  • CCTouch触摸事件
  • java递归函数的解析
  • 全国大学生统计建模大赛历年获奖论文集下载教程(知网)
  • adobe dreamweaver cs5序列号
  • 安卓开发学习笔记(1) preferences实现设置界面
  • Hook api! 如何拦截系统api, 让它做你想做的事!
  • 程序框图与计算机程序,_算法与程序框图_ppt.ppt
  • MyEclipse-7.5.0版注册码破解及激活操作
  • 最全音频总线汇总
  • Web 项目中分享到微博、QQ空间等分享功能
  • CheckBoxList、DropDownList、ListBox、RadioButtonList、BulletedList对于符号的解释区别
  • BeyondCompare3密钥过期怎么办?不用再找新的密钥,一招帮你搞定!
  • dell 笔记本禁用触摸板方法
  • 【游戏逆向】CS1.6无限手雷辅助