数据结构_顺序表专题

何为数据结构?

咱今天也来说道说道......

数据结构介绍

  • 准确概念

数据结构就是计算机存储、组织数据的方式

  • 概念分析

从上句分析,数据结构是一种方式。一种管理数据的方式。为了做什么?为的就是计算机存储数据,组织数据。

存储数据好理解,就是把数据导进计算机。

组织数据:组织二字:就好比将军带兵。计算机无疑就是将军,数据就是征入军营的士兵们,数据现在还没真正成为士兵,得靠将军对它们进行管理、操练。比如增加能人志士,淘汰不合格的士兵,平时点卯(对士兵进行点名),对士兵在刀剑、骑射方面进行能力的增强。

概念解释清楚了,再看一眼概念:相信同学们已经把概念刻进了DNA。

数据结构就是计算机存储、组织数据的方式

将军之所以能为将帅之才,那自然是依靠它们善带兵之道,识人善用——即针对不同基础的士兵,独有一套招纳、组织方式。

今日,我们就看“队伍”之一——名为:顺序表这支军队里面平时如何招纳、组织数据。

先来看看这支队伍里面的士兵是为何种数据?

数据篇—顺序表里的数据

开门见山,核心一句话:顺序表的底层就是数组

什么意思且听我一言:既然是军队,那必然有队列阵型,每一个人站位何处都是定好了的。总不能这立一个,那躺一个,无组织无纪律,这明显和数据结构所达目的背道而驰。

数组为何能成为顺序表的底层数组,是⼀组相同类型元素的集合。听听,集合二字,可见它的组织性;相同类型元素,则暗示它浑然天成能打造一支基本素质不低的军队。说白了,顺序表要的就是数组这种凝聚力。

  • 阵型排列

具体阵型,简单看看:

地址我随便意思了一下,重点是什么? 这个顺序表和数组一个样,数组里面的元素数据类型是int型,每个数据拥有4个字节的空间,在数组里元素和元素之间地址连续

好了,如果本段的解释,让你明白:顺序表的底层就是数组,这一小节儿的重点就把握了。

  • 数据类型

照例,先给出核心:顺序表里要求的数据的类型较为广泛,上到内置类型(比如:int,float,double),下到自定义类型(结构体),再到大杂烩(基于内置类型与自定义类型定义的各种数据,比如,int a[ ],char b[ ]。

可以说,数据类型在顺序表里不作重点,简单了解到类型涵盖广泛就足够。

结构篇—顺序表里存储、组织数据

先来看,顺序表是如何招纳(存储)数据的,俗话说“巧妇难为无米之炊”,先得有数据才能管理、组织数据。

存储数据

顺序表在招纳数据的时候,为了满足顺序表本身是数组的特点,数组结构要求有二:类型与大小。

类型我们在前面已讨论—每个数据类型统一且数据类型范围较为广泛。

那么大小呢?在建立“这支队伍”之初,将军似乎不能预估士兵的多少,符合要求的数据到底有多少我们也未知,后续还会需要新的数据,空间不够怎么办?

谁来决定数组的大小?似乎成了问题,“韩信点兵,多多益善”,随着将军带兵之道不断精进,能带善于带更多的士兵(需要的数据不断引进),我们要以数组的形式的排列数据,但是又得灵活地开辟空间,大小是无法一口气拍定的。所以,顺序表里使用动态内存开辟空间(动态数组)对数据进行存储

这里就开始写代码了,我们分成三个文件:SeqList.h(顺序表头文件),SeqList.c(顺序表函数实现文件)以及test.c(测试文件)

//头文件SeqList.h 包含顺序表类型的定义(即到底是一支由什么数据组成的,有多少数据,现在空间大小几何的数组) 以及相关组织数据方法声明typedef int SLDatatype;//此处int只为举例
struct SeqList
{SLDataType* arr;int size;//表示顺序表里有效数据个数int capacity;//表示当前顺序表里已开辟空间大小,表示现有capacity个数据的空间,为了后续开辟空间有依据可言
}
//既然是动态数组,我们无法在开辟数组空间无法得知需要的空间大小,但是又得用数组,我们使用指针(数组首元素地址即数组名),表示数组。同时,对于不同数组招募的数据不同,我们直接对int进行重命名,最后如果需要的数据类型不同,可以把typedef后的int改成所需的数据。 

 定义好顺序表的雏形后,可以预见,在今后代码创建顺序表时,我们的代码无疑就是创建一个结构体。

提醒一下“创建顺序表即创建一个结构体”,顺序表本质还是数组,实现出来的样子就是数组。只是在组织数据时需要size和capacity这个变量。

怎么理解呢? 好理解。 这军队里打仗上战场的是数组(军队);这把握局势,统揽全局,出谋划策、有助带兵的是谁?当然是军师(size和capacity)。在刚刚那张图里虽然看不见size和capacity,但是对于组织军队,起到重要作用的便是这俩。

所以定义顺序表,包含数组和重要的两个变量

//test.c文件int main()
{struct SeqList sl;//名字很长啊,为了方便定义,我们在顺序表定义对结构体重命名。return 0;
}

故最后的顺序表定义代码:

typedef int SLDataType;
//定义一个动态顺序表
typedef struct SeqList
{SLDataType* arr;int size;//数组里有效数据个数—元素个数int capacity;//数组当前大小
}SL;

综上,为了方便存储不同类型的数据,灵活地调整存储数据的空间,我们分别使用重命名typedef成SLDataType 和 动态开辟内存先只给一个指针表示数组和两个变量方便后续操作) 

组织数据

现在有了存储数据的方式,在没有新来数据时,我们得做些准备工作(初始化)。

一、顺序表(动态数组)的初始化 

//SeqlList.c void SLInit(SL sl)
{sl.arr = NULL;sl.size = sl.capacity = 0;
}//test.c来检验测试写的实现方法是否有bug#include "SeqList.h" 
int main()
{SL sl;//这里使用SeqList.h定义的类型,不包含就找不到这个顺序表,后续一些函数也无法调用SLInit(sl);return 0;
}

易错:传值调用和传址调用

经测试,如果这里传SL sl,会报错:使用了未初始化的局部变量sl

解:顺序表在SLInit方法里,形参拷贝了一份sl的值,但是传过去的sl本身就没有值,更不能实现拷贝值一说。这就是为何报错说使用了未初始化的局部变量。

再者,使用了这样一份拷贝的顺序表,在SLInit方法里就算初始化成功,最后被初始化的复印件也会被删除,再看原件,毫无变化——故也没能达到初始化我们原本创建顺序表的目的。

所以,使用传址调用,地址在计算机里,具有唯一性。从不同维度解决问题,精准找到对的顺序表,而不是长得像的,临时的顺序表。

故顺序表的初始化方法代码:

void SLInit(SL* psl)
{psl->arr = NULL;psl->capacity = psl->size = 0;
}

二、顺序表的销毁

顺序表的销毁,对应我们的将士带兵论即为,在必要情况下,将士将每位士兵遣散回家,这遣散过程还包括扎营驻寨的空间的归还,军师也得衣锦还乡不是?。

拢共三个方面,那代码实现:

void SLDestroy(SL* psl)
{if(psl->arr != NULL){//既然数组不为空,意味着存着这一支军队free(psl->arr);//可见数组的统一,不光组织起来方便,解散也方便}//数组为空||释放了数组后,说明数组已经不存在psl->arr = NULL;//这个数组已经不存在,不能再存地址了psl->size = psl->capacity = 0;//有效数据个数也为0,空间大小也为0}

释放顺序表,考察的是对于各个部分安排是否妥当,切记考虑全面。

三、顺序表的增删查改

这才是真正体现组织数据技术含量的部分,不同的数据结构对于实现:增删查改,有着其独到之处。顺序表的增删查改。

两种基本增加数据方法,头插和尾插。头插即在数组下标为0位置处插入我们安排的数据,原来的数据都往后面挪一位(这位新来的数据,可见实力不小,能排在首位,后面的都得挪窝) 

尾插即在原来数组的最后一位有效数字后添加我们安排的数据,原来的数据不用挪位置。(这位新来的数据被安排到最后一位)

这数据招进来,类型统一是统一,可别忘了顺序表

还有size和capacity两个变量。新来一个数据,size++就好,capacity呢?万一空间不够了就安排不进了,这就是在增之前,我们需要考虑的——是否需要此时动态开辟内存

  • 是否需要动态开辟内存 

代码:

void SLCheckCapacity(SL* psl)
{if(psl->size == psl->capacity)//“扩容”就在此刻{//说明有效数字已经满了数组已经开辟的空间,也满足刚开始size和capacity同为0的情况//扩容操作如下:SLDataType* tmp = realloc(psl->arr,2 * psl->capacity * sizeof(SLDataType));//这句为什么用reallo? 因为我们是扩容,realloc函数就是实现在原有空间上继续开辟空间,有增容的概念。第一个参数是你所指定的空间,第二个参数是你所申请开辟的空间大小,因为以存储空间(内存)以字节为单位,故第二个参数要乘上一个数据的字节大小,2*capacity意思是2倍:我们常常2倍增容,这样到后期增容速率会变慢,且2倍增容浪费空间数不会太多。//左边为什么用tmp? tmp(暂时的),realloc函数的返回类型是一个数组首地址(指针),因为申请空间可能不成功,一旦不成功,返回NULL,那原有的数据就会丢失,保险起见,先用一个tmp接收。if(tmp == NULL){// 意味着没申请成功perror("realloc:failed");//报错,提醒自己}     }
}

 realloc函数有些细节以及2倍增容真正数学原理,我没细说,不过这里的解释倒是够用。有兴趣的同学可以去详细了解。

void SLCheckCapacity(SL* psl)
{if(psl->size == psl->capacity){SLDataType* tmp = realloc(psl->arr,2 * psl->capacity * sizeof(SLDataType)); //注意这里有个坑,先往后看,马上就说if(tmp == NULL){// 意味着没申请成功perror("realloc:failed");//报错,提醒自己}else{// 申请成功了psl->arr = tmp;psl->capacity = 2 * psl->capacity;}}}

 易错:psl->capacity==0时,开辟了0个空间

所以以后写一长串参数运算时,注意一下参数为0的情况。

真正的代码实现:

void SLCheckCapacity(SL* psl)
{if(psl->size == psl->capacity){int NewCapacity = psl->capacity == 0 ? 4 : 2 * psl->capacity//创建一个新变量,判断原空间大小:如果为0,默认给它4个空间成为新空间,如果不为0,则在原有基础上2倍成为新空间数SLDataType* tmp = realloc(psl->arr,NewCapacity * sizeof(SLDataType)); if(tmp == NULL){// 意味着没申请成功perror("realloc:failed");//报错,提醒自己}else{// 申请成功了psl->arr = tmp;psl->capacity = NewCapacity;}}}
  • 头插

判断完空间需要扩容后,现在的空间大小已经满足要求。我们开始实现插入数据,最后再size++。

先来看头插:根据刚刚的描述,安排首位为新数据,后面的都要向后移一位。真正逻辑是,先有得首位空了,才能安排新数据;但是要让首位空,所有数据都得先后移一位。故先循环,让后面的数据先移位,循环结束后,下标为0的元素就赋上新数据,再size++;

void SLPushFront(SL* psl,SLDataType x)
{assert(psl);//断言:文章最后一部分有解释SLCheckCapacity(psl);for(int i = psl->size;i ;i--)//移位得从后往前,好好想想:如果从前往后移,首先0下标数字一移就把下标为1的数字就覆盖了,这一改就再也找不到了。及时止损,换个思路:从后往前挪,后面的先后挪总会留下一个空位,画个图就明白了{psl->arr[i] = psl->arr[i - 1];//循环的最后,就是arr[1] = arr[0];由此得出i的取值范围:i > 0}psl->arr[0] = x;psl->size++;
}
  •  尾插

代码思路:先照例检查是否有足够空间,再尾插,这个尾插,插在有效数字的最后,不用移位那也用不上for循环。核心就一句arr[ ] = x

void SLPushBack(SL* psl,SLDataType x)
{assert(psl);SLCheckCapacity(psl);psl->arr[] = x;psl->size++;
}

 那【】中到底是什么?

 size本来记录的就是数组里面有效的数字,还是随着数组元素增加依次增加,数组下标从0开始,到size下标时,刚好对应没有数组元素。

补全合并代码,最终的尾插方法:

void SLPushBack(SL* psl,SLDataType x)
{assert(psl);SLCheckCapacity(psl);psl->arr[psl->size++] = x;//size后++,对于安排数字不影响,优雅
}

和“增”一样,删这种组织数据的方法,也分头删和尾删  

  • 头删 

模拟一下头删场景,借此来窥探代码思路。

头删,头就是下标为0的元素。

 

“好图不嫌多用”,我们在删除此图的26时,最后达成的效果是

对比很鲜明啊,最明显的也是在刚开始写代码容易忽略的就是size--。下标为1及后面的元素都往前了一位。无疑是有循环的,被我们忽略的还有一个26,我们需要单独对它做什么吗?似乎看来,所有数据覆盖后,就达成了这个效果,单独操作不合理也费劲

代码:

void SLPopFront(SL* psl)
{assert(psl);for(int i = 0;i < psl->size - 1;i++){psl->arr[i] = psl->arr[i + 1];//最后一次就是arr[size - 2] = arr[size - 1];由此确定i < size - 1(看移动前那张图分析)}psl->size--;
}
  •  尾删

 同样模拟一下尾删场景,借此来摸索代码思路。

尾删,就是删掉当前数组的最后一位有效数字。在这里就是3

删完后,就是这个效果:

无疑size--,元素并没有发生移位,不用for循环。

代码:

void SLPopBack(SL* psl)
{assert(psl);psl->size--;//没错,size--就可以,不用对3进行什么操作
}
//为什么?size--后,我们在增加数字时,(头插、尾插),3都会被覆盖,不影响;在删除数字时(头删,我们for循环也只会关心size下标内的数据,3不在有效范围内,不影响;尾删不影响,继续size--),也只关心size内的数据。查找,修改数据更是,不然我们刚开始为何要定义一个变量叫当前数组的有效个数,因为我们只关心下标size之前的数据。
 查

我们这里的查,是根据已给的数据在原数组里判断是否存在。

思路很简单,将数组元素遍历,每个元素与x比较,如果相等就找到了;反之,循环结束后若还没找到,说明当前数组不存在该数据

代码:

int FindByX(SL* psl,SLDataType x)
{assert(psl);for(int i = 0;i < psl->size;i++){if(psl->arr[i] == x){return i;//这里先不考虑x有多个的情况,先掌握思路}}return -1;//都直到循环结束,也没找到对应的下标,说明数组里面是没有x这个元素的,故返回一个无效下标}//test.cint main()
{//...// 测试“查”方法int find = FindByX(psl,x)if(find < 0){printf("没找到\n");}else{printf("找到了,下标为%d\n",find);}//... return 0;
}

两种情况:一种指定下标更改:一步到位arr[指定下标] = x;

还有一种情况,改数组里面的某个数字,那第一步得找到那个数字的对应下标不是?再加上arr[find] = x;

这个没什么坑,直接上代码:

void SLModify(SL* psl,SLDataType x,SLDataType Newx)//为了方便,在参数里面我直接规定了新数据,其实还可以用scanf通过键盘录入新数据
{assert(psl);int find = FindByX(psl,x);if(find < 0){}else{psl->arr[find] =  Newx;
}}//情况二
void SLModify2(SL* psl,int pos,SLDataType Newx)
{psl->arr[pos] = Newx
}

四、特殊的增和删

在增加或者删除数据时,我们只介绍了头增和尾增、头删和尾删;不具有增加的任意性:万一所增的数据偏偏非头非尾,所删的数据也在中间某一个位置。

顺序表的指定位置之前插入数据

我们还是通过那张“好图”来模拟场景实现,借此摸索代码思路。

给出这样一个顺序表,现在我们要在数组下标为pos的地方即“1”之前增加一个数据“55”。最后这个顺序表长这样:

上下对比,先看数组外,变量size++了(增加一个数据,有效数据个数可不就得++),再来看数组内元素是否发生移位:发生了,发生的范围在pos及pos后面的所有数据,arr[pos] = 1数据都发生了移位;我们因此能确定使用for循环;并且i的起点应该是pos。

按照之前的理解,思路都是先移位,让arr[pos]空出来,最后arr[pos] = 55,size++,同理可得代码:

void SLInsert(SL* psl,int pos,SLDataType x)
{assert(psl);for(int i = size;i > pos;i--){psl->arr[i] = psl->arr[i - 1];//可以看到这里仍然是从后向前移动数据,循环结束后达到在目的位置为空}pal->arr[pos] = x;psl->size++;
}
//发现没,与头插代码相比,变的就是循环的元素个数(与pos有关)

你会发现,这个方法里,一旦pos==0 就是我们写的头插,pos==size就是我们写的尾插;简而言之,我们对下标任意化了,这就是前文所说的任意性。

顺序表的指定位置删除数据

同样,数据结构要的就是一个代码“跃然纸上”的效果。

这次我们要删除arr[pos] = 1这个数据了。最后的效果:

size变没有?变了——size--;元素移位没有?移了,for循环。有了pos,可以确定循环变量的范围。

代码实现:

void SLDel(SL* psl,int pos)
{assert(psl);for(int i = pos;i < psl->size - 1;i++){psl->arr[i] = psl->arr[i - 1];//最后一步是arr[size - 2] = arr[size - 1]}psl->size--;
}

 你会发现,这个方法里,一旦pos==0 就是我们写的头删,pos==size就是我们写的尾删;简而言之,我们对下标任意化了,这也对应前文所说的任意性。

代码优化——断言处理:

参数只说了传指针,没说不能传空。

比如:

调试结果:

 究竟原因其实是:对空指针进行解引用,为何不能对空指针进行解引用:操作系统和硬件通常不会将物理内存地址0分配给任何实际的内存块,因此尝试访问这个地址会导致硬件异常,比如段错误或访问违规。操作系统捕获到这种异常后,通常会终止程序的执行。

空指针往往被分配的是NULL代表着0x00,也同时意味着这是个抽象地址,既然底层没有任何物理内存支持,尝试访问是违法的,况且里面也不能存储什么有效数据。(我知道有些小伙伴会忘记之前学过的指针知识,特意再详说)

其实在传的参数为指针时,当用户传来无效指针(空指针)野指针,代码里一旦对空指针解引用(例如:psl->arr[i],psl->size,其中psl是NULL,而->是对指针的解引用),程序就直接崩溃了;为了提高代码的健壮性,我们在函数的第一步对传过来的指针进行断言assert(指针),如果为空,程序会直接终止,报错;并提醒我们发生何处断言错误。——可见,检验传参(指针)有效性,是很有必要的。

总结

Q&A: 数据结构是做什么的? 计算机存储、组织数据的方式

            为什么有顺序表?为了方便管理一堆相同类型、数量且不一定的数据。

            方便体现在何处?动态数组和一些现成(现在方法都得自己敲出来实现,才能叫现成)的“增删查改”方法;与以前定长的数组相比,简单灵活得多

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

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

相关文章

元器件基础学习笔记——磁珠

一、磁珠的作用及构造 1.1 磁珠的作用 磁珠是一种用于抑制高频噪声的被动电子组件&#xff0c;通常由铁氧体材料制成&#xff0c;这种材料具有高电阻率和高磁导率&#xff0c;使其能够在高频下有效地将干扰信号以热能的形式消耗掉。在电路设计中&#xff0c;磁珠被广泛用于信号…

LeetCode 441, 57, 79

目录 441. 排列硬币题目链接标签思路代码 57. 插入区间题目链接标签思路两个区间的情况对每个区间的处理最终的处理 代码 79. 单词搜索题目链接标签原理思路代码 优化思路代码 441. 排列硬币 题目链接 441. 排列硬币 标签 数学 二分查找 思路 由于本题所返回的 答案在区间…

3d复制的模型怎么渲染不出来?----模大狮模型网

在展览3D模型设计领域&#xff0c;技术的进步和创新使得模型的复杂性和精细度有了显著提升。然而&#xff0c;有时设计师们在尝试渲染复杂的3D复制模型时&#xff0c;却面临着无法正确呈现的问题。模大狮将探讨这一现象的可能原因&#xff0c;并提供相应的解决方案和建议&#…

知识图谱和 LLM:利用Neo4j驾驭大型语言模型(探索真实用例)

这是关于 Neo4j 的 NaLLM 项目的一篇博客文章。这个项目是为了探索、开发和展示这些 LLM 与 Neo4j 结合的实际用途。 2023 年,ChatGPT 等大型语言模型 (LLM) 因其理解和生成类似人类的文本的能力而风靡全球。它们能够适应不同的对话环境、回答各种主题的问题,甚至模拟创意写…

3d导入模型后墙体变成黑色?---模大狮模型网

在展览3D模型设计领域&#xff0c;技术和设计的融合通常是创意和实现之间的桥梁。然而&#xff0c;有时设计师们会遇到一些技术上的挑战&#xff0c;如导入3D模型后&#xff0c;墙体却突然变成了黑色。这种问题不仅影响了设计的视觉效果&#xff0c;也反映了技术应用中的一些复…

【UE5.3】笔记10-时间轴的使用

时间轴 右键--Add Timeline(在最下面) --> 双击进入时间轴的编辑界面&#xff1a; 左上角可以添加不同类型的轨道&#xff0c;可以自定义轨道的长度&#xff0c;单位秒&#xff0c;一次可以添加多个 可以通过右键添加关键帧&#xff0c;快捷键&#xff1a;shift鼠标左键按…

C++基础语法:链表和数据结构

前言 "打牢基础,万事不愁" .C的基础语法的学习 引入 链表是最基础的数据集合,对标数组.数组是固定长度,随机访问,链表是非固定长度,不能随机访问.数组查找快,插入慢;链表是插入快,查找慢. 前面推导过"数据结构算法数据集合".想建立一个数据集合,就要设计数…

鸿蒙 画布来了 我不允许你不会

前言: 作者:徐庆 团队:坚果派 公众号:“大前端之旅” 润开鸿生态技术专家,华为HDE,CSDN博客专家,CSDN超级个体,CSDN特邀嘉宾,InfoQ签约作者,OpenHarmony布道师,电子发烧友专家博客,51CTO博客专家,擅长HarmonyOS/OpenHarmony应用开发、熟悉服务卡片开发。欢迎合作…

WPF+MvvmLight 项目入门完整教程(一)

WPF+MvvmLight入门完整教程一 创建项目MvvmLight框架安装完善整个项目的目录结构创建自定义的字体资源下载更新和使用字体资源创建项目 打开VS2022,点击创建新项目,选择**WPF应用(.NET Framework)** 创建一个名称为 CommonProject_DeskTop 的项目,如下图所示:MvvmLight框架…

OpenCV开发笔记(七十八):在ubuntu上搭建opencv+python开发环境以及匹配识别Demo

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/140435870 长沙红胖子Qt&#xff08;长沙创微智科&#xff09;博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV…

【Nuxt3】vue3+tailwindcss+vuetify引入自定义字体样式

一、目的 在项目中引入自定义的字体样式&#xff08;全局页面都可使用&#xff09; 二、步骤 1、下载好字体 字体的后缀可以是ttf、otf、woff、eot或者svg&#xff08;推荐前三种&#xff09; 以抖音字体为例下载好放在静态文件夹&#xff08;font&#xff09;下 案例字…

【论文阅读】《Visual Prompt Tuning》

Abstract. 目前调整预训练模型的工作方式包括更新所有骨干参数&#xff0c;即全面微调。本文介绍了视觉提示调整&#xff08;VPT&#xff09;&#xff0c;作为大规模视觉变换器模型全面微调的高效替代方案。VPT 从高效调整大型语言模型的最新进展中汲取灵感&#xff0c;只在输…

软件测试——面试八股文(入门篇)

今天给大家分享软件测试面试题入门篇&#xff0c;看看大家能答对几题 一、 请你说一说测试用例的边界 参考回答&#xff1a; 边界值分析法就是对输入或输出的边界值进行测试的一种黑盒测试方法。通常边界值分析法是作为对等价类划分法的补充&#xff0c;这种情况下&#xff…

IAR全面支持芯驰科技E3系列车规MCU产品E3119/E3118

中国上海&#xff0c;2024年7月11日 — 全球领先的嵌入式系统开发软件解决方案供应商IAR与全场景智能车芯引领者芯驰科技宣布进一步扩大合作&#xff0c;最新版IAR Embedded Workbench for Arm已全面支持芯驰科技的E3119/E3118车规级MCU产品。IAR与芯驰科技有着悠久的合作历史&…

线程池及其底层工作原理

一、线程池是什么 线程池就是事先将多个线程对象放到一个容器中&#xff0c;当使用的时候就不用 new 线程而是直接去池中拿线程即可&#xff0c;节省了开辟子线程的时间&#xff0c;提高的代码执行效率在 JDK 的 java.util.concurrent.Executors 中提供了生成多种线程池的静态方…

分页stater

自定义aop,以添加注解的方法为切入点&#xff0c;对目标方法做一层增强 PageXAop代码如下&#xff1a; package cn.smart.pagex.aop;import com.github.pagehelper.PageHelper; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.a…

漏洞复现 | Showdoc反序列化

非常简单的一个靶场 靶场地址&#xff1a;https://hack.zkaq.cn/ 打开靶场&#xff0c;弹出了这种登录框&#xff0c;这也成为了后面的一个坑点&#xff0c;记住这个登录框。 看到了注册功能&#xff0c;showdoc有注册功能我们就不用尝试前台SQL注入了&#xff0c;直接注册…

Verilog基础:简单标识符和转义标识符

相关阅读 Verilog基础https://blog.csdn.net/weixin_45791458/category_12263729.html?spm1001.2014.3001.5482 标识符(identifier)是一个为了引用而给一个对象起的名字。一个标识符可以是一个简单标识符&#xff0c;也可以是一个转义标识符。本文将对两者进行详细阐述。 简…

Nuxt.js 错误侦探:useError 组合函数

title: Nuxt.js 错误侦探&#xff1a;useError 组合函数 date: 2024/7/14 updated: 2024/7/14 author: cmdragon excerpt: 摘要&#xff1a;文章介绍Nuxt.js中的useError组合函数&#xff0c;用于统一处理客户端和服务器端的错误&#xff0c;提供statusCode、statusMessage和…

【C++】—— 初识C++

【C】—— 初识C 一、什么是 C二、C 的发展历史三、C 版本更新四、C 的重要性五、C 在工作领域中的运用六、C 书籍推荐&#xff1a; 一、什么是 C C语言 是结构化和模块化的语言&#xff0c;适合处理较小规模的程序。对于复杂的问题&#xff0c;规模较大的程序&#xff0c;需要…