通讯录大家都很熟悉了,一个联系人包括姓名,年龄,性别,电话,地址等;
那我们想一想我们所学的数据类型(int,float,double等都是单一的相同类型 (属于内置类型))都没能很好的解决这些不同的成员 这时我们可以试试结构体(自定义类型) 结构体中可以有多个不同类型的成员 所以很符合我们的需求 那我们就定义一个联系人的个人信息
#define ADDER_MAX 20
#define NAME_MAX 20
#define TELE_MAX 15
typedef struct Presonal
{
int age;
char adder[ADDER_MAX];
char name[NAME_MAX];
char tele[TELE_MAX];
}contact;
简单了解下 typedef 这个关键词 这是对类型进行定义的 将后期可变的类型进行重新定义 以方便我们之后的操作 这个和上面的宏定义是一样的效果 都是简化操作 提高可读性 之后也会在程序中用到 先了解了解
我们通过用宏去定义数组的大小(方便我们之后对信息大小的改动) 学过数组的都知道 下标引用操作符"[ ]"里面是不能用变量的 必须是常量 亦不排除 有些版本的软件会有变长数组的概念 就是可以用变量去作为下标 做出动态内存开辟的效果
上面的代码只是将结构体类型声明了出来 并没有实际的价值(还没用定义)
什么才叫定义呢?就是开辟空间 下面就是对结构体变量进行定义 两种方式 上面使用了typedef对结构体类型做了简化
struct Presonal con;
contact con;
这些信息需要去存储 我们可以用数组去存储这些联系人的信息
我们之前写的数组是(int 等类型的数据类型) 那我们也可以用结构体类型进行定义数组 这样数组中每个单元就是一个结构体类型 这是我们就可以记录这些联系人的个人信息 同时我们也要知道已经存储了多少个联系人 这时就可以用一个变量去记录
如果将他们分开写的话 对于阅读你代码的人肯定可读性会受影响 那么我们也就可以采取上边个人信息的创建方法 将结构体数组 和 联系人数量放到同一块空间中 (放结构体中)
这里有两个版本:1,静态版本
#define MAX 100
typedef struct Contact
{
Presonal arr[MAX];
int sz;
}Contact;
2,动态版本
typedef struct Contact
{
Presonal* arr;
int sz;
}Contact;
里面的第一个成员变量就是后面要开辟的 动态数组
其实两个版本的实现就只是在数组方面 的实现 仅此而已 熟练之后 动态版本轻而易举
先看看静态版本的实现吧~
Contact.h
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define ADDER_MAX 20
#define NAME_MAX 20
#define TELE_MAX 15
typedef struct Presonal
{
int age;
char adder[ADDER_MAX];
char name[NAME_MAX];
char tele[TELE_MAX];
}Presonal;
#define MAX 100
typedef struct Contact
{
Presonal arr[MAX];
int sz;
}Contact;
//初始化
void Inti(Contact* pc);
//增加
void Add(Contact* pc);
//删除
void Del(Contact* pc);
//查找
void Search(Contact* pc);
//修改
void Modify(Contact* pc);
//展示
void Show(Contact* pc);
Contact.c
void Inti(Contact* pc)
{
pc->sz = 0;
memset(pc->arr, 0, sizeof(pc->arr));//是将结构体数组地址的每一个字节初始化为0
}
void Add(Contact* pc)
{
//判断是否空间已满
if (pc->sz == MAX)
{
printf("通讯簿已满 无法增加");
return;//结束该函数
}
printf("请输入联系人的姓名 年龄 电话 地址(每个信息用空格隔开):>");
scanf("%s %d %s %s", pc->arr[pc->sz].name, &(pc->arr[pc->sz].age), pc->arr[pc->sz].tele, pc->arr[pc->sz].adder);
//将联系人信息数量+1
pc->sz++;
printf("添加成功");
}
void Del(Contact* pc)
{
//判断电话薄是否为空 为空了就不应该删除
if (pc->sz == 0)
{
printf("电话薄为空 无法删除数据");
return;
}
//只需将pc->sz--
pc->sz--;
}
//void Search(Contact* pc)
//{
// //判断电话薄是否为空 为空了就找不了
// if (pc->sz == 0)
// {
// printf("电话薄为空 无法删除数据");
// return;
// }
// printf("请输入你要查找的联系人姓名>");
// char name[NAME_MAX] = "";
// scanf("%s", name);
// while (strcmp(name, pc->arr[pc->sz].name) != 0)
// {
// printf("该联系人信息为>\n");
// printf("%-20s\t%-4s\t%-5s\t%-12s\t%-20s\n", pc->arr[pc->sz].name, pc->arr[pc->sz].age, pc->arr[pc->sz].tele, pc->arr[pc->sz].adder);
// }
//}
int ByNameSearch(Contact* pc)
{
printf("请输入你要查找的联系人姓名>");
char name[NAME_MAX] = "";
scanf("%s", name);
int pos = 0;
while (strcmp(name, pc->arr[pos++].name) != 0)
{
if (pc->sz == pos)
{
printf("该联系人不存在");
return -1;
}
}
return pos - 1;//多加了一次
}
void Search(Contact* pc)
{
//判断电话薄是否为空 为空了就找不了
if (pc->sz == 0)
{
printf("电话薄为空 无法删除数据");
return;
}
int pos = ByNameSearch(pc);
if (pos == -1)
{
return;
}
printf("该联系人信息为>\n");
printf("%-20s\t%-4d\t%-5s\t%-12s\n", pc->arr[pos].name, pc->arr[pos].age,
pc->arr[pos].tele, pc->arr[pos].adder);
}
void Modify(Contact* pc)
{
//判断电话薄是否为空 为空了就找不了
if (pc->sz == 0)
{
printf("电话薄为空 无法删除数据");
return;
}
int pos = ByNameSearch(pc);
if (pos == -1)
return;
printf("输入你要修改的联系人信息 姓名 年龄 电话 地址(并用空格隔开)");
scanf("%s %d %s %s", &pc->arr[pos].name, &pc->arr[pos].age, &pc->arr[pos].tele, &pc->arr[pos].adder);
printf("修改成功\n");
}
void Show(Contact* pc)
{
printf("%-20s\t%-4s\t%-5s\t%-12s\n", "名字", "年龄", "电话", "地址");
for (int pos = 0; pos < pc->sz; pos++)
{
printf("%-20s\t%-4d\t%-5s\t%-12s\n", pc->arr[pos].name, pc->arr[pos].age,
pc->arr[pos].tele, pc->arr[pos].adder);
}
}
test.c
void menu()
{
printf("***************************************\n");
printf("******1.增 2.删 ****\n");
printf("******3.查 4.改 *****\n");
printf("******0.退出 5.展示 *****\n");
printf("***************************************\n");
}
enum Con
{
EXIT,
ADD,
DEL,
SEARCH,
MODIFY,
SHOW
};
int main()
{
int input = 0;
Contact con;
menu();
Inti(&con);
do
{
printf("请输入你的选择:");
scanf("%d", &input);
switch (input)
{
case ADD:
Add(&con);
break;
case DEL:
Del(&con);
break;
case SEARCH:
Search(&con);
break;
case MODIFY:
Modify(&con);
break;
case SHOW:
Show(&con);
break;
default:
printf("输入错误 请重新输入");
break;
}
} while (input);
return 0;
}
test.c 项目解释 里面运用了 枚举 switch case语句 等
枚举可以增强我们代码的可读性
运行结果展示
之前是静态存储的通讯簿 静态的通讯簿有很多缺点
内存问题:无法有效的确定通讯簿的大小 (开多了浪费少了不够用)最明显
我们学了动态内存管理之后就可以使通讯簿自己添加申请内存 大大方便了我们存储联系人
当然我们刚开始会向堆去申请4个字节的空间 (不大不小)如果不够我们再将容量扩大到原来的二倍 这就使我们能够有效的处理内存问题 也不是至于开多或开少
这个二倍关系是权衡之后采取的 你也可以按照自己的想法去开辟
void Inti(Contact* pc)
{
pc->sz = pc->capacity = 0;
pc->arr = NULL;
}
void Add(Contact* pc)
{
//判断是否空间已满
if (pc->sz == pc->capacity)
{
int newcapacity = (pc->capacity == 0) ? 4 : pc->capacity * 2;
Presonal* tmp = (Presonal*)malloc(sizeof(Presonal) * newcapacity);
if (tmp == NULL)
{
printf("failed to allocate memory.");
exit(-1);
}
pc->arr = tmp;
pc->capacity = newcapacity;
}
printf("请输入联系人的姓名 年龄 电话 地址(每个信息用空格隔开):>");
scanf("%s %d %s %s", pc->arr[pc->sz].name, &(pc->arr[pc->sz].age), pc->arr[pc->sz].tele, pc->arr[pc->sz].adder);
//将联系人信息数量+1
pc->sz++;
printf("添加成功");
}
只有添加时内存会受内存影响 所以我只列出了 内存改动部分 前面给出了 动态内存对应的结构体
但是我们这个通讯簿还有问题 既然是通讯簿 就是记录你的联系人的 如果丢失就会对我们造成影响
之前不管是静态内存管理 还是动态内存管理 都不会让我们的程序在结束之后记录和保存 我们之前存储的联系人信息 因为这些联系人是被存储在内存中的 而程序结束之后 内存会被操作系统回收
我们学了文件管理之后 就能让这些联系人信息存储在文件之中 就可以永久保存 这样在下一次存储时 只需打开之前的文件 进行相应的操作就可以了
当然保存文件应该在通讯薄销毁之前 不然信息都销毁了还拿什么保存
文件保存代码 关于文件操作的函数还有很多 不再过多解释
void Save(Contact* pc)
{
FILE* pf = fopen("contact.dat", "wb");
if (pf == NULL)
{
perror("SaveContact::fopen");
return;
}
//写数据
int i = 0;
for (i = 0; i < pc->sz; i++)
{
fwrite(pc->arr + i, sizeof(Presonal), 1, pf);
}
//关闭文件
fclose(pf);
pf = NULL;
printf("保存成功...\n");
}
同时 在下一次录入通讯簿时 将之前的信息内容加载进去
static Buy(Contact* pc)
{
if (pc->sz == pc->capacity)
{
int newcapacity = (pc->capacity == 0) ? 4 : pc->capacity * 2;
Presonal* tmp = (Presonal*)malloc(sizeof(Presonal) * newcapacity);
if (tmp == NULL)
{
printf("failed to allocate memory.");
exit(-1);
}
pc->arr = tmp;
pc->capacity = newcapacity;
}
}
void Load(Contact* pc)
{
//打开文件
FILE* pf = fopen("contact.dat", "rb");
if (pf == NULL)
{
perror("LoadContact::fopen");
return;
}
//读文件
Presonal tmp = { 0 };
while (fread(&tmp, sizeof(Presonal), 1, pf))
{
Buy(pc);
pc->arr[pc->sz++] = tmp;
}
//关闭文件
fclose(pf);
pf = NULL;
}
利用 二进制读(fread 返回值size_t ) 一次读取一个(返回的是有效的读取个数)
将他放进一个临时变量中 因为要从 pf文件中放入一个临时变量里再将变量放到通讯簿中 别忘了通讯簿里面的信息个数要++
读取存入肯定涉及空间的扩容 所以再存入之前对空间进行检查Buy
文件中存的就是二进制的信息内容
上面的三个文件是没涉及文件相关的代码 下面是涉及文件的相关代码
contact.c
static Buy(Contact* pc)
{
if (pc->sz == pc->capacity)
{
int newcapacity = (pc->capacity == 0) ? 4 : pc->capacity * 2;
Presonal* tmp = (Presonal*)malloc(sizeof(Presonal) * newcapacity);
if (tmp == NULL)
{
printf("failed to allocate memory.");
exit(-1);
}
pc->arr = tmp;
pc->capacity = newcapacity;
}
}
void Load(Contact* pc)
{
//打开文件
FILE* pf = fopen("contact.dat", "rb");
if (pf == NULL)
{
perror("LoadContact::fopen");
return;
}
//读文件
Presonal tmp = { 0 };
while (fread(&tmp, sizeof(Presonal), 1, pf))
{
Buy(pc);
pc->arr[pc->sz++] = tmp;
}
//关闭文件
fclose(pf);
pf = NULL;
}
void Inti(Contact* pc)
{
pc->sz = pc->capacity = 0;
pc->arr = NULL;
Load(pc);
}
void Save(Contact* pc)
{
FILE* pf = fopen("contact.dat", "wb");
if (pf == NULL)
{
perror("SaveContact::fopen");
return;
}
//写数据
int i = 0;
for (i = 0; i < pc->sz; i++)
{
fwrite(pc->arr + i, sizeof(Presonal), 1, pf);
}
//关闭文件
fclose(pf);
pf = NULL;
printf("保存成功...\n");
}
void DestroyConact(Contact* pc)
{
free(pc->arr);
pc->arr = NULL;
pc->capacity = 0;
pc->sz = 0;
printf("释放内存.....\n");
}
void Add(Contact* pc)
{
Buy(pc);
printf("请输入联系人的姓名 年龄 电话 地址(每个信息用空格隔开):>");
scanf("%s %d %s %s", pc->arr[pc->sz].name, &(pc->arr[pc->sz].age), pc->arr[pc->sz].tele, pc->arr[pc->sz].adder);
//将联系人信息数量+1
pc->sz++;
printf("添加成功");
}
void Del(Contact* pc)
{
//判断电话薄是否为空 为空了就不应该删除
if (pc->sz == 0)
{
printf("电话薄为空 无法删除数据");
return;
}
//只需将pc->sz--
pc->sz--;
}
int ByNameSearch(Contact* pc)
{
printf("请输入你要查找的联系人姓名>");
char name[NAME_MAX] = "";
scanf("%s", name);
int pos = 0;
while (strcmp(name, pc->arr[pos++].name) != 0)
{
if (pc->sz == pos)
{
printf("该联系人不存在");
return -1;
}
}
return pos - 1;//多加了一次
}
void Search(Contact* pc)
{
//判断电话薄是否为空 为空了就找不了
if (pc->sz == 0)
{
printf("电话薄为空 无法删除数据");
return;
}
int pos = ByNameSearch(pc);
if (pos == -1)
{
return;
}
printf("该联系人信息为>\n");
printf("%-20s\t%-4d\t%-5s\t%-12s\n", pc->arr[pos].name, pc->arr[pos].age,
pc->arr[pos].tele, pc->arr[pos].adder);
}
void Modify(Contact* pc)
{
//判断电话薄是否为空 为空了就找不了
if (pc->sz == 0)
{
printf("电话薄为空 无法删除数据");
return;
}
int pos = ByNameSearch(pc);
if (pos == -1)
return;
printf("输入你要修改的联系人信息 姓名 年龄 电话 地址(并用空格隔开)");
scanf("%s %d %s %s", &pc->arr[pos].name, &pc->arr[pos].age, &pc->arr[pos].tele, &pc->arr[pos].adder);
printf("修改成功\n");
}
void Show(Contact* pc)
{
printf("%-20s\t%-4s\t%-5s\t%-12s\n", "名字", "年龄", "电话", "地址");
for (int pos = 0; pos < pc->sz; pos++)
{
printf("%-20s\t%-4d\t%-5s\t%-12s\n", pc->arr[pos].name, pc->arr[pos].age,
pc->arr[pos].tele, pc->arr[pos].adder);
}
}
contact.h
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#define ADDER_MAX 20
#define NAME_MAX 20
#define TELE_MAX 15
typedef struct Presonal
{
int age;
char adder[ADDER_MAX];
char name[NAME_MAX];
char tele[TELE_MAX];
}Presonal;
#define MAX 100
//typedef struct Contact
//{
// Presonal arr[MAX];
// int sz;
//}Contact;
typedef struct Contact
{
Presonal* arr;
int sz;
int capacity;
}Contact;
//初始化
void Inti(Contact* pc);
//增加
void Add(Contact* pc);
//删除
void Del(Contact* pc);
//查找
void Search(Contact* pc);
//修改
void Modify(Contact* pc);
//展示
void Show(Contact* pc);
//保存文件
void Save(Contact* pc);
//内存释放
void DestroyConact(Contact* pc);
test.c
#include"Contact.h"
void menu()
{
printf("***************************************\n");
printf("******1.增 2.删 ****\n");
printf("******3.查 4.改 *****\n");
printf("******0.退出 5.展示 *****\n");
printf("***************************************\n");
}
enum Con
{
EXIT,
ADD,
DEL,
SEARCH,
MODIFY,
SHOW
};
int main()
{
int input = 0;
Contact con;
menu();
Inti(&con);
do
{
printf("请输入你的选择:");
scanf("%d", &input);
switch (input)
{
case ADD:
Add(&con);
break;
case DEL:
Del(&con);
break;
case SEARCH:
Search(&con);
break;
case MODIFY:
Modify(&con);
break;
case SHOW:
Show(&con);
break;
case EXIT:
Save(&con);
DestroyConact(&con);
break;
default:
printf("输入错误 请重新输入");
break;
}
} while (input);
return 0;
}
以上就是通讯簿的相关实现