动态通讯录(C语言版本)
如果不懂得同学可以回顾一下这个博客,里面的知识点讲的很详细,特别适合新手入门。
动态通讯录更改部分(在静态版本上进行更改)
结构体的变动:
typedef struct PeoInfo
{
char name[20];
char sex[5];
char tele[12];
int age;
char addr[30];
}PeoInfo;
typedef struct Contact
{
struct PeoInfo* data;
int sz;
int capacity;
}Contact;
因为静态版本只能给出固定的容量,超出容量就不可以扩容,这就很不人性,如果给的空间太大就过于浪费,但是给的太少又不符合实际应用,因为随机性很多。 VS不支持给数组一个变量的下标比如 :arr[n],像这样使用只能在C99平台下运行,不过大多数OJ平台下都可以运行(面试或者考试可以放心)。
解释:
我们只给了一个struct PeoInfo* data 这是一个结构体指针的形式,之后可以用malloc进行扩容。
初始化通讯录
#define DEFAULT 3
void InitContact(Contact* pc)
{
assert(pc);
pc->data = (PeoInfo*)malloc(DEFAULT*sizeof(PeoInfo));
if (pc->data == NULL)
{
perror("InitContact");
return;
}
pc->capacity = DEFAULT;
pc->sz = 0;
}
解释:
我们直接在pc->data指针所指的位置上开辟空间,记住不是在pc->data上开辟空间,因为pc->data只是一个结构体指针,开辟的大小是个人信息结构体大小的三倍(default=3),以后要开辟空间一定要检验是否开辟成功,没有成功要perror,给出标记,如果开辟成功就可以给容量赋值了,并且给sz初始化为0.
增加联系人信息
int CheckCapacity(Contact* pc)
{
if (pc->sz == pc->capacity)
{
printf("已经满了\n");
PeoInfo* ptr = (PeoInfo*)realloc(pc->data, 2 * pc->capacity * sizeof(PeoInfo));
if (ptr != NULL)
{
pc->data = ptr;
pc->capacity *= 2;
printf("增容成功\n");
return 1;
}
else
{
perror("ADDContact");
return 0;
}
}
else
return 1;
}
void AddContact(Contact* pc)
{
assert(pc);
if(CheckCapacity(pc))
{
printf("请输入名字;》\n");
scanf("%s", pc->data[pc->sz].name);
printf("请输入性别:》\n");
scanf("%s", pc->data[pc->sz].sex);
printf("请输入年龄:》\n");
scanf("%d", &(pc->data[pc->sz].age));
printf("请输入电话:》\n");
scanf("%s", pc->data[pc->sz].tele);
printf("请输入地址:》\n");
scanf("%s", pc->data[pc->sz].addr);
pc->sz++;
printf("成功增加联系人\n");
}
else
{
return;
}
}
解释:
静态增加信息因为容量有限,容量满了之后无法增加所以只需要在函数最早加一个判断sz是否等于MAX,如果不等于便可以增加,等于的话就会提示。 动态版本增加信息的函数前面也要有一个检验容量的信息如果不满可以进行增加,满了呢当然也不会退出,会直接进行扩容。因为后面也会用到检查容量的函数所以我直接将其封装成一个小函数,随用随取很方便。其他的就和静态一样增加个人信息即可,但是再也不会因为容量满了而退出程序了。
销毁整个通讯录
void DestroyContact(Contact* pc)
{
free(pc->data);
pc->data = NULL;
pc->capacity = 0;
pc->sz = 0;
}
解释:
因为是顺序表,而且动态开辟了空间,使用完之后一定要释放,不然会把内存空间浪费掉使电脑性能下降的。 在这里也要声明pc->data开辟的空间被free之后,也一定要pc->data = NULL,因为会有野指针的风险,甚至你下次调用pc->data打印信息还能有一定几率打印出之前存的消息这是因为栈帧的原因,千万不要有侥幸心理,使用完开辟分空间之后一定要free和NULL。
修改好的主函数
#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.h"
void menu()
{
printf("***************************\n");
printf(" 1.add 2.del \n");
printf(" 3.search 4.modify \n");
printf(" 5.show 6.sort \n");
printf(" 0.exit \n");
printf("***************************\n");
}
enum Option
{
EXIT,
ADD,
DEL,
SEARCH,
MODIFY,
SHOW,
SORT
};
int main()
{
int input = 0;
Contact con;
InitContact(&con);
do
{
menu();
printf("请选择>:");
scanf("%d", &input);
switch (input)
{
case ADD:
AddContact(&con);
break;
case DEL:
DelContact(&con);
break;
case SEARCH:
SearchContact(&con);
break;
case MODIFY:
ModifyContact(&con);
break;
case SHOW:
ShowContact(&con);
break;
case SORT:
SortContact(&con);
break;
case EXIT:
DestroyContact(&con);
break;
default:
break;
}
} while (input);
return 0;
}
修改点:
在这里最重要的是在退出选项的下面增加了一个DestroyContact(&con);函数,这是因为使用完动态分配的空间一定要释放。 还增加了枚举,这是因为你给出菜单选项是,别人阅读你的代码一是难免会忘记所以总要翻看查询会有很多不便,但是这里增加了枚举可以直接通过枚举来调用。
枚举的解释:
枚举如果你没有给里面的量赋值的话,他会主动给你赋值,从0,1,2…一直往下,当然你也可以直接赋值,我在这里没有赋值所以是按照 菜单的复制情况给他们拍的顺序(EXIT放在了最开始的位置也就是0)。
预告
之后还会有文件的版本敬请期待!也希望同学们要复习好知识哦。
|