目录
1.创建contact.h,contact.c,test.c三个文件
1.1 contact.h文件
1.需要包含头文件;
2.#define定义的常量;
3.#pragma指令的包含;
4.结构体的创建;
5.函数的声明;
//防止头文件多次包含
#pragma once
//防止函数报不安全警告
#pragma warning(disable:4996)
//头文件的包含
#include<stdio.h>
//#define定义的常量
#define MAX 50 //假设最大容量是50个
#define MAX_NAME 20//名字存放的大小
#define MAX_SEX 6//性别存放的大小
#define MAX_TELE 12//类型方式存放的大小
#define MAX_ADDR 20//地址存放的大小
//创建一个存放人信息的结构体
typedef struct PeoInf
{
char name[MAX_NAME];//名字
int age;//年龄
char sex[6];
char tele[MAX_TELE];//联系方式
char addr[MAX_ADDR];//地址
}PeoInf;//类型重定义为PeoInf
//创建一个通讯录结构体
typedef struct Contact
{
int count;//记录当前存放的个数
PeoInf data[MAX];//结构体数组
}Contact;//类型重定义为Contact
1.2 contact.c文件实现函数的定义
#include "contact.h"//包含自定义头文件
1.3 test.c文件
1.实现menu菜单函数;
2.使用枚举完成替换;
3.使用switch分支实现功能的选择;
#include "contact.h"//包含自定义头文件
//菜单:增删查改,展示,排序,退出
void menu()
{
printf("************************************\n");
printf("******* 1.Add 2.Delete ****\n");
printf("******* 3.Search 4.Modify ****\n");
printf("******* 5.Show 6.Sort ****\n");
printf("******* 0.Exit ****\n");
printf("************************************\n");
}
//枚举常量替换
enum
{
EXIT,//0
ADD,//1
DELETE,//2
SEARCH,//3
MODIFY,//4
SHOW,//5
SORT//6
};
int main()
{
int input = 0;
do
{
menu();
printf("请选择:>");
(void)scanf("%d", &input);
switch (input)
{
case ADD:
break;
case DELETE:
break;
case SEARCH:
break;
case MODIFY:
break;
case SHOW:
break;
case SORT:
break;
case EXIT:
printf("退出程序中...\n");
break;
default:
printf("请重新选择!\n");
}
} while (input);
return 0;
}
2.InitidContact初始化函数
在test.c文件中创建一个Contact结构体变量 con,并在该文件中调用InitidContact函数。
int main()
{
int input = 0;//功能选择
Contact con;//创建通讯录的变量
//初始化函数
InitidContact(&con);//指针传参
//多次选择
do
{
menu();
printf("请选择:>");
(void)scanf("%d", &input);
switch (input)
{
case ADD:
break;
case DELETE:
break;
case SEARCH:
break;
case MODIFY:
break;
case SHOW:
break;
case SORT:
break;
case EXIT:
printf("退出程序中...\n");
break;
default:
printf("请重新选择!\n");
}
} while (input);
return 0;
}
在contact.c文件事项该函数的定义,需要使用assert函数和memset函数。
#include "contact.h"//包含自定义头文件
void InitidContact(Contact* con)
{
assert(con);//保证con不为空指针
//初始化:使用memset函数
memset(con,0, MAX * sizeof(Contact));
}
contact.h文件需要包含assert和memset函数需要的头文件和初始化函数的定义。
//防止头文件多次包含
#pragma once
//防止函数报不安全警告
#pragma warning(disable:4996)
//头文件的包含
#include<stdio.h>//标准输入输出需要的头文件
#include<assert.h>//断言函数需要的头文件
#include<string.h>//memset函数和字符串函数需要的文件
//#define定义的常量
#define MAX 50 //假设最大容量是50个
#define MAX_NAME 20
#define MAX_SEX 6
#define MAX_TELE 12
#define MAX_ADDR 20
//创建一个存放人信息的结构体
typedef struct PeoInf
{
char name[MAX_NAME];//名字
int age;//年龄
char sex[6];//性别
char tele[MAX_TELE];//联系方式
char addr[MAX_ADDR];//地址
}PeoInf;//类型重定义为PeoInf
//创建一个通讯录结构体
typedef struct Contact
{
int count;//记录当前存放的个数
PeoInf data[MAX];//结构体数组
}Contact;//类型重定义为Contact
//函数的声明
//初始化函数
void InitidContact(Contact* con);
此时可以按F10启动调试查询能否初始化数据,可以打开监视窗口观察。
3.Add函数
在contact.h文件中包含Add函数的声明。
//Add函数
void Add(Contact* con);
在contact.c文件实现文件的定义
//Add函数的定义
void Add(Contact* con)
{
assert(con);
//判断当前存放的个数是否满了
if (con->count == MAX)
{
//此时不能存放,终止增加
printf("当前容量已满,不可创建,退出中.......\n");
return;
}
//此时容量未满,可以增加
//找到当前数组的位置
printf("请输入名字:>");
scnaf("%s", con->data[con->count].name);//字符串数组名标识地址不需要&
printf("请输入年龄:>");
scnaf("%s", &(con->data[con->count].age));//age是变量需要&
printf("请输入性别:>");
scnaf("%s", con->data[con->count].sex);
printf("请输入联系方式:>");
scnaf("%s", con->data[con->count].tele);
printf("请输入地址:>");
scnaf("%s", con->data[con->count].addr);
//此时增加成功,将count++
con->count++;
//提示一下,增加成功
printf("输入的信息已经保存\n");
}
在test.c文件调用Add函数。
do
{
menu();
printf("请选择:>");
(void)scanf("%d", &input);
switch (input)
{
case ADD:
Add(&con);//调用Add函数
break;
case DELETE:
break;
case SEARCH:
break;
case MODIFY:
break;
case SHOW:
break;
case SORT:
break;
case EXIT:
printf("退出程序中...\n");
break;
default:
printf("请重新选择!\n");
}
} while (input);
4.Show函数
实现Add函数后需要将Add含增加的信息展示,使用Show函数输出对应的信息。
contact.h文件实现函数声明。
//Show函数
void Show(Contact* con);
contact.c文件实现函数的定义
//Show函数的定义
void Show(Contact* con)
{
assert(con);//con不为空指针
//判断count是否为0
if (con->count == 0)
{
printf("没有信息,不可展示!\n");
return;//结束Show函数
}
//打印标题行:-表示左对齐,\t是间隔8个水平制表位
printf("%-20s%-5s\t%-6s\t%-12s\t%s\n","名字","年龄","性别","联系方式","地址");
//使用for循环遍历输出打印
int i = 0;//从下标为0的元素开始
for (i = 0; i < con->count; i++)
{
printf("%-20s%-5d\t%-6s\t%-12s\t%s\n", con->data[i].name,
con->data[i].age, con->data[i].sex,
con->data[i].tele, con->data[i].addr);
}
}
test.c文件实现Show函数的调用
do
{
menu();
printf("请选择:>");
(void)scanf("%d", &input);
switch (input)
{
case ADD:
Add(&con);//调用Add函数
break;
case DELETE:
break;
case SEARCH:
break;
case MODIFY:
break;
case SHOW:
Show(&con);//调用Show函数
break;
case SORT:
break;
case EXIT:
printf("退出程序中...\n");
break;
default:
printf("请重新选择!\n");
}
} while (input);
按Ctrl + F5开始调试不执行,测试当前的增加和展示.
5.Delete函数
Delete函数是将指定用户的信息进行删除,需要先输入指定的删除对象,实现一个FindName函数查询该用户是是否存在,如果存在将指定用户删除后进行覆盖,进入不存在输出提示信息。
contact.h文件实现函数的声明和头文件的包含
#include<string.h>//memset函数和strcmp函数的头文件
void contact(Contact* con);//函数声明
contact.c文件实现函数的定义。
int FindName(Contact*con,const char* tem)
{
assert(tem);//此时con已经在Delet数组中调用,不需要判断con
//使用数组遍历查找
int i = 0;
for (i = 0; i < con->count; i++)
{
//判断名字是否相等,使用strcmp函数
//头文件是#include<string.h>
if(strcmp(con->data[i].name,tem) == 0)
return i;//表示找到,返回下标
}
return -1;//遍历数组后还找不到返回-1
}
//Delete函数的定义
void Delete(Contact* con)
{
assert(con);//判断con不为空指针
char tem[20] = { 0 };
printf("请输入需要删除的对象:>");
scanf("%s", tem);
//1.需要找到删除的对象,需要实现一个查找函数;
int r = FindName(con, tem);
//2.判断是否找到
//如果找到了进行覆盖
if (r != -1)//找到了,通过后一个信息覆盖前一个信息完成删除
{ //使用for循环遍历数组
//从下标为r的数组元素开始覆盖
for (; r < con->count - 1; r++)//count - 1表示数组倒数第二个元素
{//count - 1表示数组倒数第二个元素,防止数组越界
//memcpy函数的头文件是#include<string.h>
memcpy(con->data + r, con->data + r + 1, sizeof(PeoInf));
//memcpy是内存块拷贝函数,第一个参数是下标为r的元素的起始地址
//第二个元素是r + 1下标元素的起始地址,第三个参数是拷贝元素的
//大小,刚刚好是一个人信息结构体大小。
}
}
//找不到,输出原因,并退出函数
else
{
printf("输入的用户信息不存在,请稍后尝试!\n");
return;
}
//3.count--,此时的用户信息删除了一个,用户信息减一
con->count--;
//4.提示一下删除成功
printf("输入的用户信息删除成功!\n");
}
test.c文件调用Delete函数。
//多次选择
do
{
menu();
printf("请选择:>");
(void)scanf("%d", &input);
switch (input)
{
case ADD:
Add(&con);//调用Add函数
break;
case DELETE:
Delete(&con);//调用删除函数
break;
case SEARCH:
break;
case MODIFY:
break;
case SHOW:
Show(&con);//调用Show函数
break;
case SORT:
break;
case EXIT:
printf("退出程序中...\n");
break;
default:
printf("请重新选择!\n");
}
} while (input);
按Ctrl + F5启动调试测试是否能够删除。
选择删除功能,删除李四的信息,然后调用Show函数.
6.Search函数
Search函数的作用是查找指定用户,使用已经实现的FindName函数,如果存在指定用户,将用户信息输出展示,如果指定用户不存在,输出提示信息。
contact.h文件实现Search函数的声明。
//Search函数的声明
void Search(Contact* con);
contact.c文件实现Search函数的定义。
void Search(Contact* con)
{
assert(con);//判断con不为NULL
//前面的Delete函数已经实现了查找函数
//此处可以调用该函数判断是否存在该用户信息
//如果存在,将该用户信息输入即可
//1.输入需要查找的对象
char tem[20] = { 0 };
printf("请输入需要查找的对象:>");
scanf("%s", tem);
//2.查找是否存在
int r = FindName(con, tem);
//存在输入打印
if (r != -1)
{
//找到了,提示一下
printf("输入的用户存在,正在加载中....\n");
//使用Show函数的格式输出
//打印标题行:-表示左对齐,\t是间隔8个水平制表位
printf("%-20s%-5s\t%-6s\t%-12s\t%s\n", "名字", "年龄", "性别", "联系方式", "地址");
//指定格式输出
printf("%-20s%-5d\t%-6s\t%-12s\t%s\n", con->data[r].name,
con->data[r].age, con->data[r].sex,
con->data[r].tele, con->data[r].addr);
}
//不存在,提示一下,并结束函数
else
{
printf("输入的用户不存在!\n");
return ;//结束函数
}
}
test.c实现函数的调用
//多次选择
do
{
menu();
printf("请选择:>");
(void)scanf("%d", &input);
switch (input)
{
case ADD:
Add(&con);//调用Add函数
break;
case DELETE:
Delete(&con);//调用删除函数
break;
case SEARCH:
Search(&con);//调用查找函数
break;
case MODIFY:
break;
case SHOW:
Show(&con);//调用Show函数
break;
case SORT:
break;
case EXIT:
printf("退出程序中...\n");
break;
default:
printf("请重新选择!\n");
}
} while (input);
按Ctrl + F5启动调试测试Search函数。
7.Modify函数
Modify函数是将指定的用户进行修改,使用FindName函数进行查找,如果用户存在,对用户信息重新录入修改,如果不存在,输出提示信息。
contatc.h文件实现Modify函数的声明.
//Modify函数的声明
void Modify(Contact* con);
contact.c文件实现函数的定义.
//Modify函数的定义
void Modify(Contact* con)
{
assert(con);//保证con不为NULL;
//1.查找到成员信息,还是使用查找函数
char tem[20] = { 0 };
printf("请输入需要修改的对象:>");
scanf("%s", tem);
//2.查找是否存在
int r = FindName(con, tem);
//如果存在,因为是修改,可以将原因信息重新录入即可
if (r != -1)
{
//直接使用Add函数的代码
printf("请输入名字:>");
scanf("%s", con->data[r].name);//字符串数组名标识地址不需要&
printf("请输入年龄:>");
scanf("%d", &(con->data[r].age));//age是变量需要&
printf("请输入性别:>");
scanf("%s", con->data[r].sex);
printf("请输入联系方式:>");
scanf("%s", con->data[r].tele);
printf("请输入地址:>");
scanf("%s", con->data[r].addr);
//修改成功后提示一下
printf("用户信息已经成功修改!\n");
}
//没有找到,提示一下
else
{
printf("输入的用户信息不存在!\n");
return;//结束程序
}
}
test.c文件实现函数的调用。
//多次选择
do
{
menu();
printf("请选择:>");
(void)scanf("%d", &input);
switch (input)
{
case ADD:
Add(&con);//调用Add函数
break;
case DELETE:
Delete(&con);//调用删除函数
break;
case SEARCH:
Search(&con);//调用查找函数
break;
case MODIFY:
Modify(&con);//调用修改函数
break;
case SHOW:
Show(&con);//调用Show函数
break;
case SORT:
break;
case EXIT:
printf("退出程序中...\n");
break;
default:
printf("请重新选择!\n");
}
} while (input);
按Ctrl + F5测试修改函数。
8.Sort函数
Sort函数是按照指定顺序排序,以下是按照名字排序,使用qsort函数完成具体排序,使用qsort函数需要实现一个函数指针,并包含指定头文件。
contact.h文件实现Sort函数的声明。
#include<stdlib.h>//qsort函数需要的头文件
//Sort函数的声明
void Sort(Contact* con);
contact.c文件实现函数的定义。
//Sort函数的定义
int cmp_by_name(const void* e1,const void* e2)
{
assert(e1 && e2);//e1和e2不为NULL
//比较名字,实现strcmp函数
//将比较结果自己返回
return strcmp(((PeoInf*)e1)->name,((PeoInf*)e2)->name);
}
void Sort(Contact* con)
{
assert(con);//判断con不为NULL
//假设按照名字排列,使用qsort函数实现排序
//qsort函数的头文件是#inlcude<stdlib.h>
//qsor函数需要实现一个函数指针
qsort(con->data, con->count, sizeof(PeoInf), cmp_by_name);
//提示一下排序成功
printf("用户信息已经成功排序!\n");
}
test.c文件实现函数的调用
//多次选择
do
{
menu();
printf("请选择:>");
(void)scanf("%d", &input);
switch (input)
{
case ADD:
Add(&con);//调用Add函数
break;
case DELETE:
Delete(&con);//调用删除函数
break;
case SEARCH:
Search(&con);//调用查找函数
break;
case MODIFY:
Modify(&con);//调用修改函数
break;
case SHOW:
Show(&con);//调用Show函数
break;
case SORT:
Sort(&con);//调用Sort函数
break;
case EXIT:
printf("退出程序中...\n");
break;
default:
printf("请重新选择!\n");
}
} while (input);
按Ctrl + F5测试Sort函数。
9.修改为动态版本
此时的通讯录最大只能存放MAX个用户的信息,可以有些用户不需要这么多的空间,可能导致空间的浪费,有些用户需要更大的空间,导致空间不足;因此需要将大小根据用户的需求进行调整,可以实现动态内存管理的函数即时调整空间大小。
contact文件修改Contact结构体的内容,增加一个capacity(容量)变量,并将data数组修改为结构体指针类型,节省空间大小,并重新定义初始容量大小,和扩容大小,并进行销毁函数的声明。
#define INT_EXPANSION 5//扩容大小
//动态版本
typedef struct Contact
{
int count;//记录当前存放的个数
int capacity;//记录当前容量
PeoInf* data;//结构体指针
}Contact;//类型重定义为Contact
//销毁通讯录
void DestroyContact(Contact* con);
contact.c文件需要修改初始化函数,Add函数,增加一个扩容函数和销毁函数。
//动态版本
void InitidContact(Contact* con)
{
assert(con);//保证con不为空指针
//初始化:使用memset函数
con->count = 0;//开始信息为0
con->capacity = INT_EXPANSION;//表示当前容量是5
PeoInf*pfData = (PeoInf*)calloc(INT_EXPANSION, sizeof(PeoInf));
if (pfData == NULL)//判断是否开辟空间失败
{
perror("InitidContact");//输出错误信息
return;//结束扩容
}
//赋值
con->data = pfData;//data可以指向pf指向的空间
}
//销毁通讯录
void DestroyContact(Contact* con)
{
assert(con);//判断con不为NULL
//回收空间,设置空指针
free(con->data);
con->data = NULL;
}
//扩容函数,static修饰只能在当前文件使用
static void Expansion(Contact* con)
{
//扩容
PeoInf* pfData = (PeoInf*)realloc(con->data,
(con->capacity + INT_EXPANSION) * sizeof(PeoInf));
//
if (pfData == NULL)
{
perror("Expansion::");//提示扩容失败的原因
return;//结束扩容函数
}
con->data = pfData;//data数组可以指向调整后开辟的空间。
con->capacity += INT_EXPANSION;//记录下当前的容量
//提示一下扩容成功
printf("扩容成功,当前容量为:%d\n", con->capacity);
}
void Add(Contact* con)
{
assert(con);
//考虑是否需要扩容
if (con->capacity == con->count)
Expansion(con);
//此时容量未满,可以增加
//找到当前数组的位置
printf("请输入名字:>");
scanf("%s", con->data[con->count].name);//字符串数组名标识地址不需要&
printf("请输入年龄:>");
scanf("%d", &(con->data[con->count].age));//age是变量需要&
printf("请输入性别:>");
scanf("%s", con->data[con->count].sex);
printf("请输入联系方式:>");
scanf("%s", con->data[con->count].tele);
printf("请输入地址:>");
scanf("%s", con->data[con->count].addr);
//此时增加成功,将count++
con->count++;
//提示一下,增加成功
printf("输入的信息已经保存\n");
}
test.c文件在退出程序时调用DestroyContact函数。
//多次选择
do
{
menu();
printf("请选择:>");
(void)scanf("%d", &input);
switch (input)
{
case ADD:
Add(&con);//调用Add函数
break;
case DELETE:
Delete(&con);//调用删除函数
break;
case SEARCH:
Search(&con);//调用查找函数
break;
case MODIFY:
Modify(&con);//调用修改函数
break;
case SHOW:
Show(&con);//调用Show函数
break;
case SORT:
Sort(&con);//调用Sort函数
break;
case EXIT:
DestroyContact(&con);//销毁通讯录
printf("退出程序中...\n");
break;
default:
printf("请重新选择!\n");
}
} while (input);
按Ctrl + F5启动调试,测试是否能使用动态的版本。
10.修改为文件版本
文件版本是为了改进动态版本程序退出后数据销毁的问题,可以在程序退出时将已经保存的数据存放于一个data.txt的文件中,当程序下一次运行时将data.txt文件的信息在初始化阶段加载到data数组中,
需要创建一个Save函数保存用户信息,创建一个LoadContact函数将信息加载到data数组中。
contact.h文件对Save函数和LoadContact函数进行声明。
//Save函数的声明
void Save(Contact* con);
//LoadContact函数的声明
void LoadContact(Contact* con);
contact.c文件需要对Save函数和LoadContact函数进行函数定义。
//LoadContact函数的定义
void LoadContact(Contact* con)
{
//以二进制读取方式打开文件
FILE* pfRead = fopen("data.txt", "rb");
if (pfRead == NULL)
{
perror("LoadContact");//输出错误信息
return;//结束函数
}
//使用
PeoInf tem = { 0 };//创建一个结构体变量
while (fread(&tem,sizeof(PeoInf),1,pfRead) == 1)
{
Expansion(con);//检测是否需要扩容
//因为文件的内容的大小可能比初始化的可以存储的空间多
//因此需要考虑扩容
con->data[con->count] = tem;//将读取到的信息赋给data数组的元素
con->count++;//用户信息+1
}
//关闭
fclose(pfRead);
pfRead = NULL;
}
//Save函数的定义
void Save(Contact* con)
{
assert(con);//判断不为NULL
//打开文件
FILE* pfWrite = fopen("data.txt", "wb");
//判断
if (pfWrite == NULL)
{
printf("Save");//输出错误信息
return;//结束函数
}
//使用for遍历数组
for (int i = 0; i < con->count; i++)
{
fwrite(con->data+i,sizeof(PeoInf),1,pfWrite);
}
//关闭
fclose(pfWrite);
pfWrite = NULL;
}
test.c文件在程序退出卡调用Save函数,保存信息。
//多次选择
do
{
menu();
printf("请选择:>");
(void)scanf("%d", &input);
switch (input)
{
case ADD:
Add(&con);//调用Add函数
break;
case DELETE:
Delete(&con);//调用删除函数
break;
case SEARCH:
Search(&con);//调用查找函数
break;
case MODIFY:
Modify(&con);//调用修改函数
break;
case SHOW:
Show(&con);//调用Show函数
break;
case SORT:
Sort(&con);//调用Sort函数
break;
case EXIT:
Save(&con);//保存信息
DestroyContact(&con);//销毁通讯录
printf("退出程序中...\n");
break;
default:
printf("请重新选择!\n");
}
} while (input);
按Ctrl + F5测试是否能使用Save函数。
打开data.txt文件观察是否存放信息,可以观察到已经将用户信息存放。
将程序关闭,重新按Ctrl + F5测试LoadContact函数是否能使用,程序运行后使用Show功能展示。
此时可以使用文件的版本保存和加载有效信息,但是此时的程序还是存在很多问题,效率还是比较低的,高效的使用应该使用数据库管理用户信息,这里就不再进行改进,有兴趣的学习数据库后进行版本的改进,推荐数据库的学习链接:数据库学习
11.程序代码展示
11.1.contact.h文件
//防止头文件多次包含
#pragma once
//防止函数报不安全警告
#pragma warning(disable:4996)
//头文件的包含
#include<stdio.h>//标准输入输出需要的头文件
#include<assert.h>//断言函数需要的头文件
#include<string.h>//memset函数和字符串函数需要的文件
#include<stdlib.h>//qsort函数和realloc函数需要的头文件
//#define定义的常量
//#define MAX 50 //假设最大容量是50个
#define MAX_NAME 20
#define MAX_SEX 6
#define MAX_TELE 12
#define MAX_ADDR 20
#define INT_EXPANSION 5//扩容大小
//创建一个存放人信息的结构体
typedef struct PeoInf
{
char name[MAX_NAME];//名字
int age;//年龄
char sex[6];
char tele[MAX_TELE];//联系方式
char addr[MAX_ADDR];//地址
}PeoInf;//类型重定义为PeoInf
//创建一个通讯录结构体
//静态版本
//typedef struct Contact
//{
// int count;//记录当前存放的个数
// PeoInf data[MAX];//结构体数组
//
//}Contact;//类型重定义为Contact
//动态版本
typedef struct Contact
{
int count;//记录当前存放的个数
int capacity;//记录当前容量
PeoInf* data;//结构体指针
}Contact;//类型重定义为Contact
//函数的声明
//初始化函数
void InitidContact(Contact* con);
//销毁通讯录
void DestroyContact(Contact* con);
//Add函数
void Add(Contact* con);
//Show函数
void Show(Contact* con);
//Delete函数
void Delete(Contact* con);
//Search函数的声明
void Search(Contact* con);
//Modify函数的声明
void Modify(Contact* con);
//Sort函数的声明
void Sort(Contact* con);
//Save函数的声明
void Save(Contact* con);
//LoadContact函数的声明
void LoadContact(Contact* con);
11.2.contact.c文件
在这里插入代码片
#include "contact.h"//包含自定义头文件
//初始化函数的定义
//静态版本
//void InitidContact(Contact* con)
//{
// assert(con);//保证con不为空指针
//
// //初始化:使用memset函数
// con->count = 0;
// memset(con->data,0, sizeof(con ->data));
//}
//扩容函数,static修饰只能在当前文件使用
static void Expansion(Contact* con)
{
//扩容
if (con->capacity == con->count)
{
PeoInf* pfData = (PeoInf*)realloc(con->data, (con->capacity + INT_EXPANSION) * sizeof(PeoInf));
//
if (pfData == NULL)
{
perror("Expansion::");//提示扩容失败的原因
return;//结束扩容函数
}
con->data = pfData;//data数组可以指向调整后开辟的空间。
con->capacity += INT_EXPANSION;//记录下当前的容量
//提示一下扩容成功
printf("扩容成功,当前容量为:%d\n", con->capacity);
}
}
//LoadContact函数的定义
void LoadContact(Contact* con)
{
//以二进制读取方式打开文件
FILE* pfRead = fopen("data.txt", "rb");
if (pfRead == NULL)
{
perror("LoadContact");//输出错误信息
return;//结束函数
}
//使用
PeoInf tem = { 0 };//创建一个结构体变量
while (fread(&tem,sizeof(PeoInf),1,pfRead) == 1)
{
Expansion(con);//检测是否需要扩容
//因为文件的内容的大小可能比初始化的可以存储的空间多
//因此需要考虑扩容
con->data[con->count] = tem;//将读取到的信息赋给data数组的元素
con->count++;//用户信息+1
}
//关闭
fclose(pfRead);
pfRead = NULL;
}
//动态版本
void InitidContact(Contact* con)
{
assert(con);//保证con不为空指针
//初始化:使用memset函数
con->count = 0;//开始信息为0
con->capacity = INT_EXPANSION;//表示当前容量是5
PeoInf*pfData = (PeoInf*)calloc(INT_EXPANSION, sizeof(PeoInf));
if (pfData == NULL)
{
perror("InitidContact");//输出错误信息
return;//结束扩容
}
//赋值
con->data = pfData;//data可以指向pf指向的空间
LoadContact(con);
}
//销毁通讯录
void DestroyContact(Contact* con)
{
assert(con);//判断con不为NULL
//回收空间,设置空指针
free(con->data);
con->data = NULL;
}
//Add函数的定义
// 静态版本
//void Add(Contact* con)
//{
// assert(con);
//
// //判断当前存放的个数是否满了
// if (con->count == MAX)
// {
// //此时不能存放,终止增加
// printf("当前容量已满,不可创建,退出中.......\n");
// return;
// }
// //此时容量未满,可以增加
// //找到当前数组的位置
// printf("请输入名字:>");
// scanf("%s", con->data[con->count].name);//字符串数组名标识地址不需要&
// printf("请输入年龄:>");
// scanf("%d", &(con->data[con->count].age));//age是变量需要&
// printf("请输入性别:>");
// scanf("%s", con->data[con->count].sex);
// printf("请输入联系方式:>");
// scanf("%s", con->data[con->count].tele);
// printf("请输入地址:>");
// scanf("%s", con->data[con->count].addr);
//
// //此时增加成功,将count++
// con->count++;
// //提示一下,增加成功
// printf("输入的信息已经保存\n");
//}
//动态版本
void Add(Contact* con)
{
assert(con);
//考虑是否需要扩容
if (con->capacity == con->count)
Expansion(con);
//此时容量未满,可以增加
//找到当前数组的位置
printf("请输入名字:>");
scanf("%s", con->data[con->count].name);//字符串数组名标识地址不需要&
printf("请输入年龄:>");
scanf("%d", &(con->data[con->count].age));//age是变量需要&
printf("请输入性别:>");
scanf("%s", con->data[con->count].sex);
printf("请输入联系方式:>");
scanf("%s", con->data[con->count].tele);
printf("请输入地址:>");
scanf("%s", con->data[con->count].addr);
//此时增加成功,将count++
con->count++;
//提示一下,增加成功
printf("输入的信息已经保存\n");
}
//Show函数的定义
void Show(Contact* con)
{
assert(con);//con不为空指针
//判断count是否为0
if (con->count == 0)
{
printf("没有信息,不可展示!\n");
return;//结束Show函数
}
//打印标题行:-表示左对齐,\t是间隔8个水平制表位
printf("%-20s%-5s\t%-6s\t%-12s\t%s\n","名字","年龄","性别","联系方式","地址");
//使用for循环遍历输出打印
int i = 0;//从下标为0的元素开始
for (i = 0; i < con->count; i++)
{
printf("%-20s%-5d\t%-6s\t%-12s\t%s\n", con->data[i].name,
con->data[i].age, con->data[i].sex,
con->data[i].tele, con->data[i].addr);
}
}
int FindName(Contact*con,const char* tem)
{
assert(tem);//此时con已经在Delet数组中调用,不需要判断con
//使用数组遍历查找
int i = 0;
for (i = 0; i < con->count; i++)
{
//判断名字是否相等,使用strcmp函数
if(strcmp(con->data[i].name,tem) == 0)
return i;//表示找到,返回下标
}
return -1;//遍历数组后还找不到返回-1
}
//Delete函数的定义
void Delete(Contact* con)
{
assert(con);//判断con不为空指针
char tem[20] = { 0 };
printf("请输入需要删除的对象:>");
scanf("%s", tem);
//1.需要找到删除的对象,需要实现一个查找函数;
int r = FindName(con, tem);
//2.判断是否找到
//如果找到了进行覆盖
if (r != -1)//找到了,通过后一个信息覆盖前一个信息完成删除
{ //使用for循环遍历数组
//从下标为r的数组元素开始覆盖
for (; r < con->count - 1; r++)//count - 1表示数组倒数第二个元素
{ //防止数组越界
memcpy(con->data + r, con->data + r + 1, sizeof(PeoInf));
//memcpy是内存块拷贝函数,第一个参数是下标为r的元素的起始地址
//第二个元素是r + 1下标元素的起始地址,第三个参数是拷贝元素的
//大小,刚刚好是一个人信息结构体大小。
}
}
//找不到,输出原因,并退出函数
else
{
printf("输入的用户信息不存在,请稍后尝试!\n");
return;
}
//3.count--,此时的用户信息删除了一个,用户信息减一
con->count--;
//4.提示一下删除成功
printf("输入的用户信息删除成功!\n");
}
void Search(Contact* con)
{
assert(con);//判断con不为NULL
//前面的Delete函数已经实现了查找函数
//此处可以调用该函数判断是否存在该用户信息
//如果存在,将该用户信息输入即可
//1.输入需要查找的对象
char tem[20] = { 0 };
printf("请输入需要查找的对象:>");
scanf("%s", tem);
//2.查找是否存在
int r = FindName(con, tem);
//存在输入打印
if (r != -1)
{
//找到了,提示一下
printf("输入的用户存在,正在加载中....\n");
//使用Show函数的格式输出
//打印标题行:-表示左对齐,\t是间隔8个水平制表位
printf("%-20s%-5s\t%-6s\t%-12s\t%s\n", "名字", "年龄", "性别", "联系方式", "地址");
//指定格式输出
printf("%-20s%-5d\t%-6s\t%-12s\t%s\n", con->data[r].name,
con->data[r].age, con->data[r].sex,
con->data[r].tele, con->data[r].addr);
}
//不存在,提示一下,并结束函数
else
{
printf("输入的用户不存在!\n");
return;//结束函数
}
}
//Modify函数的定义
void Modify(Contact* con)
{
assert(con);//保证con不为NULL;
//1.查找到成员信息,还是使用查找函数
char tem[20] = { 0 };
printf("请输入需要修改的对象:>");
scanf("%s", tem);
//2.查找是否存在
int r = FindName(con, tem);
//如果存在,因为是修改,可以将原因信息重新录入即可
if (r != -1)
{
//直接使用Add函数的代码
printf("请输入名字:>");
scanf("%s", con->data[r].name);//字符串数组名标识地址不需要&
printf("请输入年龄:>");
scanf("%d", &(con->data[r].age));//age是变量需要&
printf("请输入性别:>");
scanf("%s", con->data[r].sex);
printf("请输入联系方式:>");
scanf("%s", con->data[r].tele);
printf("请输入地址:>");
scanf("%s", con->data[r].addr);
//修改成功后提示一下
printf("用户信息已经成功修改!\n");
}
//没有找到,提示一下
else
{
printf("输入的用户信息不存在!\n");
return;//结束程序
}
}
//Sort函数的定义
int cmp_by_name(const void* e1,const void* e2)
{
assert(e1 && e2);//e1和e2不为NULL
//比较名字,实现strcmp函数
//将比较结果自己返回
return strcmp(((PeoInf*)e1)->name,((PeoInf*)e2)->name);
}
void Sort(Contact* con)
{
assert(con);//判断con不为NULL
//假设按照名字排列,使用qsort函数实现排序
//qsort函数的头文件是#inlcude<stdlib.h>
//qsor函数需要实现一个函数指针
qsort(con->data, con->count, sizeof(PeoInf), cmp_by_name);
//提示一下排序成功
printf("用户信息已经成功排序!\n");
}
//Save函数的定义
void Save(Contact* con)
{
assert(con);//判断不为NULL
//打开文件
FILE* pfWrite = fopen("data.txt", "wb");
//判断
if (pfWrite == NULL)
{
printf("Save");//输出错误信息
return;//结束函数
}
//使用for遍历数组
for (int i = 0; i < con->count; i++)
{
fwrite(con->data+i,sizeof(PeoInf),1,pfWrite);
}
//关闭
fclose(pfWrite);
pfWrite = NULL;
}
11.3.test.c文件
#include "contact.h"//包含自定义头文件
//菜单
void menu()
{
printf("************************************\n");
printf("******* 1.Add 2.Delete ****\n");
printf("******* 3.Search 4.Modify ****\n");
printf("******* 5.Show 6.Sort ****\n");
printf("******* 0.Exit ****\n");
printf("************************************\n");
}
//枚举常量
enum
{
EXIT,//0
ADD,//1
DELETE,//2
SEARCH,//3
MODIFY,//4
SHOW,//5
SORT//6
};
int main()
{
int input = 0;//功能选择
Contact con;//创建通讯录的变量
//初始化函数
InitidContact(&con);//指针传参
//多次选择
do
{
menu();
printf("请选择:>");
(void)scanf("%d", &input);
switch (input)
{
case ADD:
Add(&con);//调用Add函数
break;
case DELETE:
Delete(&con);//调用删除函数
break;
case SEARCH:
Search(&con);//调用查找函数
break;
case MODIFY:
Modify(&con);//调用修改函数
break;
case SHOW:
Show(&con);//调用Show函数
break;
case SORT:
Sort(&con);//调用Sort函数
break;
case EXIT:
Save(&con);//保存信息
DestroyContact(&con);//销毁通讯录
printf("退出程序中...\n");
break;
default:
printf("请重新选择!\n");
}
} while (input);
return 0;
}