顺序表小项目---通讯录的实现

news2024/11/24 18:25:20

文章目录

  • 前言
  • 一、静态通讯录的需求分析与实现
    • 1.通讯录的结构体需求
    • 2.通讯录的功能需求
    • 3.通讯录的主函数创建
    • 4.通讯录的所用函数的实现
      • 1.通讯录的初始化
      • 2.通讯录的增加联系人
      • 3.通讯录的查找联系人
      • 4.通讯录的删除联系人
      • 5.通讯录的修改联系人
      • 6.联系人的排序
  • 二、静态通讯录的需求分析与实现
    • 1.通讯录的结构体需求
    • 2.通讯录的功能需求
    • 3.通讯录的所用函数的实现
      • 1.通讯录的初始化
      • 2.通讯录的增加联系人
      • 3.通讯录的销毁
  • 三、通讯录的文件操作
      • 1.通讯录的加载
      • 2.通讯录的保存
  • 总结


前言

根据上一节的顺序表,我们来做一个小项目,就是实现一个通讯录,这个通讯录也是实现静态和动态的两个版本,并且还会加入我们所学过的文件操作,使我们的通讯录可以保存在磁盘上,也使我们可以查询以往操作的联系人的数据。话不多说,让我们快快开始吧。

一、静态通讯录的需求分析与实现

1.通讯录的结构体需求

我们先来看联系人的信息,我们要存储联系人的姓名,性别,电话,年龄,住址等等。姓名,性别,电话和住址我们分别要用一个字符数组来存储。年龄我们需要用一个int整形来存储。
这些我们要创建一个联系人的结构体来存储信息。我们在创建一个通讯录的结构体,一个结构体成员是我们的联系人结构体数组,另一个成员是我们当前联系人的个数。

#define MAX 3			//通讯录最大的容量
#define MAX_NAME 10		//联系人的姓名最大字符个数
#define MAX_GENDER 3	//联系人的性别最大字符个数
#define MAX_PHONE 15	//联系人的电话最大字符个数
#define MAX_ADDRESS 15	//联系人的住址最大字符个数
typedef struct People//联系人的信息
{
	char name[MAX_NAME];//姓名
	char gender[MAX_GENDER];//性别
	int age;//年龄
	char phone[MAX_PHONE];//电话
	char address[MAX_ADDRESS];//住址
}People;
typedef struct Communicate//通讯录的结构体
{
	People people[MAX];//联系人的结构体数组
	int position;//通讯录中人物的个数和要插入人物的位置
}Com;

我们把存储联系人的信息结构体数组最大值设为了3,方便测试我们存满的情况,联系人的个人信息我们也设置了上限,方便我们日后修改我们的数据。我们创建联系人的结构体数组相当于我们顺序表中整形数组,只是类型不同,一个是内置类型,一个是自定义类型。但实现起来大同小异。

2.通讯录的功能需求

我们现在有了可以用来存储来联系人信息的结构体了。现在我们来看我们要实现的功能。
我们要有增加联系人的功能(我们这里直接用尾插实现),我们还要有删除联系人的功能(这里要配合查询功能实现),我们还要有查找联系人和修改联系人。最后就是我们要实现把所有联系人按照姓名排序来打印出来。
这时间我们的简易菜单和要实现的函数就出来了:

enum OPTION//选则
{
	EXIT,//退出通讯录
	ADD,//增加联系人
	DEL,//删除联系人
	FIND,//查找联系人
	MODF,//修改联系人
	SORT,//对通讯录的人进行姓名排序
	PRIN//对通讯录的人物进行打印
};
void Initialization(Com* com);//初始话通讯录
void AddPeople(Com* com);//增加联系人
void DelPeople(Com* com);//删除联系人
void FindPeople(const Com* com);//查找联系人
void ModfPeople(Com* com);//修改联系人
void SortPeople(Com* com);//对通讯录的人进行姓名排序
void PrintPeople(const Com* com);//对通讯录的人物进行打印

我们这里用了枚举形式,方便我们选则循环时看名字直接知道我们要实现的功能。这个就是我们一会要实现的功能。我们把所需的头文件进行包含,至此我们自己的头文件的全部内容了已经完成了。当我们需要在实现其他额外的功能可以直接把我们要添加的功能函数的声明放在头文件中。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>

以上是我所包含的头文件。

3.通讯录的主函数创建

主函数我们只需要实现响应的调用接口:

#include"main1.h"
void menu()
{
	printf("****************************\n");
	printf("****   1.ADD    2.DEL   ****\n");
	printf("****   3.FIND   4.MODF  ****\n");
	printf("****   5.SORT   6.PRIN  ****\n");
	printf("****	   0.EXIT	****\n");
	printf("****************************\n");
}
int main()
{
	Com com;//创建通讯录
	Initialization(&com);//初始化通讯录
	int input = 0;
	do
	{
		menu();
		printf("请输入你的选则>>\n");
		scanf("%d", &input);
		switch (input)
		{
		case EXIT:
			printf("通讯录已退出\n");
			break;
		case ADD:
			AddPeople(&com);
			break;
		case DEL:
			DelPeople(&com);
			break;
		case FIND:
			FindPeople(&com);
			break;
		case MODF:
			ModfPeople(&com);
			break;
		case SORT:
			SortPeople(&com);
			break;
		case PRIN:
			PrintPeople(&com);
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (input);
	return 0;
}

在这里我们通过switch的选择循环语句来实现。其中我们的功能用一个菜单函数来实现,switch选择是我们的枚举类型,这样日后加入一些其他功能也方便,把我们需要的功能名称加入到枚举类型中,在我们选择结构中在加入我们所需功能的名称。这样做的好处是我们可以直接知道我们要实现的功能,而不用再去查找数字来对应我们要实现的功能。
以上就是我们主函数源文件的全部内容了。

4.通讯录的所用函数的实现

1.通讯录的初始化

在我们实现函数前要先引入我们自己的头文件。

#include"main1.h"//引入我们自己的头文件
void Initialization(Com* com)//初始话通讯录
{
	assert(com);//进行断言,防止传入的指针或者地址不合法而造成的错误
	com->position = 0;//开始没有元素,所以位置为0
}

初始化通讯录和我们初始化顺序表一样。当然,我们也可以选择把我们联系人的结构体也初始化一下。这里我们只需要用到一个库函数:

memset(com->people, 0, MAX*sizeof(People));//要加的代码
//函数原型:
//void *memset( void *dest, int c, size_t count );
//dest:要初始化的起始位置
//c:要初始化的内容
//count:要初始化的字节数

我们只需要在初始话函数上加上上面的代码就可以把我们联系人的结构体也进行初始化。
在这里插入图片描述
这是我们联系人的结构体部分内存中在执行该函数时的前后对比。

2.通讯录的增加联系人

由于我们实现的是静态的通讯录,和我们实现静态顺序表一样,需要判断我们的空间是否已经存满,只有我们在未满的时间才可以加入我们的联系人。

void AddPeople(Com* com)//增加联系人
{
	assert(com);
	if (com->position == MAX)//判断是否已满
	{
		printf("通讯录已满,无法添加\n");
		return;
	}
	printf("请输入要添加联系人的姓名:");
	scanf("%s", com->people[com->position].name);
	printf("请输入要添加联系人的性别:");
	scanf("%s", com->people[com->position].gender);
	printf("请输入要添加联系人的年龄:");
	scanf("%d", &(com->people[com->position].age));//年龄是整形,这里我们需要加上取地址符
	printf("请输入要添加联系人的电话:");
	scanf("%s", com->people[com->position].phone);
	printf("请输入要添加联系人的住址:");
	scanf("%s", com->people[com->position].address);
	printf("添加成功\n");
	com->position++;//对联系人进行加一
}

在这里插入图片描述
这里当我们添加够三个人时发现我们已经添加满了。说明我们的功能可以正常使用。
注意:我们输入年龄时要用取地址符,而其他不用取地址符,因为数组名代表数组首元素地址(当数组名单独加取地址符或数组名单独在sizeof中的情况下,数组名为整个数组的地址)。

3.通讯录的查找联系人

联系人的删除和修改都需要我们的查找功能。我们要做到函数的低耦合,高内聚。所以我们把查找函数在单独封装出来一个,这个和顺序表查找功能实现类似。

int Find(const Com* com)//查找人物,并返回该人物的下标,找不到则返回-1
{
	assert(com);
	char name[MAX_NAME] = { 0 };//通过姓名查找
	printf("请输入要操作联系人的姓名:");
	scanf("%s", name);
	int i = 0;
	for (i = 0; i < com->position; i++)
	{
		if ( 0 == strcmp(com->people[i].name, name) )//判断改联系人是否和我们要查找的联系人姓名相同
		{
			return i;//相同返回改联系人的下标
		}
	}
	return -1;
}
void FindPeople(const Com* com)//查找联系人
{
	int find = Find(com);//用来判断是否找到该人物
	if (-1 == find)
	{
		printf("未找到该联系人\n");
		return;
	}
	printf("%-10s %-5s %-5s %-15s %-10s\n", "姓名", "性别", "年龄", "电话", "住址");//打印信息
	printf("%-10s %-5s %-5d %-15s %-10s\n", com->people[find].name, com->people[find].gender,
		com->people[find].age, com->people[find].phone, com->people[find].address);
	return;
}

我们单独封装出来一个find函数,该函数只实现查找功能。我们查找联系人的函数负责把我们查找的人物打印出来。
在这里插入图片描述
这里我们进行了两次操作,分别为找到和为找到的情况。
注意:我们字符串的比较不可以直接用等号来判断,而是需要用到库函数(strcmp)来实现比较。

4.通讯录的删除联系人

在实现删除联系人之前我们先实现我们的打印功能,方便我们进行观察我们是否删除成功。

void PrintPeople(const Com* com)//对通讯录的人物进行打印
{
	assert(com);
	if (com->position == 0)//判断我们的结构体中是否有人
	{
		printf("暂无联系人\n");
		return;
	}
	int i = 0;
	printf("%-10s %-5s %-5s %-15s %-10s\n", "姓名", "性别", "年龄", "电话", "住址");
	for (i = 0; i < com->position; i++)
	{
		printf("%-10s %-5s %-5d %-15s %-10s\n", com->people[i].name, com->people[i].gender, 
			com->people[i].age,com->people[i].phone, com->people[i].address);
	}
	return;
}

打印功能和顺序表一模一样,只是把遍历整形数组改为遍历结构体数组。
现在让我们实现删除功能吧。我们首先要知道删除人的姓名,这时间就可以通过我们刚才的find函数来找到我们删除人物的下标,剩下的就是顺序表中中间删除元素的操作啦。。

void DelPeople(Com* com)//删除联系人
{
	assert(com);
	if (com->position == 0)//判断我们的结构体中是否有人
	{
		printf("暂无联系人,先添加联系人在删除吧\n");
		return;
	}
	int find = Find(com);//查找我们要删除的人
	if (-1 == find)
	{
		printf("未找到该联系人\n");
		return;
	}
	int i = 0;
	for (i = find; i < com->position - 1; i++)
	{
		com->people[i] = com->people[i + 1];//把这个联系人用后面的元素进行覆盖
	}
	com->position--;//对联系人的数量进行减一
	printf("删除成功\n");
	return;
}

在这里插入图片描述
根据我们上述的操作,可以看出我们的这两个功能没有什么问题。

5.通讯录的修改联系人

修改的前提还是要先找到我们的联系人

void ModfPeople(Com* com)//修改联系人
{
	int find = Find(com);
	if (-1 == find)
	{
		printf("未找到该联系人\n");
		return;
	}
	printf("请输入要修改联系人的姓名:");
	scanf("%s", com->people[find].name);
	printf("请输入要修改联系人的性别:");
	scanf("%s", com->people[find].gender);
	printf("请输入要修改联系人的年龄:");
	scanf("%d", &(com->people[find].age));
	printf("请输入要修改联系人的电话:");
	scanf("%s", com->people[find].phone);
	printf("请输入要修改联系人的住址:");
	scanf("%s", com->people[find].address);
	printf("修改成功\n");
}

这里的修改就和顺序表一样,没什么可以改的,这里我们还是要调用我们的查找函数来判断是否找到。
在这里插入图片描述
根据我们上述的操作,可以看出我们的修改功能没有什么问题。可以完成我们的需求。

6.联系人的排序

联系人的排序我们使用的是按姓名排序,实现如下:

int compar_by_name(const void* e1, const void* e2)
{
	return *((People*)e1)->name - *((People*)e2)->name;
}
void SortPeople(Com* com)//对通讯录的人进行姓名排序
{
	qsort(com->people, com->position, sizeof(People), compar_by_name);
}

我这里使用的是库函数(qsort)进行排序,需要我们自己实现一个比较规则。当然也可以使用我们顺序表中的排序,不过要把那个整形比较改为结构体中姓名的比较。
在这里插入图片描述
这样我们静态通讯录就已经完成了。

二、静态通讯录的需求分析与实现

我们已经实现静态的通讯录,更具我们静态顺序表改动态顺序表我们也可以轻而易举的把我们的静态的通讯录改造为动态通讯录。

1.通讯录的结构体需求

typedef struct Communicate
{
	People* people;
	int position;//通讯录中人物的个数和要插入人物的位置
	int max;//通讯录的容量
}Com;

这里我们不再需要结构体数组了,而是换成了结构体指针。增加了一个表示容量的整型值,其他的和静态顺序表一模一样。

2.通讯录的功能需求

这里我们多加入一个函数,用来实现动态开辟内存的释放。我们把这个函数放在程序退出时调用。

void Destroy(Com* com);//对通讯录进行销毁

3.通讯录的所用函数的实现

1.通讯录的初始化

void Initialization(Com* com)//初始话通讯录
{
	assert(com);//进行断言,防止传入的指针或者地址不合法而造成的错误
	com->position = 0;//开始没有元素,所以位置为0
	com->max = 3;//初始容量赋为3
	com->people = (People*)malloc(sizeof(People) * com->max);//开辟容量大小的联系人结构体
	if (com->people == NULL)//开辟失败就进行报错
	{
		perror("malloc");
		return;
	}
}

在这里我们需要为我们的联系人结构体开辟一块空间,使之可以存放数据。和我们动态顺序表开辟空间一样。思路没有什么不同。

2.通讯录的增加联系人

void Expansion(Com* com)//判断是否进行扩容
{
	if (com->position == com->max)
	{
		People* peo = (People*)realloc(com->people, sizeof(People) * com->max * 2);
		if (peo == NULL)
		{
			perror("realloc");
			return;
		}
		com->people = peo;
		com->max *= 2;
	}
}
void AddPeople(Com* com)//增加联系人
{
	assert(com);
	Expansion(com);//判断否进行扩容
	printf("请输入要添加联系人的姓名:");
	scanf("%s", com->people[com->position].name);
	printf("请输入要添加联系人的性别:");
	scanf("%s", com->people[com->position].gender);
	printf("请输入要添加联系人的年龄:");
	scanf("%d", &(com->people[com->position].age));
	printf("请输入要添加联系人的电话:");
	scanf("%s", com->people[com->position].phone);
	printf("请输入要添加联系人的住址:");
	scanf("%s", com->people[com->position].address);
	printf("添加成功\n");
	com->position++;
}

这里和静态通讯录相比,我们把静态通讯录中是否已满换成一个函数,这个函数用来实现扩容操作的。
在这里插入图片描述

3.通讯录的销毁

void Destroy(Com* com)
{
	free(com->people);//释放为联系人结构体开辟的空间
	com->people = NULL;//把空间指向空
	com->max = 0;
	com->position = 0;
	return;
}

我们的销毁函数要在程序退出时调用。
至此,我们的动态通讯录就已经完成了。其他的和静态通讯录一模一样,其他无需变化。

三、通讯录的文件操作

1.通讯录的加载

void Loading(Com* com)//加载文件
{
	FILE* pf = fopen("test.txt", "rb");//以二进制方式读文件
	if (pf == NULL)
	{
		perror("read file open");
		return;
	}
	while (fread((com->people) + com->position, sizeof(People), 1, pf))//把文件读入我们的结构体中
	{
		com->position++;
		Expansion(com);
	}
	fclose(pf);
	pf == NULL;
	return;
}

我们把改函数放入到我们初始化函数的最后,当我们为我们结构体初始化后就可以把我们上次保存的信息加载进来了。
当我们第一次执行时:
在这里插入图片描述
这是我们现在还没有文件,当我们对本次操作进行保存时,下次使用就不会出现这种提示了。

2.通讯录的保存

void Save(Com* com)
{
	FILE* pf = fopen("test.txt", "wb");//以二进制方式写文件
	if (pf == NULL)
	{
		perror("write file open");
		return;
	}
	int i = 0;
	for (i = 0; i < com->position; i++)
	{
		fwrite((com->people) + i, sizeof(People), 1, pf);//把联系人结构体的内容写道文件中
	}
	fclose(pf);
	pf == NULL;
	return;
}

我们把改函数放入到我们销毁函数的开始,我们要在销毁我们通讯录之前进行保存。
![在这里插入图片描述](https://img-blog.csdnimg.cn/46b227c115f549ac8983387edbfd7b3b.png
这是我们文件保存的内容,我们使用的是二进制进行存储读写,所以里面的内容我们看不懂。
至此我们的文件版本也改造结束了。

总结

我们先实现的静态版本的通讯录,动态是在静态版本上做一点小小的改动,而文件版本又是在动态版本上加上一些内容。相信到这里你对顺序表的理解有进一步的加深,我们的通讯录也可以在加入一些其他的功能,我们做不出来是因为我们周边知识的缺失。像加入文件操作一样,当我们不知文件如何操作,就没办法改造我们动态的通讯录,随着我们学习的深入,还可以把这个通讯录改为网络版本。让我们一起进步吧。

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

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

相关文章

使用Debate Dynamics在知识图谱上进行推理(2020)7.31+8.1

使用Debate Dynamics在知识图谱上进行推理 摘要介绍背景与相关工作我们的方法状态行为环境policiesDebate Dynamics 摘要 我们提出了一种新的基于 Debate Dynamics 的知识图谱自动推理方法。 其主要思想是将三重分类任务定义为两个强化学习主体之间的辩论游戏&#xff0c;这两…

n位的二进制可以表示多少个小数?

文章目录 导论推理过程结论练习 导论 首先来思考一个十进制小数是如何被转化成二进制的。它使用的方式&#xff1a;乘基取整。你想把它化成n进制&#xff0c;基数就是n。以小数0.6875为例&#xff0c;将它化成二进制 那么它化成二进制0.1101。以原码或补码表示成01101。 并不…

Gis入门,使用起止点和两个控制点生成三阶贝塞尔曲线(共四个控制点,线段转曲线)

前言 本章讲解如何在gis地图中使用起止点和两个控制点(总共四个控制点)生成三阶贝塞尔曲线。 二阶贝塞尔曲线请参考上一章《Gis入门,如何根据起止点和一个控制点计算二阶贝塞尔曲线(共三个控制点)》 贝塞尔曲线(Bezier curve)介绍 贝塞尔曲线(Bezier curve)是一种…

用Delphi编写一个通用视频转换工具,让视频格式转换变得更简单

用Delphi编写的简单视频格式转换程序&#xff0c;它使用TComboBox、TOpenDialog和TSaveDialog组件来选择转换格式、选择源视频文件和选择目标视频文件。程序还使用TEdit组件允许用户输入参数&#xff0c;然后将这些组件中的信息拼接成转换命令并在DOS窗口中运行它。 procedure…

认识springboot 之 了解它的日志 -4

前言 本篇介绍springboot的日志&#xff0c;如何认识日志&#xff0c;如何进行日志持久化&#xff0c;通过日志级别判断信息&#xff0c;了解Lombok插件的使用&#xff0c;通过Lombok自带注释更简洁的来完成日志打印&#xff0c;如有错误&#xff0c;请在评论区指正&#xff0…

动手学深度学习—深度学习计算(层和块、参数管理、自定义层和读写文件)

目录 1. 层和块1.1 自定义块1.2 顺序块1.3 在前向传播函数中执行代码 2. 参数管理2.1 参数访问2.1.1 目标参数2.1.2 一次性访问所有参数2.1.3 从嵌套块收集参数 2.2 参数初始化2.2.1 内置初始化2.2.2 自定义初始化 2.3 参数绑定 3. 自定义层3.1 不带参数的层3.2 带参数的层 4. …

【PWN · 栈迁移】[BUUCTF][Black Watch 入群题]PWN

记一道栈迁移题目 前言 【PWN 栈迁移】[BUUCTF]ciscn_2019_es_2_Mr_Fmnwon的博客-CSDN博客 一、代码审计 总结信息如下&#xff1a; 1. 第12行存在栈溢出漏洞&#xff0c;刚好可以溢出覆盖到ebp和ret 2. 第9行可输入大量数据到bss段 二、思路过程 1.栈迁移 进能够覆盖e…

Nodejs 第八章(npm搭建私服)

构建npm私服 构建私服有什么收益吗&#xff1f; 可以离线使用&#xff0c;你可以将npm私服部署到内网集群&#xff0c;这样离线也可以访问私有的包。提高包的安全性&#xff0c;使用私有的npm仓库可以更好的管理你的包&#xff0c;避免在使用公共的npm包的时候出现漏洞。提高…

SAM(Segment Anything)大模型论文汇总

A Comprehensive Survey on Segment Anything Model for Vision and Beyond 论文&#xff1a;https://arxiv.org/abs/2305.08196 25页综述&#xff0c;198篇参考文献&#xff01;52个开源项目&#xff01;本文第一个全面回顾了分割一切模型(SAM)的研究和应用进展&#xff0c;…

AttributeError: module ‘PyQt5.QtGui‘ has no attribute ‘QMainWindow‘

场景描述&#xff1a; 这个问题是使用PyUIC将ui文件变成py文件后遇到的 解决办法&#xff1a; 改动1&#xff1a;把object改成QtWidgets.QMainWindow 改动2&#xff1a;增加__init__函数&#xff0c;函数结构如下&#xff1a; def __init__(self):super(Ui_MainWindow,self).…

vue03 es6中对数组的操作,vue对数据监控的原理(分别对对象和数组的监控)

在js中&#xff0c;对数组的操作一般都是固定的模式&#xff1a;常用的函数&#xff0c;具体的方法在这个文章中去看&#xff1a; http://t.csdn.cn/Fn1Ik 一般会用到的函数有&#xff1a; pop&#xff08;&#xff09; 这个函数是表示把数组中的元素&#xff08;数组&#xff…

Elasticsearch 全文检索 分词检索-Elasticsearch文章四

文章目录 官方文档地址refercence文档全文搜索体系match简单查询match 多词/分词单字段分词match多个词的逻辑控制match的匹配精度match_pharse_prefix分词前缀方式match_bool_prefixmulti_match多字段匹配 query string类型Interval类型DSL查询之Term详解聚合查询之Bucket聚合…

Java入门指南:Java语言优势及其特点

目录 1. Java语言简介及发展概述 2. Java语言的优势 2.1 可移植性 2.2 面向对象 2.3 安全性 2.4 大量类库 3. Java语言与C/C的区别 4. 初识Java程序入口之main方法 5. 注释、标识符、关键字 5.1 注释 5.2 标识符 5.3 关键字 1. Java语言简介及发展概述 Java是一种面…

天下风云出我辈,AI准独角兽实在智能获评“十大数字经济风云企业

时值盛夏&#xff0c;各地全力拼经济的氛围同样热火朝天。在浙江省经济强区余杭区这片创业热土上&#xff0c;人工智能助力数字经济建设正焕发出蓬勃生机。 7月28日&#xff0c;经专家评审、公开投票&#xff0c;由中共杭州市余杭区委组织部&#xff08;区委两新工委&#xff…

【Java】数据结构篇:经典链表OJ题 |超详细图解+代码

博主简介:努力学习的预备程序媛一枚~博主主页: @是瑶瑶子啦所属专栏: Java岛冒险记【从小白到大佬之路】文章目录 1、获取链表的中间节点⭐2、单链表的逆置⭐3、判定链表是否是回文⭐4、链表分割5、合并两个有序链表6、链表中倒数第k个结点⭐7、判定链表相交8、判断链表带环⭐…

15、两个Runner初始化器和 springboot创建非web应用

两个Runner初始化器 两个Runner初始化器——主要作用是对component组件来执行初始化 这里的Component组件我理解为是被Component注解修饰的类 Component //用这个注解修饰的类&#xff0c;意味着这个类是spring容器中的一个组件&#xff0c;springboot应用会自动加载该组件。 …

一文读懂浏览器本地存储:Web Storage

一、 简介 二、localStorage 和 sessionStorage 2.1、区别 2.2、浏览器兼容性 三、使用说明 3.1、API介绍 3.2、浏览器查看 3.3、监听 四、存储 4.1、存储容量 4.2、存储性能 五、应用 5.1、使用习惯记录 5.2、首次打开提示 5.3、减少重复访问接口 六、总结 一、 …

在 3ds Max 中使用相机映射将静止图像转换为实时素材

推荐&#xff1a; NSDT场景编辑器 助你快速搭建可二次开发的3D应用场景 1. 在 Photoshop 中准备图像 步骤 1 这是我将在教程中使用的静止图像。 这是我的静态相机纸箱的快照。 静止图像 步骤 2 打开 Photoshop。将图像导入 Photoshop。 打开 Photoshop 步骤 3 单击套索工…

计算机网络基础(静态路由,动态路由,公网IP,私网IP,NAT技术)

文章目录 一&#xff1a;静态路由和动态路由二&#xff1a;静态路由的配置路由信息的方式演示三&#xff1a;默认路由四&#xff1a;公网IP和私网IP和NAT技术的基本理解 一&#xff1a;静态路由和动态路由 在说静态路由和动态路由前&#xff0c;我们需要来了解一下&#xff0…

近2年入侵13家电信公司的幕后黑手浮出水面

10月20日&#xff0c;网络安全公司赛门铁克刚披露了一个针对南亚电信公司的神秘APT&#xff08;高级持续威胁&#xff09;组织&#xff0c;一个名为 LightBasin 的黑客组织被确定为针对电信行业发起一系列攻击的幕后黑手&#xff0c;其目标是从移动通信基础设施中收集“高度特定…