✨前言✨:
大家在学习完自定义类型结构体后,就可以尝试自己写一些小项目了,例如:通讯录--图书管理系统---学生信息管理系统等项目,它们的内在逻辑都是相同的,这不仅可以提升大家的coding能力,还可以提升大家对于结构体的理解。
本博客以通讯录的实现为例子
目录
✨前言✨:
💎预期功能💎
💎具体功能的实现💎
一:如何存放联系人的信息
二:如何创建通讯录
三:如何添加联系人
四:如何打印出当前通讯录
五:如何删除指定联系人的信息
💎总代码的展示💎
💎预期功能💎
在做项目时,无论是大项目还是小项目,都要做到先理清:要实现项目的预期功能是什么----按照这些预期功能把项目进行拆解成一个个的功能----把每一个特定功能分装成一个函数。这其实就是模块化编程思想,在做项目时尤其重要。
🔑:预期通讯录的功能如下
1:能存放联系人的信息(包括:姓名 年龄 性别 电话号码 家庭住址 等信息)
2:支持在这个通讯录内部添加💎功能的具体实现💎联系人的信息
3:支持删除这个通讯录中指定的联系人的信息
4:支持打印出整个通讯录,显示出整个通讯录的信息
5:支持查找指定联系人,并打印出该联系人的信息
6:支持修改这个通讯录中指定联系人的信息
💡:由上述预期通讯录的功能可以将整个项目按功能拆成一个个模块,再把一个个模块分装成一个个函数。
💎具体功能的实现💎
一:如何存放联系人的信息
💡:由于要描述每一个联系人,我们需要记录下每一位联系人的姓名,年龄,性别,电话号码,家庭住址。所以我们知道想要描述一个联系人需要多种不同的数据类型,因此我们考虑用一个结构体来记录联系人的信息。
🔑:具体代码的实现:
struct PeoInfo
{
char name[NAME_MAX];//记录联系人的性别
int age;//联系人的年龄
char sex[SEX_MAX];//联系人的性别
char tele[TELE_MAX];//联系人的电话号码
char addr[ADDR_MAX];//联系人的家庭地址
};
二:如何创建通讯录
💡:首先我们要明确通讯录中包含什么?
1:通讯录中要有一块空间来储存联系人的信息,而联系人的信息我们明确了是结构体类型。
2:通讯录中要有一个变量记录当前通讯录中联系人的个数
3:通讯录中要有一个变量来记录当前通讯录的总容量
🔑:读到这里,大家可能意识到了,创建一个通讯录遇到的最大困难是:如何为通讯录分配空间?
如果我们预先确定其最大容量,即写一个总容量大小确定的通讯录(例如创建一个最多放1000个联系人的通讯录),这时就存在问题,由于空间在运行前就固定好了,所以你无论开辟多大空间都不太合适。预先开辟的空间大了,就会浪费空间;预先开辟空间小了,就会导致放不下联系人。
在这里我们就可以引入动态内存开辟的思路,我们可以预先开辟一个较小空间。如果在放入数据时,发现通讯录满了,就给通讯录扩容。这样可以很好的减少空间的浪费。
💡:在这里还要解决一个问题,那就是什么时候给通讯录进行扩容处理,很容易就能够分析出来,只有在准备添加联系人时,发现通讯录已经满了,此时才需要扩容,也就是说扩容发生在添加联系人的时候。
//由于通讯录中包含多种不同的数据类型
//因此通讯录应该是结构体类型
//动态容量版本
struct Contact
{
struct PeoInfo* date;//指向n个联系人空间的起始地址 作用是维护整个整个通讯录
int sz;//通讯录有效元素的个数
int capcity;//当前通讯录的最大容量
};
//动态容量版本
//DEFAULT_SZ是定义的宏常量 初始值预开辟的较小容量是3
void InitContact(struct Contact* pc)
{
//给通讯录初始化较小的空间
pc->sz = 0;
pc->date=(struct PeoInfo*)malloc(DEFAULT_SZ *sizeof(struct PeoInfo));
pc->capcity = DEFAULT_SZ;
}
void AddContact(struct Contact* pc)
{
if (pc->sz == pc->capcity)
{
//需要进行扩容操作
struct PeoInfo* tmp=(struct PeoInfo*)realloc(pc->date, (DEFAULT_SZ + 2) * sizeof(struct PeoInfo));
if (tmp != NULL)
{
pc->date = tmp;
pc->capcity += 2;
printf("扩容成功\n");
}
else {
printf("扩容失败,通讯录已满,无法添加联系人\n");
return 0;
}
}
//在动态增长的版本,不存在满的概念
//只要空间满就会扩容,不存在添加失败的情况
//if (pc->sz == MAX)
//{
// printf("添加失败,通讯录已满了\n");
//}
printf("请输入姓名:>");
scanf("%s", pc->date[pc->sz].name);
printf("请输入年龄:>");
scanf("%d",&(pc->date[pc->sz].age));
printf("请输入性别:>");
scanf("%s", pc->date[pc->sz].sex);
printf("请输入电话:>");
scanf("%s", pc->date[pc->sz].tele);
printf("请输入地址:>");
scanf("%s", pc->date[pc->sz].addr);
pc->sz++;
printf("添加成功\n");
}
其中的代码块是从总代码中截取出来的,有一些函数,变量未曾声明定义。
三:如何添加联系人
💡:想要在通讯录中添加联系人十分简单,由于我们的通讯录总容量是动态开辟的,所以在扩容成功的前提下不存在通讯录已经满了,无法添加的情况。我们只需要把人的信息一个个输入就够了。
🔑:代码展示
void AddContact(struct Contact* pc)
{
if (pc->sz == pc->capcity)
{
//需要进行扩容操作
struct PeoInfo* tmp=(struct PeoInfo*)realloc(pc->date, (DEFAULT_SZ + 2) * sizeof(struct PeoInfo));
if (tmp != NULL)
{
pc->date = tmp;
pc->capcity += 2;
printf("扩容成功\n");
}
else {
printf("扩容失败,通讯录已满,无法添加联系人\n");
return 0;
}
}
//在动态增长的版本,不存在满的概念
//只要空间满就会扩容,不存在添加失败的情况
//if (pc->sz == MAX)
//{
// printf("添加失败,通讯录已满了\n");
//}
printf("请输入姓名:>");
scanf("%s", pc->date[pc->sz].name);
printf("请输入年龄:>");
scanf("%d",&(pc->date[pc->sz].age));
printf("请输入性别:>");
scanf("%s", pc->date[pc->sz].sex);
printf("请输入电话:>");
scanf("%s", pc->date[pc->sz].tele);
printf("请输入地址:>");
scanf("%s", pc->date[pc->sz].addr);
pc->sz++;
printf("添加成功\n");
}
四:如何打印出当前通讯录
首先我们先来看一看预期达到的效果,再按照这个预期的效果来写代码
从这个表格可以看出我们预期实现的效果,类似一个表格,上面一行写标签,下面写具体联系人的信息。
想要达到这个效果,无疑我们必须要会灵活的运用格式化输出,如果不会的可以看看书本,在此不做过多的赘述。
🔑:代码展示:
void ShowContact(struct Contact* pc)
{
int i = 0;
//打印表格标签名 注意格式化输出
printf("%20s\t%5s\t%8s\t%20s\t%30s\n", "name", "age", "sex", "tele", "addr");
for (i = 0; i < pc->sz; i++)
{
//利用循环逐个打印联系人的信息
printf("%20s\t%5d\t%8s\t%20s\t%30s\n", pc->date[i].name,
pc->date[i].age,
pc->date[i].sex,
pc->date[i].tele,
pc->date[i].addr);
}
}
五:如何删除指定联系人的信息
💡:我们想删除指定联系人的信息,前提条件是我们要通过联系人的姓名在通讯录中找到该联系人,所以这个问题的难点就在于以下两点:
1:通过姓名找到该联系人后,要返回什么信息,执行什么样的操作?
2:如何在date指针所维护的联系人信息空间中,删除指定的元素?
🔑:现在我们来解决以上的两个问题
1:在找到时,我们希望能返回该联系人的下标(即把动态变化的空间视为一个数组),若找不到则返回-1
2:要在这个数组中删除一个联系人其实很简单,我们只需要把该联系人之后的所有联系人整体向左移动一个单位,然后让有效联系人个数减一就够了
💡:代码展示
//对于按名字查找指定联系人时我们定义如下规则
//如果查找到了,则返回date数组中该人信息的下标
//如果找不到,则返回-1
int SearchByNameContact(const char name[], struct Contact* pc)
{
int i = 0;
for (i = 0; i < pc->sz; i++)
{
if (strcmp(name, pc->date[i].name) == 0)
{
return i;
}
}
return -1;
}
//删除指定联系人
void DelContact(struct Contact* pc)
{
if (pc->sz == 0)
{
printf("该通讯录为空,无法删除\n");
return;
}
printf("请输入你要删除联系人的姓名:>");
char name[NAME_MAX] = { 0 };
scanf("%s", name);
int pos = SearchByNameContact(name,pc);
if (pos == -1)
{
printf("你想删除的联系人不存在\n");
}
else
{
//对联系人进行删除
for (int i = pos; i < pc->sz - 1; i++)
{
pc->date[i] = pc->date[i + 1];
}
pc->sz--;
printf("删除成功\n");
}
}
而查找功能其实就是按名字查找函数和打印的组合。
而修改功能其实就是按名字查找和重新录入信息的组合。
这两种功能的思路和以上方法思路重合,在此就不做过多的赘述。
💎总代码的展示💎
有于该项目涉及到很多的函数,因此我们分模块来写,在contact.h中声明函数 ,在contact.c中给出函数的具体定义 在text.c中使用函数并打印菜单
💡;contact.h
#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#define NAME_MAX 20
#define SEX_MAX 5
#define TELE_MAX 20
#define ADDR_MAX 30
#define MAX 1000
#define DEFAULT_SZ 3
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct PeoInfo
{
char name[NAME_MAX];
int age;
char sex[SEX_MAX];
char tele[TELE_MAX];
char addr[ADDR_MAX];
};
//静态版本
//struct Contact
//{
将1000个人的数据放在date数组中
// struct PeoInfo date[MAX];
// int sz;//记录当前通讯录有效学生信息的个数
//};
//动态版本
struct Contact
{
struct PeoInfo* date;
int sz;//通讯录有效元素的个数
int capcity;//当前通讯录的最大容量
};
//初始化通讯录
void InitContact(struct Contact* pc);
//添加联系人
void AddContact(struct Contact* pc);
//显示通讯录(打印通讯录)
void ShowContact(struct Contact* pc);
//查找练习人,并输出
void SearchContact(const struct Contact* pc);
//删除指定的联系人
void DelContact(struct Contact* pc);
//修改指定的联系人
void ModifyContact(struct Contact* pc);
💡:text.c的代码
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#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()
{
//1:创建一个通讯录
struct Contact con;
//2:对通讯录进行初始化
InitContact(&con);
int input = 0;
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case ADD:
AddContact(&con);
break;
case DEL:
DelContact(&con);
break;
case SHOW:
ShowContact(&con);
break;
case EXIT:
printf("退出通讯录\n");
break;
case SEARCH:
SearchContact(&con);
break;
case MODIFY:
ModifyContact(&con);
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
return 0;
}
💡:contact.c的代码
#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.h"
//OK 已经实现功静态容量的通讯录
//将静态容量的通讯录-->动态容量的通讯录 升级版本
//void InitContact(struct Contact* pc)
//{
// pc->sz = 0;
// memset(pc->date,0,sizeof(pc->date));
//}
//动态容量版本
void InitContact(struct Contact* pc)
{
//给通讯录初始化较小的空间
pc->sz = 0;
pc->date=(struct PeoInfo*)malloc(DEFAULT_SZ *sizeof(struct PeoInfo));
pc->capcity = DEFAULT_SZ;
}
void AddContact(struct Contact* pc)
{
if (pc->sz == pc->capcity)
{
//需要进行扩容操作
struct PeoInfo* tmp=(struct PeoInfo*)realloc(pc->date, (DEFAULT_SZ + 2) * sizeof(struct PeoInfo));
if (tmp != NULL)
{
pc->date = tmp;
pc->capcity += 2;
printf("扩容成功\n");
}
else {
printf("扩容失败,通讯录已满,无法添加联系人\n");
return 0;
}
}
//在动态增长的版本,不存在满的概念
//只要空间满就会扩容,不存在添加失败的情况
//if (pc->sz == MAX)
//{
// printf("添加失败,通讯录已满了\n");
//}
printf("请输入姓名:>");
scanf("%s", pc->date[pc->sz].name);
printf("请输入年龄:>");
scanf("%d",&(pc->date[pc->sz].age));
printf("请输入性别:>");
scanf("%s", pc->date[pc->sz].sex);
printf("请输入电话:>");
scanf("%s", pc->date[pc->sz].tele);
printf("请输入地址:>");
scanf("%s", pc->date[pc->sz].addr);
pc->sz++;
printf("添加成功\n");
}
void ShowContact(struct Contact* pc)
{
int i = 0;
printf("%20s\t%5s\t%8s\t%20s\t%30s\n", "name", "age", "sex", "tele", "addr");
for (i = 0; i < pc->sz; i++)
{
//进行通讯表的打印
printf("%20s\t%5d\t%8s\t%20s\t%30s\n", pc->date[i].name,
pc->date[i].age,
pc->date[i].sex,
pc->date[i].tele,
pc->date[i].addr);
}
}
//静态版通讯录--->升级到动态版
//
//如果在通讯录中找到了联系人,则打印出该联系人的信息
//如果没有该联系人则返回false
//并不是像hash 查找一样 需要一个个的查找
//对于按名字查找指定联系人时我们定义如下规则
//如果查找到了,则返回date数组中该人信息的下标
//如果找不到,则返回-1
int SearchByNameContact(const char name[], struct Contact* pc)
{
int i = 0;
for (i = 0; i < pc->sz; i++)
{
if (strcmp(name, pc->date[i].name) == 0)
{
return i;
}
}
return -1;
}
//删除指定联系人
void DelContact(struct Contact* pc)
{
if (pc->sz == 0)
{
printf("该通讯录为空,无法删除\n");
return;
}
printf("请输入你要删除联系人的姓名:>");
char name[NAME_MAX] = { 0 };
scanf("%s", name);
int pos = SearchByNameContact(name,pc);
if (pos == -1)
{
printf("你想删除的联系人不存在\n");
}
else
{
//对联系人进行删除
for (int i = pos; i < pc->sz - 1; i++)
{
pc->date[i] = pc->date[i + 1];
}
pc->sz--;
printf("删除成功\n");
}
}
void SearchContact(const struct Contact* pc)
{
printf("请输入你要查找联系人的名字:>");
char name[NAME_MAX] = { 0 };
scanf("%s", name);
int ret = SearchByNameContact(name,pc);
if (ret == -1)
{
printf("查无此人\n");
return 0;
}
printf("%20s\t%5s\t%8s\t%20s\t%30s\n", "name", "age", "sex", "tele", "addr");
printf("%20s\t%5d\t%8s\t%20s\t%30s\n", pc->date[ret].name,
pc->date[ret].age,
pc->date[ret].sex,
pc->date[ret].tele,
pc->date[ret].addr);
}
void ModifyContact(struct Contact* pc)
{
printf("请输入你要修改的联系人\n");
char name[NAME_MAX] = { 0 };
scanf("%s", name);
int pos = SearchByNameContact(name,pc);
if (pos == -1)
{
printf("要修改的联系人不存在\n");
return;
}
else
{
printf("请输入姓名:>");
scanf("%s", pc->date[pos].name);
printf("请输入年龄:>");
scanf("%d", &(pc->date[pos].age));
printf("请输入性别:>");
scanf("%s", pc->date[pos].sex);
printf("请输入电话:>");
scanf("%s", pc->date[pos].tele);
printf("请输入地址:>");
scanf("%s", pc->date[pos].addr);
printf("修改成功\n");
}
}
以上代码,还可做优化在此仅作参考,若有更好的算法,还望能够私信告知,多谢各位。
由于本人水平十分有限,若有错误请即使告知!如果有帮助别忘了,万分感谢。
点赞👍 收藏✨ 关注✌