CSDN话题挑战赛第2期
参赛话题:学习笔记
目录
前言
这篇文章对如何写出一个通讯录进行了详细的讲解,希望能对刚开始学习、想要写一个通讯录的同学提供一些帮助。同时博主还利用枚举和动态内存对通讯录进行了一定的优化,能让你的通讯录更加的强大。
一.旧版大致思路
-
我们都用过手机上的通讯录,手机的通讯录支持我们存储联系人的姓名、电话、住址等信息,而且通讯录中有不同的人,每个人对应不同的个人信息,如何才能保存一个人的各种信息呢?
-
这时我们想到可以利用结构体,因为结构体中可以保存不同的信息,年龄可以保存在其中的int类型变量中,名字和地址可以保存在里面的字符串变量中。同时通讯录中肯定不只含有一个用户,这时我们需要用另一个通讯录来包含上面的通讯录数组变量(后面详解),这样就把存储空间搞定了
-
通讯录要实现很多功能,本文的通讯录能够实现增删查改以及排序的功能,怎样实现这些功能呢?这时就需要我们通过各种函数进行逐一实现了
-
我们希望这个通讯录能根据我们的需求实现不同的功能,如何实现呢?我们能够联想到switch函数,他不是正好可以根据我们不同的输入来进行不同的操作么?如此看来理论上貌似是可行的,让我们开始实际的操作吧。
二.旧版通讯录
1.框架构建
1.1存储空间
在思路中给我们有提到用结构体来储存一个人的各种信息,这里我们设计了一个peoinfo结构体来储存名字、性别、电话、年龄以及地址。
struct peoinfo
{
char name[10];
char sex[5];
char tele[20];
int age;
char addr[20];
};//别忘了分号
通讯录里当然不只含有一名用户,我们需要定义一个struct peoinfo类型的数组并且把他包含到另一个结构体contact中,contact里面还需要变量sz来记录通讯录里面有多少个用户。
struct contact
{
struct peoinfo data[100];//此时的通讯录只能存储100个用户
int sz;//通讯录中的人数
};
这样我们就能把所有用户存储到通讯录中了。
1.2菜单
通讯录能够按照我们的输入来进行相应的功能,不过对于使用的用户,我们希望通讯录能够在使用者开始使用时弹出一个菜单,菜单用来介绍通讯录的功能以及对应的命令。
void menu()
{
printf("*********************************\n");
printf(" 1.add 2.delete \n");
printf(" 3.search 4.show \n");
printf(" 5.change 6.sort \n");
printf(" 0.exit \n");//exit表示退出通讯录
printf("*********************************\n");
}
运行后是这样的效果
1.3功能实现框架
存储空间有了,菜单有了,现在我们要想办法让我们的命令和我们写的函数连接起来,switch语句能实现这个功能。do-while语句能实现通讯录的连续使用
int main()
{
int input = 0;
struct contact con;//struct contact类型的变量
init_con(&con);//因为我们需要修改通讯录的内容因此需要传址,下面的实参同理
do
{
printf("你的选择是?\n");
menu();
scanf("%d", &input);
switch (input)
{
case 1://数字对应菜单中的数字
add_con(&con);//传址才能改变con的内容
break;
case 2:
del_con(&con);
break;
case 3:
search_con(&con);
break;
case 4:
show_con(&con);
break;
case 5:
change_con(&con);
break;
case 6:
sort_con(&con);
break;
case 0:
break;
default:
printf("请重新输入\n");
}
} while (input);//分号别忘了
return 0;
}
这样程序就能根据我们不同的输入来进行不同的操作了。
2.实现功能函数
2.0初始化
void init_con(struct contact* con)
{
memset(con->data, 0, 100 * sizeof(con->data[0]));
con->sz = 0;
}
机翻,想进一步了解的读者可以在CSDN查一下。
通过memset我们就可以初始化通讯录了。
2.1添加用户
void add_con(struct contact* con)
{
printf("姓名\n");
scanf("%s", con->data[con->sz].name);
printf("性别\n");
scanf("%s", con->data[con->sz].sex);
printf("电话\n");
scanf("%s", con->data[con->sz].tele);
printf("年龄\n");
scanf("%d", &(con->data[con->sz].age));
printf("地址\n");
scanf("%s", con->data[con->sz].addr);
con->sz++;
}
记得每次添加完使sz++,记录有多少用户能方便我们后面函数的书写。
2.2删除用户
在写删除函数的时候我们需要思考一个问题,删除一个用户的前提是找到一个用户,而且找到用户的功能在删除、查找、修改这三个函数中都需要使用,因此我们先书写一个简单的查找函数(通过名字查找)。
static int find(char* name, struct contact* con)
{
int i = 0;
for (i = 0; i < con->sz; i++)
{
if (strcmp(con->data[i].name, name) == 0)
{
return i;
}
}
return -1;
}
当函数返回值是 i(数组中第i+1位用户),证明存在这个用户,反之不存在。这样我们就能通过返回值找到这个用户在第几位。
strcmp用来比较两字符串的大小。
现在我们可以写删除函数了
void del_con(struct contact* con)
{
char* name[10];
printf("你想要删除的名字是\n");
scanf("%s", name);
int i = find(name, con);
if (i != -1)
{
for (; i <= con->sz - 1; i++)
{
con->data[i] = con->data[i + 1];
}
printf("删除成功\n");
con->sz--;
}
else
{
printf("用户不存在\n");
}
}
这里我们采用被删除用户后一位替换被删除位并以此类推做法,也可以使用memcpy。
2.3查找用户
既然我们已经写了一个查找的函数,那么这一步就变得很简单了,我们只需要完善这个函数的功能即查找后输出查找用户的所有信息就行了。
void search_con(struct contact* con)
{
char* name[10];
printf("你想要查找的名字是\n");
scanf("%s", name);
int i = find(name, con);
if (i != -1)
{
printf("%-10s\t", "姓名");//负号能实现左对齐
printf("%-5s\t", "性别");//数字加上\t可以实现数据的对齐
printf("%-20s\t", "电话");
printf("%-10s\t", "年龄");
printf("%-20s\t", "地址");
printf("\n");
printf("%-10s\t", con->data[i].name);
printf("%-5s\t", con->data[i].sex);
printf("%-20s\t", con->data[i].tele);
printf("%-10d\t", con->data[i].age);
printf("%-20s\t", con->data[i].addr);
printf("\n");
}
}
2.4展示所有用户
如果我们想要看到目前通讯录中的所有用户呢?
void show_con(struct contact* con)
{
int i = 0;
printf("%-10s\t", "姓名");
printf("%-5s\t", "性别");
printf("%-20s\t", "电话");
printf("%-10s\t", "年龄");
printf("%-20s\t", "地址");
printf("\n");
for (i = 0; i < con->sz; i++)
{
printf("%-10s\t", con->data[i].name);
printf("%-5s\t", con->data[i].sex);
printf("%-20s\t", con->data[i].tele);
printf("%-10d\t", con->data[i].age);
printf("%-20s\t", con->data[i].addr);
printf("\n");
}
}
-
负号能实现左对齐
-
\t和数字能够实现同样的数据对齐
看一下大概效果:
2.5修改用户信息
此函数同样需要查找函数
void change_con(struct contact* con)
{
char* name[10];
printf("你要修改的用户是\n");
scanf("%s", name);
int i = find(name, con);
printf("姓名\n");
scanf("%s", con->data[i].name);
printf("性别\n");
scanf("%s", con->data[i].sex);
printf("电话\n");
scanf("%s", con->data[i].tele);
printf("年龄\n");
scanf("%d", &(con->data[i].age));
printf("地址\n");
scanf("%s", con->data[i].addr);
}
需要注意的是,此函数只能实现将要修改的用户信息重新输入,无法精准修改某一项信息,如果想实现可以再设计一个菜单。
2.6用户信息排序
这里我们采用的是用名字排序
int my_cmp(void* e1, void* e2)
{
return (strcmp(((struct peoinfo*)e1)->name, ((struct peoinfo*)e2)->name));
}
void sort_con(struct contact* con)
{
qsort(con->data, con->sz, sizeof(con->data[0]), my_cmp);
show_con(con);
}
qsort实现快速排序,是一个非常好用的库函数。
三.一级优化
我们采用枚举来对结构体进行优化,不知道各位在写功能框架(switch语句那里)时会不会感觉有些麻烦因为写到case某个数字的时候我们都需要翻到我们的菜单,查看对应的功能是什么,需要写那个函数。
枚举可以解决这个问题,我们先定义一个枚举
enum num
{
Exit,
add,
Delete,
search,
show,
change,
sort
};
我们知道在枚举中,成员其实是一个个的常量,且在没有赋值的情况下,第一个成员默认赋值为0,后面以此类推,这样我们就可以用枚举成员替换case后面的数字。
do
{
printf("你的选择是?\n");
menu();
scanf("%d", &input);
switch (input)
{
case add:
add_con(&con);
break;
case Delete:
del_con(&con);
break;
case search:
search_con(&con);
break;
case show:
show_con(&con);
break;
case change:
change_con(&con);
break;
case sort:
sort_con(&con);
break;
case Exit:
break;
default:
printf("请重新输入\n");
}
} while (input);
一级优化算是对程序的书写提供了一定的便利
四.二级优化
1.意义
在旧版通讯录中我们不难发现,因为我们定义了
struct peoinfo data[100];
因此我们的通讯录只能存储100个用户的信息,如果我们需要存储1000个用户此时这些空间就不够用了,如果我们只需要存储两个用户,那么剩下的98个空间就被浪费了,有没有什么方法能解决这个问题呢?这时就需要我们的动态内存分配了。
使用动态内存分配我们能够实现用多少开辟多少空间,提高我们通讯录的性能
2.存储
先回顾一下之前的存储方法
使用动态内存后,我们希望新的空间一开始能存储三个用户的信息,当存储满了就会自动生成新的空间
3.代码实现
3.1存储
struct peoinfo
{
char name[10];
char sex[5];
char tele[20];
int age;
char addr[20];
};
struct contact
{
struct peoinfo* data;
int sz;//通讯录中的人数
int check;//达到三个就重置
};
3.2初始化
动态内存的初始化跟旧版的不太一样,而且我们需要先开辟初始的三个空间
void init_con(struct contact* con)
{
con->sz = 0;
con->check = 0;
con->data = (struct peoinfo*)calloc(3, sizeof(struct peoinfo));
}
3.3判断
动态通讯录的精髓在于能够自动开辟空间,每次空间满了,check都会达到3,这时相应函数就会再开辟三个空间同时给check重新赋值为0。我们需要在add函数中添加这个判断。。
void add_con(struct contact* con)
{
printf("姓名\n");
scanf("%s", con->data[con->sz].name);
printf("性别\n");
scanf("%s", con->data[con->sz].sex);
printf("电话\n");
scanf("%s", con->data[con->sz].tele);
printf("年龄\n");
scanf("%d", &(con->data[con->sz].age));
printf("地址\n");
scanf("%s", con->data[con->sz].addr);
con->sz++;
con->check++;
if (con->check == 3)
{
con->data = (struct peoinfo*)realloc(con->data, (con->sz + 3) * sizeof(struct peoinfo));
con->check = 0;
}
}
3.4释放
最后一定要记得释放,否则可能导致内存泄漏。
free(con.data);
因为我们其实只是将数组换成了指针,因此不影响其它函数的使用。
五.最后
这篇文章博主进行了通讯录的二级优化,但这个通讯录依然有很多优化的空间,比如运行结束存储的数据就会消失,这就需要运用文件管理的知识,因此后面可能会进一步优化,敬请期待
博主码文不易,如果觉得这篇文章对你有帮助的话,可以点赞、收藏、关注。你的支持是我创作的最大动力!