上节说到,在掌握了指针、结构体。了解了枚举的知识之后,我们可以用C语言写一个通讯录
那其实在实际生活当中,固定大小的通讯录显然并不适用
所以,在掌握了动态开辟内存的函数之后,可以把通讯录改造为一个动态的。
使之能够实现:
1.刚开始只能保存3个联系人信息
2.当保存的联系人信息等于3个的时候,再增加可以保存2个联系人信息的空间
(这个默认和调整的大小是可以手动设置的)
3.每次增加的空间可以保存2个联系人信息
与静态开辟的比较就是这样
那相对于静态实现通讯录,代码也有所不同:
#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.h"
void menu()
{
printf("*****************************\n");
printf("*** 1.add 2.delete ****\n");
printf("*** 3.modify 4.find ****\n");
printf("*** 5.sort 6.print ****\n");
printf("*** 0.exit ****\n");
printf("*****************************\n");
}
void test()
{
int input = 0;
Contact TXL;
//初始化
InitContact(&TXL);
do
{
menu();
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case ADD:
AddLianxiren(&TXL);
break;
case DELETE:
DelLianxiren(&TXL);
break;
case MODIFY:
ModifyLianxiren(&TXL);
break;
case FIND:
FindLianxiren(&TXL);
break;
case SORT:
LianxirenSort(&TXL);
break;
case PRINT:
PrintXinXi(&TXL);
break;
case EXIT:
DestoryTXL(&TXL);
printf("退出成功!\n");
break;
default:
printf("输入有误,请重新输入!\n");
break;
}
} while (input);
}
int main()
{
test();
}
在test.c中,因为是动态内存开辟,所以在使用完成之后,需要将申请的空间给free掉
所以这里只有在销毁通讯录的时候多了一个销毁函数
再来看contact.h
#define _CRT_SECURE_NO_WARNINGS 1
//#define的放置
#define MAX_SIZE 10
#define MAX_NAME 20
#define MAX_SEX 10
#define MAX_TELE 12
#define MAX_ADDR 30
//动态
#define DEFAULT_SIZE 3
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<assert.h>
enum choose
{
EXIT,
ADD,
DELETE,
MODIFY,
FIND,
SORT,
PRINT
};
//创建出来结构体
//联系人信息:姓名,性别,年龄,电话,住址
typedef struct PeoInFo
{
char name[20];
char sex[10];
int age;
char tele[12];
char addr[30];
}PeoInFo;
//创建出通讯录
//typedef struct Contact
//{
// PeoInFo data[MAX_SIZE];
// int sz;
//}Contact;
//动态
typedef struct Contact
{
PeoInFo* data;
int sz;
int capacity;
}Contact;
//初始化通讯录
void InitContact(Contact*pc);
//增加
void AddLianxiren(Contact* pc);
//打印
void PrintXinXi(Contact* pc);
//删除
void DelLianxiren(Contact* pc);
//查
void FindLianxiren(Contact* pc);
//改
void ModifyLianxiren(Contact* pc);
//排序
void LianxirenSort(Contact*pc);
//销毁
void DestoryTXL(Contact* pc);
可以看到,我们在创建结构体的时候,结构体成员的设置发生了变化
1.第一个是PeoInFo类型的指针,指向我们申请的那片空间
2.第二个就是现在保存了多少个联系人信息
3.第三个就是当前申请的空间能够保存多少个联系人的信息。
最重要的是在contact.c中的实现:
我用几个截出来的代码进行讲解:
首先是初始化:
然后就是在判断空间用完,增加空间的地方:
在销毁这里,也有些不同:
然后下面就是contact.c的代码了。
#define _CRT_SECURE_NO_WARNINGS 1
//联系人信息:姓名,性别,年龄,电话,住址
#include"contact.h"
//初始化通讯录
//void InitContact(Contact* pc)
//{
// assert(pc);
// pc->sz = 0;
// memset(pc->data, 0, MAX_SIZE * sizeof(PeoInFo));
//}
//动态
void InitContact(Contact* pc)
{
assert(pc);
pc->sz = 0;
pc->data = (PeoInFo*)malloc(sizeof(PeoInFo) * DEFAULT_SIZE);
pc->capacity = DEFAULT_SIZE;
if (pc->data == NULL)
{
perror("InitContact:malloc");
return;
}
memset(pc->data, 0, sizeof(PeoInFo) * DEFAULT_SIZE);
}
//空间不足,动态增容
void check_capacity(Contact* pc)
{
PeoInFo* tmp = (PeoInFo*)realloc(pc->data,sizeof(PeoInFo) * (pc->capacity + 2));
if (tmp == NULL)
{
perror("check_capacity:realloc");
return;
}
pc->capacity += 2;
pc->data = tmp;
printf("增容成功!\n");
}
//增加联系人信息
void AddLianxiren(Contact* pc)
{
assert(pc);
/*if (pc->sz == MAX_SIZE)
{
printf("空间不足!\n");
return;
}*/
//动态
if (pc->sz == pc->capacity)
{
check_capacity(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].tele);
printf("请输入联系人住址:");
scanf("%s", pc->data[pc->sz].addr);
pc->sz++;
printf("添加成功!\n");
}
//问题1——忘记const
int find_by_name(const Contact* pc, char name[MAX_NAME])
{
int i = 0;
for (i = 0; i < pc->sz; i++)
{
if (strcmp(pc->data[i].name, name) == 0)
{
return i;
}
}
return -1;
}
//删除信息
void DelLianxiren(Contact* pc)
{
assert(pc);
if (pc->sz == 0)
{
printf("无可删除信息\n");
return;
}
char ret[MAX_NAME] = {0};
printf("请输入要删除联系人的名字:");
scanf("%s", ret);
int XiaBiao = find_by_name(pc,ret);
if (XiaBiao == -1)
{
printf("通讯录中没有该联系人的信息\n");
return;
}
int i = 0;
for(i=XiaBiao;i<pc->sz-1;i++)
{
pc->data[i] = pc->data[i + 1]; //注意这里有点懵
}
pc->sz--;
printf("删除成功\n");
}
//打印信息
void PrintXinXi(Contact* pc)
{
assert(pc);
if (pc->sz == 0)
{
printf("无联系人信息\n");
}
printf("%-20s%-10s%-5s%-12s%-30s\n", "姓名","性别", "年龄", "电话号码", "家庭住址");
int i = 0;
for (i = 0; i < pc->sz; i++)
{
printf("%-20s", pc->data[i].name);
printf("%-10s", pc->data[i].sex);
printf("%-5d", pc->data[i].age);
printf("%-12s", pc->data[i].tele);
printf("%-30s\n", pc->data[i].addr);
}
}
//查
void FindLianxiren(Contact* pc)
{
assert(pc);
if (pc->sz == 0)
{
printf("通讯录中无联系人信息!\n");
}
char ret[MAX_NAME] = { 0 };
printf("请输入要查找联系人的名字:");
scanf("%s", ret);
int XiaBiao = find_by_name(pc, ret);
if (XiaBiao == -1)
{
printf("没有该联系人的信息!\n");
return;
}
printf("%-20s%-10s%-5s%-12s%-30s\n", "姓名", "性别", "年龄", "电话号码", "家庭住址");
printf("%-20s", pc->data[XiaBiao].name);
printf("%-10s", pc->data[XiaBiao].sex);
printf("%-5d", pc->data[XiaBiao].age);
printf("%-12s", pc->data[XiaBiao].tele);
printf("%-30s\n", pc->data[XiaBiao].addr);
return;
}
//改
void ModifyLianxiren(Contact* pc)
{
assert(pc);
if (pc->sz == 0)
{
printf("没有联系人信息可修改!\n");
return;
}
char name[MAX_NAME] = { 0 };
printf("请输入要修改联系人的名字:");
scanf("%s", name);
int ret = find_by_name(pc, name);
if (ret == -1)
{
printf("没有您要修改的联系人信息!\n");
}
printf("请输入联系人姓名:");
scanf("%s", pc->data[ret].name);
printf("请输入联系人性别:");
scanf("%s", pc->data[ret].sex);
printf("请输入联系人年龄:");
scanf("%d", &(pc->data[ret].age));
printf("请输入联系人电话:");
scanf("%s", pc->data[ret].tele);
printf("请输入联系人住址:");
scanf("%s", pc->data[ret].addr);
printf("修改成功!\n");
printf("%-20s%-10s%-5s%-12s%-30s\n", "姓名", "性别", "年龄", "电话号码", "家庭住址");
printf("%-20s", pc->data[ret].name);
printf("%-10s", pc->data[ret].sex);
printf("%-5d", pc->data[ret].age);
printf("%-12s", pc->data[ret].tele);
printf("%-30s\n", pc->data[ret].addr);
}
int cmp_by_name(const void* e1, const void* e2)
{
return strcmp(((PeoInFo*)e1)->name, ((PeoInFo*)e2)->name);
}
int cmp_by_sex(const void* e1, const void* e2)
{
return strcmp(((PeoInFo*)e1)->sex, ((PeoInFo*)e2)->sex);
}
int cmp_by_age(const void* e1, const void* e2)
{
return ((PeoInFo*)e1)->age - ((PeoInFo*)e2)->age;
}
int cmp_by_tele(const void* e1, const void* e2)
{
return strcmp(((PeoInFo*)e1)->tele, ((PeoInFo*)e2)->tele);
}
int cmp_by_addr(const void* e1, const void* e2)
{
return strcmp(((PeoInFo*)e1)->addr, ((PeoInFo*)e2)->addr);
}
//排序
void LianxirenSort(Contact* pc)
{
assert(pc);
int n = 0;
printf("选择您想要的排列方式:\n");
printf("*********************************\n");
printf("*** 1.姓名 2.性别 ****\n");
printf("*** 3.年龄 4.电话号码 ****\n");
printf("*** 5.家庭住址 ****\n");
printf("*********************************\n");
scanf("%d", &n);
while(1)
{
switch (n)
{
case 1:
qsort(pc->data, pc->sz, sizeof(PeoInFo), cmp_by_name);
PrintXinXi(pc);
break;
case 2:
qsort(pc->data, pc->sz, sizeof(PeoInFo), cmp_by_sex);
PrintXinXi(pc);
break;
case 3:
qsort(pc->data, pc->sz, sizeof(PeoInFo), cmp_by_age);
PrintXinXi(pc);
break;
case 4:
qsort(pc->data, pc->sz, sizeof(PeoInFo), cmp_by_tele);
PrintXinXi(pc);
break;
case 5:
qsort(pc->data, pc->sz, sizeof(PeoInFo), cmp_by_addr);
PrintXinXi(pc);
break;
default:
printf("输入错误请重新输入!\n");
}
}
}
//销毁
void DestoryTXL(Contact* pc)
{
free(pc->data);
pc->data = NULL;
printf("销毁成功!\n");
}
总结:
总的来说,这个我理解起来还是有难度的,
1.最主要的不同就是在静态实现通讯录的时候,我们创建的结构体中就包含了整个通讯录的空间
但是在动态实现通讯录的时候,我们实际上只是创建了一个仅有3个变量的结构体
最主要的是靠着那个指针来实现的各种操作
2.然后就是我经常会忘记使用assert来断言指针,还有const也经常忘记使用
在自己实现了动态申请空间的通讯录之后,真正的感受到了动态申请,动态管理是怎么一回事了。
作者水平欠佳,文章恐有疏漏,还望读者指正。