【C语言期末项目-通讯录】-升级可动态申请内存版(手把手详细过程,期末评分A+的项目,答辩辅助神博文,建议三连点赞收藏)

目录

​编辑

前言:

 1.项目功能需求分析 

2.文件框架说明 

3.程序主框架实现 

4.创建联系人结构体类型和通讯录结构体类型 

4.1创建通讯录 

5.程序功能实现--封装功能函数实现不同功能 

5.1通讯录初始化

 5.2增加联系人 

 5.3显示所有联系人的信息

5.4删除指定联系人的信息 

5.5查找指定联系人 

 5.6修改联系人的信息

 6.释放通讯录

7.测试通讯录动态增长 

6.结语及源码


前言:

对于这个项目,是前面数组基础版本的通讯录的升级版本,大家如果一步理解有困难,一定先点击我的主页了解一下前几篇博客的内容做一个铺垫,我这里罗列一下关联最大的三篇:

①动态内存详解

②通讯录基础数组版本

③C语言结构体详解

 1.项目功能需求分析 

实现一个通讯录:
通讯录保存个人信息

 名字,年龄 性别 电话 住址    用保存人的信息的结构体实现

1.通讯录空间不固定,可以先存放一部分的信息,当用户还要存入信息的时候可以自动增加容量。

(比如默认先存放3个联系人的信息,不够了,就每次增加两个联系人信息的空间给用户)
 2.添加联系人
3.删除指定联系人
4.查找指定联系人
5.显示所有人的信息
6.排序功能

7.可以修改指定联系人的信息

2.文件框架说明 

test.c 主菜单文件,用于功能测试

contact.c 函数具体实现文件

contact.h 存放函数和类型的声明和必要头文件

3.程序主框架实现 

我们希望在程序中实现不同的操作,比如上述的增删查改联系人的信息,而不是执行一次程序就结束,用户可以选择退出。接下来我们就实现一下主体框架,包含主页面菜单显示。

void menu()
{printf("----------------------------------\n");printf("|***0.Exit       1.ADD***********|\n");printf("|***2.DEL       3.SEARCH*********|\n");printf("|***4.MODIFY    5.SHOW***********|\n");printf("|***6.SORT       ****************|\n");printf("----------------------------------\n");}
void test()
{int  input = 0;do{menu();printf("请选择:》");scanf("%d", &input);switch (input){case 1:break;case 2:break;case 3:break;case 4:break;case 5:break;case 6:break;case 0:printf("退出程序成功\n");break;default:printf("选择错误,请根据菜单选项进行功能选择,谢谢配合\n");break;}} while (input);
}int main()
{test();return 0;
}

case后面的选项是1,2,3,4,5,那么我们想可不可以利用枚举,让case后面的选项既能表示我们的功能含义又能表示功能选择数字,这样的代码可读性就会更高一些,我们就可以修改如下 :

首先定义选择枚举类型:

enum OPtion
{EXIT,//0ADD,//1DEL,//2SEARCH,MODIFY,SHOW,SORT};

这个依然是放在我们的头文件中去定义,这样代码的整体美观些,接下来的结构修改为:

void menu()
{printf("----------------------------------\n");printf("|***0.Exit       1.ADD***********|\n");printf("|***2.DEL       3.SEARCH*********|\n");printf("|***4.MODIFY    5.SHOW***********|\n");printf("|***6.SORT       ****************|\n");printf("----------------------------------\n");}
void test()
{int  input = 0;Contact con;InitContact(&con);do{menu();printf("请选择:》");scanf("%d", &input);switch (input){case ADD:break;case DEL:break;case SEARCH:break;case MODIFY:break;case SHOW:break;case SORT:break;case EXIT:printf("退出程序成功\n");break;default:printf("选择错误,请根据菜单选项进行功能选择,谢谢配合\n");break;}} while (input);
}int main()
{test();return 0;
}

4.创建联系人结构体类型和通讯录结构体类型 

由于通讯录存储的是每一个联系人的基本信息,名字、电话、性别、住址等,所以我们可以封装一个联系人结构体类型用于描述每一个联系人。

typedef struct PeoInfo
{char name[20];int age;char sex[5];char telepnumber[12];char addr[30];} PeoInfo;

为了后续书写的方便将struct PeoInfo类型重定义为PeoInfo

我们现在需要一块空间来存储我们联系人的信息,这块空间的起始地址交给我们的一个联系人结构体类型的指针。

PeoInfo* data;//指向了存放数据的空间

由于,我们后续会对通讯录进行增删操作,就会改变数组的大小,为了方便查看或者显示或者为了方便操作我们可以定义一个SZ来记录数组的大小,增加一个联系人,sz+1.......

int sz;//记录的是当前有效元素的个数

这,当我们起始有三个联系人类型那么大的空间的时候,还没有存入联系人的信息,此时我们的sz=0,那么我们什么时候开始扩大我们的空间呢,所以我们专门设计一个变量来记录我们通讯录的数据空间容量,起始容量为3,当sz 等于我们的容量的时候,就可以扩容了。

int  capacity;//记录当前存放数据空间的容量

数据区,容量和联系人数目都是通讯录的属性,我们可以设计一下通讯录类型结构体:

typedef struct Contact
{PeoInfo* data;//指向了存放数据的空间int sz;//记录的是当前有效元素的个数int  capacity;//记录当前存放数据空间的容量}Contact;

为了以后书写方便,用typedef重定义通讯录结构体类型为:Contact类型。

4.1创建通讯录 

当我们的通讯录类型有了过后,我们就可以创建一个通讯录:

5.程序功能实现--封装功能函数实现不同功能 

5.1通讯录初始化

初始化函数,首先要为我们的data数据区申请3个PeoINfo类型的空间,将sz也初始化为0.将容量初始化3,起始的容量不一定设置为3,方便不同的伙伴来设置,我们将容量的大小也设置为宏.我们传参使用了结构体地址传参,接收参数使用了结构体指针,内存占用少。

void Initcontact(Contact* pc)
{assert(pc);pc->data = (PeoInfo*)malloc(CAPICITY * sizeof(PeoInfo));if (pc->data == NULL){perror("Initcontact");}pc->sz = 0;pc->capacity = CAPICITY;
}

 5.2增加联系人 

在动态版本中,我们不用考虑通讯录满没满。而是应该考虑通讯录申请的空间是否够了,不够就要扩容,每录入一个联系的信息,sz就+1,当我们的sz与我们的容量相等的时候,就要考虑扩容,所以我们进入增加函数第一步就应该先判断是否要增容。

为了代码方便,我们将检查是否要增容这一步封装为一个函数:

int ChekCapacity(Contact* pc)
{if (pc->sz == pc->capacity){PeoInfo* ptr =(PeoInfo * )realloc(pc->data, (pc->capacity + 2 * sizeof(PeoInfo)));if (ptr == NULL){perror("ChekCapaticy");return  0;}else{pc->data = ptr;pc->capacity += 2;}}return 1;
}

函数返回1,说明增容成功,函数返回0说明增容失败。当然,也不一定每次只增加两个空间,我们还是将增加的空间数目定义为宏,方便伙伴们修改:

#define ADDC 2
void Initcontact(Contact* pc)
{assert(pc);pc->data = (PeoInfo*)malloc(CAPICITY * sizeof(PeoInfo));if (pc->data == NULL){perror("Initcontact");}pc->sz = 0;pc->capacity = CAPICITY;
}
int ChekCapacity(Contact* pc)
{if (pc->sz == pc->capacity){PeoInfo* ptr =(PeoInfo * )realloc(pc->data, (pc->capacity + ADDC * sizeof(PeoInfo)));if (ptr == NULL){perror("ChekCapaticy");return  0;}else{pc->data = ptr;pc->capacity += ADDC;printf("增容成功\n");
return1;}}return 1;
}

我们的增加联系人的函数为:

void AddContact(Contact* pc)
{assert(pc);//断言防止传入空指针//首先判断通讯录满每满if (0 == ChekCapacity(pc)){return;}printf("请输入联系人的名字>\n");scanf("%s", pc->data[pc->sz].name);//由于name是数组名这里就不要&符号了printf("请输入联系人的年龄>\n");scanf("%d", &(pc->data[pc->sz].age));printf("请输入联系人的性别>\n");scanf("%s", pc->data[pc->sz].sex);printf("请输入联系人的电话>\n");scanf("%s", pc->data[pc->sz].telepnumber);printf("请输入联系人的地址>\n");scanf("%s", pc->data[pc->sz].addr); //添加联系人成功,我们sz++pc->sz++;printf("添加联系人成功\n");}

 

 5.3显示所有联系人的信息

为了看一下我们添加联系人或者删除联系人的效果,我们这里就先写显示函数

用于我们只是显示通讯录信息,并不将参数内容进行修改所以参数最好使用const进行修饰,防止出错,这一步不理解的伙伴可以点击我的主页看一下我讲解const的博文。

这个函数我们只用循环打印我们data数组内容就行

void ShowContact(const Contact* pc)
{assert(pc);//断言一下防止传入空指针int i = 0;printf("————————————————————————————————————————————————————————————————————————————————————————\n");printf("|%-20s\t|%-4s\t|%-5s\t|%-12s\t|%-30s|\n", "名字", "年龄", "性别", "电话", "地址");for (i = 0; i < pc->sz; i++){printf("|%-20s\t|%-4d\t|%-5s\t|%-12s\t|%-30s|\n", pc->data[i].name,pc->data[i].age,pc->data[i].sex,pc->data[i].telepnumber,pc->data[i].addr);printf("————————————————————————————————————————————————————————————————————————————————————————\n");}printf("————————————————————————————————————————————————————————————————————————————————————————\n");

我们来看一下前两个函数执行的效果:

5.4删除指定联系人的信息 

首先,如果通讯录为空,我们就没有删除的东西

第二,要删除对应联系人的信息,实现思想是先找到对应联系人的名字匹配相同将后面的内容前移覆盖完成删除。

void DelCotact(Contact* pc)
{if (pc->sz == 0){printf("通讯录为空,无法删除\n");return;}char name[DATAMAX] = { 0 };assert(pc);//断言,防止传入空指针printf("请输入要删除的人的名字:》\n");scanf("%s", name);//遍历找到要删除的人int i = 0;int del = 0;int flag = 0;for (i = 0; i < pc->sz; i++){if (strcmp(pc->data[i].name, name) == 0){del = i;flag = 1;break;}}if (flag == 0){printf("找不到要删除的人,请检查名字是否输入正确\n");return;}//找到了,删除联系人for (i = del; i < pc->sz - 1; i++){pc->data[i] = pc->data[i + 1];}pc->sz--;printf("成功删除联系人\n");
}

由于我们后面修改呀等等都会用到查找这个函数,所以我们将其封装为一个查找函数,那我们就可以改造代码如下:

int Findbyname(Contact* pc, char name[])
{assert(pc);int i = 0;for (i = 0; i < pc->sz; i++){if (strcmp(pc->data[i].name, name) == 0){return i;//找到了}}return -1;//找不到
}
void DelCotact(Contact* pc)
{if (pc->sz == 0){printf("通讯录为空,无法删除\n");return;}char name[DATAMAX] = { 0 };assert(pc);//断言,防止传入空指针printf("请输入要删除的人的名字:》\n");scanf("%s", name);//遍历找到要删除的人int i = 0;int del = 0;del = Findbyname(pc, name);if (del == -1){printf("找不到要删除的人,请检查名字是否输入正确\n");return;}//找到了,删除联系人for (i = del; i < pc->sz - 1; i++){pc->data[i] = pc->data[i + 1];}pc->sz--;printf("成功删除联系人\n");
}

我们来看一下效果:

5.5查找指定联系人 

这里我们就实现一下通过名字查找,之所以单独写一个查找函数是因为在功能中,查找到过后还要显示出来。

看实现:

void SearchContact(Contact* pc)
{assert(pc);char name[DATAMAX] = { 0 };printf("请输入要查找的人的名字:》\n");scanf("%s", name);int pos = Findbyname(pc, name);if (pos == -1){printf("要查找的人不存在,请检查名字是否输入正确\n");}else{printf("————————————————————————————————————————————————————————————————————————————————————————\n");printf("|%-20s\t|%-4s\t|%-5s\t|%-12s\t|%-30s|\n", "名字", "年龄", "性别", "电话", "地址");printf("————————————————————————————————————————————————————————————————————————————————————————\n");printf("|%-20s\t|%-4d\t|%-5s\t|%-12s\t|%-30s|\n",pc->data[pos].name,pc->data[pos].age,pc->data[pos].sex,pc->data[pos].telepnumber,pc->data[pos].addr);printf("————————————————————————————————————————————————————————————————————————————————————————\n");}
}

这里查找也可以根据其他的比如号码等,可以利用Switch来实现。看一下效果:

 5.6修改联系人的信息

通过名字来找到要修改信息的联系人,然后重新录入信息

void 	ModifyContact(Contact* pc)
{assert(pc);char name[DATAMAX] = { 0 };printf("请输入要修改人的名字:>\n");scanf("%s", name);int pos = Findbyname(pc, name);if (pos == -1){printf("要修改的人不存在,请检查名字是否输入正确\n");}else{printf("请输入要修改人的名字>\n");scanf("%s", pc->data[pos].name);//由于name是数组名这里就不要&符号了printf("请输入要修改人的年龄>\n");scanf("%d", &(pc->data[pos].age));printf("请输入要修改人的性别>\n");scanf("%s", pc->data[pos].sex);printf("请输入要修改人的电话>\n");scanf("%s", pc->data[pos].telepnumber);printf("请输入要修改人的地址>\n");scanf("%s", pc->data[pos].addr); \printf("修改成功\n");}
}

我们看一下效果:

 6.释放通讯录

由于我们的通讯录空间是malloc来的,所以在最后程序结束应该将空间释放,防止造成内存泄漏。

那我们就在退出选项后面增加一个销毁函数:

void DestoryContact(Contact* pc)
{free(pc->data);pc->data = NULL;pc->capacity = 0;pc->sz = 0;
}

7.测试通讯录动态增长 

先往通讯录里面放入三个联系人信息:

下一步增容:

增容成功,我们展现一下通讯录然后我们查找一下2号联系人:

功能正常。到这里我们的动态版本就修改完成了。

6.结语及源码

以上就是本期的所有内容,大家可以配合解释和原码运行理解。创作不易,大家如果觉得还可以的话,欢迎大家三连,有问题的地方欢迎大家指正,一起交流学习,一起成长,我是Nicn,正在c++方向前行的奋斗者,感谢大家的关注与喜欢。下面附上整个项目源码。后续会陆续更新升级版,大家敬请期待。

test.c

#include"contact.h"//通讯录空间不固定,大小1可以调整
// 默认放3个人的信息,不够,每次增加两个void menu()
{printf("----------------------------------\n");printf("|***0.Exit       1.ADD***********|\n");printf("|***2.DEL       3.SEARCH*********|\n");printf("|***4.MODIFY    5.SHOW***********|\n");printf("|***6.SORT       ****************|\n");printf("----------------------------------\n");}
void test()
{int  input = 0;Contact con;Initcontact(&con);do{menu();printf("请选择:》");scanf("%d", &input);switch (input){case ADD:AddContact(&con);break;case DEL:DelCotact(&con);break;case SEARCH:SearchContact(&con);break;case MODIFY:ModifyContact(&con);break;case SHOW:ShowContact(&con);break;case SORT:break;case EXIT:DestoryContact(&con);printf("退出程序成功\n");break;default:printf("选择错误,请根据菜单选项进行功能选择,谢谢配合\n");break;}} while (input);
}int main()
{test();return 0;
}

contact.h

#include<stdio.h>
#include<string.h>
#include<assert.h>
#include<stdlib.h>#define DATAMAX 100
#define CAPICITY 3
#define ADDC 2enum OPtion
{EXIT,//0ADD,//1DEL,//2SEARCH,MODIFY,SHOW,SORT};//类型的声明
typedef struct PeoInfo
{char name[20];int age;char sex[5];char telepnumber[12];char addr[30];} PeoInfo;//typedef struct Contact
//{
//	PeoInfo data[DATAMAX];
//	int sz;
//}Contact;
typedef struct Contact
{PeoInfo* data;//指向了存放数据的空间int sz;//记录的是当前有效元素的个数int  capacity;//记录当前存放数据空间的容量}Contact;//初始化函数
void Initcontact(Contact* pc);//函数声明
void AddContact(Contact* pc);//增加联系人函数
void ShowContact(const Contact* pc);//显示通讯录信息
void DelCotact(Contact* pc);//删除联系人信息
void SearchContact(Contact* pc);//查找联系人的信息
void 	ModifyContact(Contact* pc);//修改联系人的信息
void DestoryContact(Contact* pc);//销毁通讯录

contact.c

#include"contact.h"
int Findbyname(Contact* pc, char name[])
{assert(pc);int i = 0;for (i = 0; i < pc->sz; i++){if (strcmp(pc->data[i].name, name) == 0){return i;//找到了}}return -1;//找不到
}
void Initcontact(Contact* pc)
{assert(pc);pc->data = (PeoInfo*)malloc(CAPICITY * sizeof(PeoInfo));if (pc->data == NULL){perror("Initcontact");}pc->sz = 0;pc->capacity = CAPICITY;
}
int ChekCapacity(Contact* pc)
{if (pc->sz == pc->capacity){PeoInfo* ptr =(PeoInfo * )realloc(pc->data, (pc->capacity + ADDC * sizeof(PeoInfo)));if (ptr == NULL){perror("ChekCapaticy");return  0;}else{pc->data = ptr;pc->capacity += ADDC;printf("增容成功\n");return 1;}}return 1;
}void AddContact(Contact* pc)
{assert(pc);//断言防止传入空指针//首先判断通讯录满每满if (0 == ChekCapacity(pc)){return;}printf("请输入联系人的名字>\n");scanf("%s", pc->data[pc->sz].name);//由于name是数组名这里就不要&符号了printf("请输入联系人的年龄>\n");scanf("%d", &(pc->data[pc->sz].age));printf("请输入联系人的性别>\n");scanf("%s", pc->data[pc->sz].sex);printf("请输入联系人的电话>\n");scanf("%s", pc->data[pc->sz].telepnumber);printf("请输入联系人的地址>\n");scanf("%s", pc->data[pc->sz].addr); //添加联系人成功,我们sz++pc->sz++;printf("添加联系人成功\n");}
void ShowContact(const Contact* pc)
{assert(pc);//断言一下防止传入空指针int i = 0;printf("————————————————————————————————————————————————————————————————————————————————————————\n");printf("|%-20s\t|%-4s\t|%-5s\t|%-12s\t|%-30s|\n", "名字", "年龄", "性别", "电话", "地址");printf("————————————————————————————————————————————————————————————————————————————————————————\n");for (i = 0; i < pc->sz; i++){printf("|%-20s\t|%-4d\t|%-5s\t|%-12s\t|%-30s|\n",pc->data[i].name,pc->data[i].age,pc->data[i].sex,pc->data[i].telepnumber,pc->data[i].addr);printf("————————————————————————————————————————————————————————————————————————————————————————\n");}printf("————————————————————————————————————————————————————————————————————————————————————————\n");}void DelCotact(Contact* pc)
{if (pc->sz == 0){printf("通讯录为空,无法删除\n");return;}char name[DATAMAX] = { 0 };assert(pc);//断言,防止传入空指针printf("请输入要删除的人的名字:》\n");scanf("%s", name);//遍历找到要删除的人int i = 0;int del = 0;del = Findbyname(pc, name);if (del == -1){printf("找不到要删除的人,请检查名字是否输入正确\n");return;}//找到了,删除联系人for (i = del; i < pc->sz - 1; i++){pc->data[i] = pc->data[i + 1];}pc->sz--;printf("成功删除联系人\n");
}void SearchContact(Contact* pc)
{assert(pc);char name[DATAMAX] = { 0 };printf("请输入要查找的人的名字:》\n");scanf("%s", name);int pos = Findbyname(pc, name);if (pos == -1){printf("要查找的人不存在,请检查名字是否输入正确\n");}else{printf("————————————————————————————————————————————————————————————————————————————————————————\n");printf("|%-20s\t|%-4s\t|%-5s\t|%-12s\t|%-30s|\n", "名字", "年龄", "性别", "电话", "地址");printf("————————————————————————————————————————————————————————————————————————————————————————\n");printf("|%-20s\t|%-4d\t|%-5s\t|%-12s\t|%-30s|\n",pc->data[pos].name,pc->data[pos].age,pc->data[pos].sex,pc->data[pos].telepnumber,pc->data[pos].addr);printf("————————————————————————————————————————————————————————————————————————————————————————\n");}
}void 	ModifyContact(Contact* pc)
{assert(pc);char name[DATAMAX] = { 0 };printf("请输入要修改人的名字:>\n");scanf("%s", name);int pos = Findbyname(pc, name);if (pos == -1){printf("要修改的人不存在,请检查名字是否输入正确\n");}else{printf("请输入要修改人的名字>\n");scanf("%s", pc->data[pos].name);//由于name是数组名这里就不要&符号了printf("请输入要修改人的年龄>\n");scanf("%d", &(pc->data[pos].age));printf("请输入要修改人的性别>\n");scanf("%s", pc->data[pos].sex);printf("请输入要修改人的电话>\n");scanf("%s", pc->data[pos].telepnumber);printf("请输入要修改人的地址>\n");scanf("%s", pc->data[pos].addr); \printf("修改成功\n");}
}void DestoryContact(Contact* pc)
{free(pc->data);pc->data = NULL;pc->capacity = 0;pc->sz = 0;
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/680672.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Java EE初阶十二】网络编程TCP/IP协议(二)

1. 关于TCP 1.1 TCP 的socket api tcp的socket api和U大片的socket api差异很大&#xff0c;但是和前面所讲的文件操作很密切的联系 下面主要讲解两个关键的类&#xff1a; 1、ServerSocket&#xff1a;给服务器使用的类&#xff0c;使用这个类来绑定端口号 2、Socket&#xf…

【图论】【树形dp】【深度优先搜索】2538. 最大价值和与最小价值和的差值

作者推荐 【深度优先搜索】【树】【图论】2973. 树中每个节点放置的金币数目 本文涉及知识点 深度优先搜索 LeetCode2538. 最大价值和与最小价值和的差值 给你一个 n 个节点的无向无根图&#xff0c;节点编号为 0 到 n - 1 。给你一个整数 n 和一个长度为 n - 1 的二维整数…

使用LoRA和QLoRA微调LLMs:数百次实验的见解

前言 翻译文章《Finetuning LLMs with LoRA and QLoRA: Insights from Hundreds of Experiments》原文地址因译者水平有限&#xff0c;翻译过程中有错误请在评论区指出 提要 LoRA是用于训练自定义LLM的最广泛使用、参数效率最高的微调技术之一。从使用QLoRA节省内存到选择最…

iTop-4412 裸机程序(十九)- 按键中断

目录 0.源码1.异常向量表1.1 原理1.2 异常种类1.3 ARMv7 规定的异常向量表 2. 中断2.1 iTop-4412 中使用的中断相关寄存器 上篇博文介绍了按键的轮询处理方式&#xff0c;本篇介绍按键的中断方式。 0.源码 GitHub&#xff1a;https://github.com/Kilento/4412NoOS 1.异常向量…

常见范数介绍

在线性代数中&#xff0c;符号 ( ||x|| ) 表示向量 ( x ) 的范数&#xff08;Norm&#xff09;。范数是一个将向量映射到非负值的函数&#xff0c;它衡量了向量的大小或长度。范数可以是多种类型&#xff0c;其中最常见的有&#xff1a; 欧几里得范数&#xff08;L2范数&#x…

力扣题目训练(8)

2024年2月1日力扣题目训练 2024年2月1日力扣题目训练404. 左叶子之和405. 数字转换为十六进制数409. 最长回文串116. 填充每个节点的下一个右侧节点指针120. 三角形最小路径和60. 排列序列 2024年2月1日力扣题目训练 2024年2月1日第八天编程训练&#xff0c;今天主要是进行一些…

人工智能能产生情绪吗?

此图片来源于网络 一、人情绪的本质是什么&#xff1f; 人的情绪本质是一个复杂的现象&#xff0c;涉及到生理、心理和社会的多个层面。以下是关于情绪本质的几种观点&#xff1a; 情绪的本质是生命能量的表达。情绪被认为是生命能量的一种体现&#xff0c;通过情绪的体验和…

web前端(第一天HTML)

前端是什么&#xff1f; 网页&#xff1f; 将数据以各种方式&#xff08;如&#xff1a;表格、饼图、柱状图等&#xff09;呈现给用户&#xff0c;我们就可以称之为前端。 做前端所需要的工具&#xff1f; notepad 、 editplus 、 notepad 、 vscode 、 webstorm 等&#x…

Netty应用——通过WebSocket编程实现服务器和客户端长连接(十八)

Http协议是无状态的&#xff0c;浏览器和服务器间的请求响应一次&#xff0c;下一次会重新创建连接要求:实现基于webSocket的长连接的全双工的交互改变Http协议多次请求的约束&#xff0c;实现长连接了&#xff0c; 服务器可以发送消息给浏览器客户端浏览器和服务器端会相互感知…

【python】Fraction类详解及生成分数四则运算“试卷”

文章目录 一、前言实验所需的库终端指令Fraction类1. Fraction(numerator, denominator)&#xff1a;2. Fraction(numerator)3. Fraction()4. 分数作参数5. 负分数作参数6. 字符串作参数7. 小数作参数8. 科学计数法9. 浮点数作参数10. 浮点数精度问题11. Decimal对象作参数 二、…

【Java程序设计】【C00264】基于Springboot的原创歌曲分享平台(有论文)

基于Springboot的原创歌曲分享平台&#xff08;有论文&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于Springboot的原创歌曲分享平台 本系统分为平台功能模块、管理员功能模块以及用户功能模块。 平台功能模块&#xff1a;在平台首页可以查看首…

可视化工具:将多种数据格式转化为交互式图形展示的利器

引言 在数据驱动的时代&#xff0c;数据的分析和理解对于决策过程至关重要。然而&#xff0c;不同的数据格式和结构使得数据的解读变得复杂和困难。为了解决这个问题&#xff0c;一种强大的可视化工具应运而生。这个工具具有将多种数据格式&#xff08;包括JSON、YAML、XML、C…

Swift Combine 用 Future 来封装异步请求 从入门到精通十一

Combine 系列 Swift Combine 从入门到精通一Swift Combine 发布者订阅者操作者 从入门到精通二Swift Combine 管道 从入门到精通三Swift Combine 发布者publisher的生命周期 从入门到精通四Swift Combine 操作符operations和Subjects发布者的生命周期 从入门到精通五Swift Com…

16 亚稳态原理和解决方案

1. 亚稳态原理 亚稳态是指触发器无法在某个规定的时间段内到达一个可以确认的状态。在同步系统中&#xff0c;输入总是与时钟同步&#xff0c;因此寄存器的setup time和hold time是满足的&#xff0c;一般情况下是不会发生亚稳态情况的。在异步信号采集中&#xff0c;由于异步…

【C++】STL之string 超详解

目录 1.string概述 2.string使用 1.构造初始化 2.成员函数 1.迭代器 2.容量操作 1.size和length 返回字符串长度 2.resize 调整字符串大小 3.capacity 获得字符串容量 4.reserve 调整容量 5.clear 清除 6.empty 判空 3.string插入、追加 、拼接 1.运算…

DataBinding源码浅析---初始化过程

作为Google官方发布的支持库&#xff0c;DataBinding实现了UI组件和数据源的双向绑定&#xff0c;同时在Jetpack组件中&#xff0c;也将DataBinding放在了Architecture类型之中。对于DataBinding的基础使用请先翻阅前两篇文章的详细阐述。本文所用代码也是建立在之前工程基础之…

利用Windows10漏洞破解密码(保姆级教学)

前言: 本篇博客只是技术分享并非非法传播知识,实验内容均是在虚拟机中进行,并非真实环境 正文: 一.windows10电脑密码破解 1)开启windows10虚拟机,停留在这个页面 2&#xff09;按5次Shift键,出现这个粘滞键,如果没有出现的,则说明漏洞已经修复 3)重新启动,在这个页面的时候…

P1928 外星密码

网址如下&#xff1a; P1928 外星密码 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) C的string真的是太好用辣&#xff01; 思路就是用一个函数来递归翻译 代码如下&#xff1a; #include<iostream> #include<string> #include<cctype> using namespace…

C++重新入门-字符串

C 提供了以下两种类型的字符串表示形式&#xff1a; C 风格字符串C 引入的 string 类类型 1.C 风格字符串 C 风格字符串是使用字符数组来表示的&#xff0c;以空字符 \0 结尾。它们通常被称为 "C-style strings"。例如&#xff1a; char str[] "Hello"…

CSC8014: Software Development – Advanced Techniques 第二周

2.3 Introduction to the Collections Framework The Collections Framework • A unified, generic framework for the representation and manipulation of groups of objects –Manipulation is independent of representation • Includes: –Interfaces that define typ…