目录
前言
改进后的优势
一、test.c
二、contact.h
三、contact.c
1.加载通讯录函数load_contact
2、将数据保存到文件save_contact函数
3、初始化通讯录init_contact函数
4、扩容check_full函数
5、销毁通讯录destory_contact函数
6、其余函数
四、完整源码
总结
通过我们对结构体等内容的学习,我们已经可以制作一个初级的通讯录,所以在我们学会动态内存管理和文件的处理之后,我们来升级我们的通讯录吧,我们将通讯录分为三个文件来编写,通过test.c以及contact.h和contact.c来编写程序。
由于在改进前我们的通讯录只能定义一个足够大的数组进行存放数据,所以我们必然会对空间浪费,而且也有可能存在空间不足的情况,所以我们采用动态开辟内存,首先初始化,然后在判断通讯录已满之后进行扩容操作。
当我们进行通讯录联系人的添加之后,我们退出程序,我们的数据也随之丢失了,所以我们可以通过文件将数据保存,当我们下一次运行程序的时候,还会加载我们之前保存的联系人信息,真正做到了通讯录的作用。
test.c源文件是我们主函数所在的文件,实现我们对程序的测试功能。
void menu()
{
printf("*******1.add 2.del*******\n");
printf("*******3.find 4.mod*******\n");
printf("*******5.show 6.sortname**\n");
printf("*******7.sortage 0.exit******\n");
printf("*****************************\n");
}
void test()
{
struct contact con;
init_contact(&con);
int input = 0;
do
{
menu();
printf("请输入>\n");
scanf("%d", &input);
switch(input)
{
case add:
add_contact(&con);
break;
case del:
del_contact(&con);
break;
case find:
find_contact(&con);
break;
case mod:
mod_contact(&con);
break;
case show:
show_contact(&con);
break;
case sortname:
sort_by_name_contact(&con);
break;
case sortage:
sort_by_age_contact(&con);
break;
case quit:
//保存数据到文件中
save_contact(&con);
destory_contact(&con);
printf("退出\n");
break;
deafult:
printf("输入错误\n");
break;
}
} while (input);
}
int main()
{
test();
return 0;
}
和我们之前的三子棋游戏类似,我们在主函数中定义一个test函数进行程序的测试工作,然后定义一个简单的menu菜单输出提供给用户的选项。
在test函数内部主要是一个do…while循环,在循环内部通过Switch语句是控制多分支的选择,在这里大家可能会有点疑问,case语句后边不是应该跟一个常数吗,怎么这个后边看起来是一个变量呢,其实在这里我们定义了一个option的枚举类型,此时他们其实就代表0到7的数字,此时我们直接使用名称代替数字,提高了程序的可读性。
enum Option {
quit,
add,
del,
find,
mod,
show,
sortname,
sortage,
};
在我们整个程序的主体离不开结构体,在这里我们实现通讯录,我们要定义一个contact类型的结构体,内部的成员变量为通讯录现有的人数,通讯录最大容量,以及通讯录的人员信息。
typedef struct Stu
{
char name[20];
int age;
char sex[4];
char tel[12];
char addr[15];
}Stu;
struct contact
{
Stu* s;//存放学生的信息
int sz;//存放通讯录中已经添加学生的个数
int capacity;//存放通讯录的最大容量
};
contact.h是我们的头文件,实现的是函数的声明以及结构体,枚举类型的声明。
#pragma once
#include
#include
#include
#include
#define MAX 100
#define Init_cap 3
#define NUM 2
enum Option {
quit,
add,
del,
find,
mod,
show,
sortname,
sortage,
};
typedef struct Stu
{
char name[20];
int age;
char sex[4];
char tel[12];
char addr[15];
}Stu;
//动态版本
struct contact
{
Stu* s;//存放学生的信息
int sz;//存放通讯录中已经添加学生的个数
int capacity;//存放通讯录的最大容量
};
//初始化通讯录
void init_contact(struct contact* pc);
//添加联系人
void add_contact(struct contact* pc);
//打印通讯录
void show_contact(const struct contact* pc);
//删除联系人
void del_contact(struct contact* pc);
//查找联系人
void find_contact(struct contact* pc);
//修改通讯录
void mod_contact(struct contact* pc);
//姓名排序通讯录
void sort_by_name_contact(struct contact* pc);
//年龄排序通讯录
void sort_by_age_contact(struct contact* pc);
//保存通讯录到文件
void save_contact(struct contact* pc);
//加载文件的通讯录
void load_contact(struct contact* pc);
在声明contact结构体的时候,我们不在直接定义一个数组来存放数据,而是使用一个指针,然后再使这个指针指向我们后来动态开辟的空间,实现空间合理利用。
//从文件中加载通讯录
void load_contact(struct contact* pc)
{
//断言
assert(pc);
//打开文件使用二进制读取的方式,并使用pf来接收地址
FILE* pf = fopen("contact.txt", "rb");
//判断是否读取成功
if (pf == NULL)
{
perror("load_contact");
}
else
{
int i = 0;
struct Stu tmp = { 0 };
//while循环,由于fread返回值是读取成功的个数,当读取不到时跳出循环
while (fread(&tmp, sizeof(struct Stu), 1, pf))
{
//扩容函数,当空间不足时自动扩容
check_full(pc);
pc->s[i] = tmp;
pc->sz++;
i++;
}
//关闭文件
fclose(pf);
pf = NULL;
}
}
//保存数据到文件
void save_contact(struct contact* pc)
{
assert(pc);
//打开文件以写入的方式,并且返回地址交给pf
FILE* pf = fopen("contact.txt","wb");
//判断
if (pf == NULL)
{
perror("save_contact");
}
else
{
int i = 0;
for(i=0;isz ;i++)
{
//二进制输出数据
fwrite(pc->s+i, sizeof(struct Stu), 1, pf);
}
printf("保存成功\n");
//关闭文件
fclose(pf);
pf=NULL;
}
}
//初始化通讯录
void init_contact(struct contact* pc)
{
//断言,判断是否为空指针
assert(pc);
pc->sz = 0;
//动态开辟空间
Stu* ptr = (Stu*)calloc(3, sizeof(Stu));
//判断是否开辟到空间
if (ptr == NULL)
{
perror("init_contact::calloc");
}
//如果开辟成功,交给pc->s来维护
pc->s = ptr;
pc->capacity = Init_cap;
load_contact(pc);
}
与之前的不同的时,我们在初始化时调用了load_contact 函数将文件中的初始数据赋予程序,来实现数据保存的功能。
//判断是否已满,如果已满,就进行扩容
void check_full(struct contact* pc)
{
//当已存数据等于容量大小时扩容
if (pc->sz == pc->capacity)
{
//使用realloc函数进行扩容,每次增加NUM个数据
Stu* ptr = (Stu*)realloc(pc->s, sizeof(Stu) * (NUM +pc->capacity ));
if (ptr == NULL)
{
perror("check_full::realloc");
return;
}
pc->s = ptr;
pc->capacity += NUM;
printf("扩容成功\n");
}
}
由于我们在堆区动态开辟了内存,所以我们应该主动去释放内存,以免造成内存泄漏。
//在使用完之后,进行销毁
void destory_contact(struct contact* pc)
{
assert(pc);
free(pc->s);
pc->s = NULL;
pc->capacity = 0;
pc->sz = 0;
pc = NULL;
}
其他函数并无变化,实现增删改查排序展示等功能。
#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.h"
//初始化通讯录
void init_contact(struct contact* pc)
{
//断言,判断是否为空指针
assert(pc);
pc->sz = 0;
//动态开辟空间
Stu* ptr = (Stu*)calloc(3, sizeof(Stu));
//判断是否开辟到空间
if (ptr == NULL)
{
perror("init_contact::calloc");
}
//如果开辟成功,交给pc->s来维护
pc->s = ptr;
pc->capacity = Init_cap;
load_contact(pc);
}
//在使用完之后,进行销毁
void destory_contact(struct contact* pc)
{
assert(pc);
free(pc->s);
pc->s = NULL;
pc->capacity = 0;
pc->sz = 0;
pc = NULL;
}
//判断是否已满,如果已满,就进行扩容
void check_full(struct contact* pc)
{
//当已存数据等于容量大小时扩容
if (pc->sz == pc->capacity)
{
//使用realloc函数进行扩容,每次增加NUM个数据
Stu* ptr = (Stu*)realloc(pc->s, sizeof(Stu) * (NUM +pc->capacity ));
if (ptr == NULL)
{
perror("check_full::realloc");
return;
}
pc->s = ptr;
pc->capacity += NUM;
printf("扩容成功\n");
}
}
//保存数据到文件
void save_contact(struct contact* pc)
{
assert(pc);
//打开文件以写入的方式,并且返回地址交给pf
FILE* pf = fopen("contact.txt","wb");
//判断
if (pf == NULL)
{
perror("save_contact");
}
else
{
int i = 0;
for(i=0;isz ;i++)
{
//二进制输出数据
fwrite(pc->s+i, sizeof(struct Stu), 1, pf);
}
printf("保存成功\n");
//关闭文件
fclose(pf);
pf=NULL;
}
}
//从文件中加载通讯录
void load_contact(struct contact* pc)
{
//断言
assert(pc);
//打开文件使用二进制读取的方式,并使用pf来接收地址
FILE* pf = fopen("contact.txt", "rb");
//判断是否读取成功
if (pf == NULL)
{
perror("load_contact");
}
else
{
int i = 0;
struct Stu tmp = { 0 };
//while循环,由于fread返回值是读取成功的个数,当读取不到时跳出循环
while (fread(&tmp, sizeof(struct Stu), 1, pf))
{
//扩容函数,当空间不足时自动扩容
check_full(pc);
pc->s[i] = tmp;
pc->sz++;
i++;
}
//关闭文件
fclose(pf);
pf = NULL;
}
}
//添加联系人
void add_contact(struct contact* pc)
{
assert(pc);
check_full(pc);
printf("请输入姓名|>");
scanf("%s", pc->s[pc->sz].name);
printf("请输入年龄|>");
scanf("%d", &(pc->s[pc->sz].age));
printf("请输入性别|>");
scanf("%s", pc->s[pc->sz].sex);
printf("请输入电话|>");
scanf("%s", pc->s[pc->sz].tel);
printf("请输入地址|>");
scanf("%s", pc->s[pc->sz].addr);
pc->sz++;
printf("添加联系人结束\n");
}
//打印通讯录
void show_contact(const struct contact* pc)
{
int i = 0;
printf("%-15s\t%-4s\t%-4s\t%-12s\t%-10s\n", "姓名", "年龄", "性别", "电话", "地址");
for (i = 0; i < pc->sz; i++)
{
printf("%-15s\t%-4d\t%-4s\t%-12s\t%-10s\n",pc->s[i].name,
pc->s [i].age ,
pc->s [i].sex ,
pc->s [i].tel ,
pc->s [i].addr);
}
}
//通过姓名查找联系人
int find_by_name(struct contact* pc,char* name) {
int i = 0;
for (i = 0; i < pc->sz; i++)
{
if (strcmp(name,pc->s[i].name )==0)
{
return i;
}
}
return -1;
}
//删除联系人
void del_contact(struct contact* pc)
{
int i = 0;
assert(pc);
char del_name[20] = {0};
if (pc->sz == 0)
{
printf("通讯录中无联系人\n");
}
else
{
printf("请输入联系人姓名|>\n");
scanf("%s", del_name);
int pos = find_by_name(pc,del_name);
for (i = pos; i < pc->sz - 1; i++)
{
pc->s[i] = pc->s[i + 1];
}
}
pc->sz--;
printf("删除联系人结束\n");
}
//
void find_contact(struct contact* pc)
{
assert(pc);
char name[20] = {0};
printf("请输入查找的姓名\n");
scanf("%s", name);
int ret = find_by_name(pc, name);
if (ret == -1)
{
printf("没找到\n");
}
else
{
printf("%-15s\t%-4s\t%-4s\t%-12s\t%-10s\n", "姓名", "年龄", "性别", "电话", "地址");
printf("%-15s\t%-4d\t%-4s\t%-12s\t%-10s\n", pc->s[ret].name,
pc->s[ret].age,
pc->s[ret].sex,
pc->s[ret].tel,
pc->s[ret].addr);
}
}
void mod_contact(struct contact* pc)
{
assert(pc);
int i = 0;
char name[20] = { 0 };
if (pc->sz == 0)
{
printf("通讯录中无联系人\n");
}
else
{
printf("请输入修改的姓名\n");
scanf("%s", name);
int ret = find_by_name(pc, name);
printf("请输入修改后姓名|>");
scanf("%s", pc->s[ret].name);
printf("请输入修改后年龄|>");
scanf("%d", &(pc->s[ret].age));
printf("请输入修改后性别|>");
scanf("%s", pc->s[ret].sex);
printf("请输入修改后电话|>");
scanf("%s", pc->s[ret].tel);
printf("请输入修改后地址|>");
scanf("%s", pc->s[ret].addr);
printf("%-15s\t%-4s\t%-4s\t%-12s\t%-10s\n", "姓名", "年龄", "性别", "电话", "地址");
printf("%-15s\t%-4d\t%-4s\t%-12s\t%-10s\n",
pc->s[ret].name,
pc->s[ret].age,
pc->s[ret].sex,
pc->s[ret].tel,
pc->s[ret].addr);
}
printf("修改结束\n");
}
void sort_by_name_contact(struct contact* pc)
{
assert(pc);
int i = 0;
for (i = 0; i < pc->sz-1; i++)
{
int j = 0;
for (j = 0; j < pc->sz - 1 - i; j++) {
if (memcmp(pc->s[j].name, pc->s[j + 1].name, 20) > 0)
{
Stu tmp = pc->s[j];
pc->s[j] = pc->s[j + 1];
pc->s[j+1] = tmp;
}
}
}
}
int cmp_int(void* e1,void* e2)
{
return *(int*)e2 - *(int*)e1;
}
void sort_by_age_contact(struct contact* pc)
{
assert(pc);
qsort(pc->s, pc->sz, sizeof(Stu), cmp_int);
printf("排序结束\n");
}
contact.h
#pragma once
#include
#include
#include
#include
#define MAX 100
#define Init_cap 3
#define NUM 2
enum Option {
quit,
add,
del,
find,
mod,
show,
sortname,
sortage,
};
typedef struct Stu
{
char name[20];
int age;
char sex[4];
char tel[12];
char addr[15];
}Stu;
//动态版本
struct contact
{
Stu* s;//存放学生的信息
int sz;//存放通讯录中已经添加学生的个数
int capacity;//存放通讯录的最大容量
};
//初始化通讯录
void init_contact(struct contact* pc);
//添加联系人
void add_contact(struct contact* pc);
//打印通讯录
void show_contact(const struct contact* pc);
//删除联系人
void del_contact(struct contact* pc);
//查找联系人
void find_contact(struct contact* pc);
//修改通讯录
void mod_contact(struct contact* pc);
//姓名排序通讯录
void sort_by_name_contact(struct contact* pc);
//年龄排序通讯录
void sort_by_age_contact(struct contact* pc);
//保存通讯录到文件
void save_contact(struct contact* pc);
//加载文件的通讯录
void load_contact(struct contact* pc);
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.h"
void menu()
{
printf("*******1.add 2.del*******\n");
printf("*******3.find 4.mod*******\n");
printf("*******5.show 6.sortname**\n");
printf("*******7.sortage 0.exit******\n");
printf("*****************************\n");
}
void test()
{
struct contact con;
init_contact(&con);
int input = 0;
do
{
menu();
printf("请输入>\n");
scanf("%d", &input);
switch(input)
{
case add:
add_contact(&con);
break;
case del:
del_contact(&con);
break;
case find:
find_contact(&con);
break;
case mod:
mod_contact(&con);
break;
case show:
show_contact(&con);
break;
case sortname:
sort_by_name_contact(&con);
break;
case sortage:
sort_by_age_contact(&con);
break;
case quit:
//保存数据到文件中
save_contact(&con);
destory_contact(&con);
printf("退出\n");
break;
deafult:
printf("输入错误\n");
break;
}
} while (input);
}
int main()
{
test();
return 0;
}
contact.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.h"
//初始化通讯录
void init_contact(struct contact* pc)
{
//断言,判断是否为空指针
assert(pc);
pc->sz = 0;
//动态开辟空间
Stu* ptr = (Stu*)calloc(3, sizeof(Stu));
//判断是否开辟到空间
if (ptr == NULL)
{
perror("init_contact::calloc");
}
//如果开辟成功,交给pc->s来维护
pc->s = ptr;
pc->capacity = Init_cap;
load_contact(pc);
}
//在使用完之后,进行销毁
void destory_contact(struct contact* pc)
{
assert(pc);
free(pc->s);
pc->s = NULL;
pc->capacity = 0;
pc->sz = 0;
pc = NULL;
}
//判断是否已满,如果已满,就进行扩容
void check_full(struct contact* pc)
{
//当已存数据等于容量大小时扩容
if (pc->sz == pc->capacity)
{
//使用realloc函数进行扩容,每次增加NUM个数据
Stu* ptr = (Stu*)realloc(pc->s, sizeof(Stu) * (NUM +pc->capacity ));
if (ptr == NULL)
{
perror("check_full::realloc");
return;
}
pc->s = ptr;
pc->capacity += NUM;
printf("扩容成功\n");
}
}
//保存数据到文件
void save_contact(struct contact* pc)
{
assert(pc);
//打开文件以写入的方式,并且返回地址交给pf
FILE* pf = fopen("contact.txt","wb");
//判断
if (pf == NULL)
{
perror("save_contact");
}
else
{
int i = 0;
for(i=0;isz ;i++)
{
//二进制输出数据
fwrite(pc->s+i, sizeof(struct Stu), 1, pf);
}
printf("保存成功\n");
//关闭文件
fclose(pf);
pf=NULL;
}
}
//从文件中加载通讯录
void load_contact(struct contact* pc)
{
//断言
assert(pc);
//打开文件使用二进制读取的方式,并使用pf来接收地址
FILE* pf = fopen("contact.txt", "rb");
//判断是否读取成功
if (pf == NULL)
{
perror("load_contact");
}
else
{
int i = 0;
struct Stu tmp = { 0 };
//while循环,由于fread返回值是读取成功的个数,当读取不到时跳出循环
while (fread(&tmp, sizeof(struct Stu), 1, pf))
{
//扩容函数,当空间不足时自动扩容
check_full(pc);
pc->s[i] = tmp;
pc->sz++;
i++;
}
//关闭文件
fclose(pf);
pf = NULL;
}
}
//添加联系人
void add_contact(struct contact* pc)
{
assert(pc);
check_full(pc);
printf("请输入姓名|>");
scanf("%s", pc->s[pc->sz].name);
printf("请输入年龄|>");
scanf("%d", &(pc->s[pc->sz].age));
printf("请输入性别|>");
scanf("%s", pc->s[pc->sz].sex);
printf("请输入电话|>");
scanf("%s", pc->s[pc->sz].tel);
printf("请输入地址|>");
scanf("%s", pc->s[pc->sz].addr);
pc->sz++;
printf("添加联系人结束\n");
}
//打印通讯录
void show_contact(const struct contact* pc)
{
int i = 0;
printf("%-15s\t%-4s\t%-4s\t%-12s\t%-10s\n", "姓名", "年龄", "性别", "电话", "地址");
for (i = 0; i < pc->sz; i++)
{
printf("%-15s\t%-4d\t%-4s\t%-12s\t%-10s\n",pc->s[i].name,
pc->s [i].age ,
pc->s [i].sex ,
pc->s [i].tel ,
pc->s [i].addr);
}
}
//通过姓名查找联系人
int find_by_name(struct contact* pc,char* name) {
int i = 0;
for (i = 0; i < pc->sz; i++)
{
if (strcmp(name,pc->s[i].name )==0)
{
return i;
}
}
return -1;
}
//删除联系人
void del_contact(struct contact* pc)
{
int i = 0;
assert(pc);
char del_name[20] = {0};
if (pc->sz == 0)
{
printf("通讯录中无联系人\n");
}
else
{
printf("请输入联系人姓名|>\n");
scanf("%s", del_name);
int pos = find_by_name(pc,del_name);
for (i = pos; i < pc->sz - 1; i++)
{
pc->s[i] = pc->s[i + 1];
}
}
pc->sz--;
printf("删除联系人结束\n");
}
//
void find_contact(struct contact* pc)
{
assert(pc);
char name[20] = {0};
printf("请输入查找的姓名\n");
scanf("%s", name);
int ret = find_by_name(pc, name);
if (ret == -1)
{
printf("没找到\n");
}
else
{
printf("%-15s\t%-4s\t%-4s\t%-12s\t%-10s\n", "姓名", "年龄", "性别", "电话", "地址");
printf("%-15s\t%-4d\t%-4s\t%-12s\t%-10s\n", pc->s[ret].name,
pc->s[ret].age,
pc->s[ret].sex,
pc->s[ret].tel,
pc->s[ret].addr);
}
}
void mod_contact(struct contact* pc)
{
assert(pc);
int i = 0;
char name[20] = { 0 };
if (pc->sz == 0)
{
printf("通讯录中无联系人\n");
}
else
{
printf("请输入修改的姓名\n");
scanf("%s", name);
int ret = find_by_name(pc, name);
printf("请输入修改后姓名|>");
scanf("%s", pc->s[ret].name);
printf("请输入修改后年龄|>");
scanf("%d", &(pc->s[ret].age));
printf("请输入修改后性别|>");
scanf("%s", pc->s[ret].sex);
printf("请输入修改后电话|>");
scanf("%s", pc->s[ret].tel);
printf("请输入修改后地址|>");
scanf("%s", pc->s[ret].addr);
printf("%-15s\t%-4s\t%-4s\t%-12s\t%-10s\n", "姓名", "年龄", "性别", "电话", "地址");
printf("%-15s\t%-4d\t%-4s\t%-12s\t%-10s\n",
pc->s[ret].name,
pc->s[ret].age,
pc->s[ret].sex,
pc->s[ret].tel,
pc->s[ret].addr);
}
printf("修改结束\n");
}
void sort_by_name_contact(struct contact* pc)
{
assert(pc);
int i = 0;
for (i = 0; i < pc->sz-1; i++)
{
int j = 0;
for (j = 0; j < pc->sz - 1 - i; j++) {
if (memcmp(pc->s[j].name, pc->s[j + 1].name, 20) > 0)
{
Stu tmp = pc->s[j];
pc->s[j] = pc->s[j + 1];
pc->s[j+1] = tmp;
}
}
}
}
int cmp_int(void* e1,void* e2)
{
return *(int*)e2 - *(int*)e1;
}
void sort_by_age_contact(struct contact* pc)
{
assert(pc);
qsort(pc->s, pc->sz, sizeof(Stu), cmp_int);
printf("排序结束\n");
}
我们通过使用动态开辟和文件处理的知识对我们的通讯录进行了更好的优化,希望可以帮到大家。