C语言第十三课:初阶指针

news2024/11/15 6:42:08

目录

前言:

一、指针是什么:

        1.那么指针到底是什么呢?

        2.内存中的数据存储原理:

        3.数据存储与指针使用实例:

        4.存储编址原理:

二、指针和指针类型:

        1.决定了指针的步长:

        2.决定了对指针进行解引用时的权限大小:

三、野指针:

        1.野指针的成因:

        ①.指针未初始化:

        ②.指针越界访问:

        ③.指针指向的空间被释放:

        2.如何规避野指针:

四、指针运算:

        1.指针+/-整数:

        2.指针-指针:

        3.指针的关系运算:

五、指针和数组:

六、二级指针:

七、指针数组:

八、总结:


前言:

        在前面的三篇文章中,我和各位小伙伴们一起细致的学习了各种常用操作符的用法、属性以及各种常见的错误等等相关知识点。在掌握了操作符的相关使用后,本文我们将要一起学习关于指针的新知识。

一、指针是什么:

        1.那么指针到底是什么呢?

        在这里,关于指针的理解有两个要点:

        1.指针是内存中一个最小单元的编号,也就是地址

        2.平时我们口中所说的指针,通常指的是指针变量,是用来存放内存地址的变量

        总结:指针就是地址口语中的指针指的是指针变量

        2.内存中的数据存储原理:

        我们的内存就如同下图,当我们使用内存进行存储时,存储空间被划分为一个一个的小的存储空间,在使用时按照我们的需求进行内存分配,而这个过程中被划分为最小储存单元的每一个存储单元的大小为一个字节,且每一个存储单元都按照顺序依次拥有自己的十六进制内存编号,即各个存储单元的存储地址

内存十六进制内存编号(地址)
一个字节0xFFFFFFFF
一个字节0xFFFFFFFE
一个字节0xFFFFFFFD

.

.

.

.

.

.

.

.

.

.

.

.

一个字节0x00000002
一个字节0x00000001
一个字节0x00000000

        3.数据存储与指针使用实例:

        例如下述代码:

#define _CRT_SECURE_NO_WARNINGS 1

#include<stdio.h>

int main()
{
	int a = 10;
    //定义整形变量a,在内存中的存储占用四个字节(连续的)的空间
	int* b = &a;
    //定义指针变量b,用取地址操作符&获取变量a的地址,并将其储存在指针变量b中
    //整型变量a的存储空间大小为四个字节,这里是取出其中的第一个字节地址放入
    //因为存储空间的分配是连续的,获取到第一个字节地址即可找到全部数据

	printf("变量 a 的    值    为:%d\n", a);
	printf("变量 a 的 存储地址 为:%p\n", b);
	return 0;
}

        我们看到我们的程序成功的打印出了变量a的值,也成功打印出了存储在指针变量b中的变量a的存储地址

        而这段代码的实现过程是这样的,当我们定义了一个整型变量a后,便在内存中为我们定义的整型变量分配了一块空间用于存储整型变量a的数据,而我们都知道,一个整型变量的大小是四个字节,也就是说此时在内存中为我们的整型变量a分配了一片大小为四个字节的空间用来存储数据。而后我们再使用取地址操作符获取到了整形变量a在内存中的存储空间的地址,要注意的是在这里使用取地址操作符&取出的是整型变量a的存储空间(共四个字节)中第一个字节的地址。接着我们定义了一个指针变量b接收并存储了我们获取到的地址,并最终将整形变量a与指针变量b中各自存储的数据打印出来:

内存十六进制内存编号(地址)
一个字节0xFFFFFFFF
一个字节0xFFFFFFFE
一个字节0xFFFFFFFD
.
.
.

.

.

.

整型变量

int a

第四字节0x0012FF43
第三字节0x0012FF42
第二字节0x0012FF41
第一字节0x0012FF40(指针变量存储)

.

.

.

.

.

.

一个字节0x00000002
一个字节0x00000001
一个字节0x00000000

        4.存储编址原理:

        经过上面实例的演示和讲解,我们就能理解内存中的存储与指针的使用原理了,我们也知道了指针变量就是用来存储地址的变量存储在指针变量中的值都将被作为地址进行处理)。那么在内存中我们的计算机到底是如何进行编址的呢?

        对于32位的机器,我们可以假设它有32根地址线,那么假设每根地址线在进行寻址时,都产生高电平(高电压)低电平(低电压),则每一位上都将是对应的1或0,这也就是计算机内存进行数据存储采用二进制的原理了。且此时32根地址线产生的地址就将是:

00000000 00000000 00000000 00000000

00000000 00000000 00000000 00000001

00000000 00000000 00000000 00000010

    ........         ........         ........         ........

 11111111  11111111   11111111   11111111

        共32位,且每一位上均为1或0两种可能,则在内存中将存在2^32个地址

        同时我们也都知道8比特位为1个字节,而每个地址32位又恰好是四个字节,即一个整型变量的大小。并且我们在第三课中讲解数据类型时为各位小伙伴们介绍过,整形int(分正负,有符号位)取值范围是-2147483648到2147483647,而这32位除去最前面的一位最作为符号位共有31位,每一位上有1或0两种可能,则共可以表示2^31个数字即共2147483648个,又恰好对应了0到2147483647的取值范围负值部分同样如此,对应了-1到-2147483648的2147483648个数字。它们相互印证,不谋而合。

        每一个地址标识一个字节,则我们就可以给2^32Byte4GB大小的空间进行编址:

2^32Byte = 2^32/1024KB = 2^32/1024^2MB = 2^32/1024^3GB = 4GB

        则使用同样的编码方式,在64位的机器上,有64根地址线,就可以对的空间进行编址。

        到这里我们就明白:

★在32位机器上,地址为32个0或1组成的二进制序列,则地址就需要32比特位即4个字节的空间来进行存储。即在32位机器中一个指针变量的大小为4个字节

★在64位机器上,地址为64个0或1组成的二进制序列,则地址就需要64比特位即8个字节的空间来进行存储。即在64位机器中一个指针变量的大小为8个字节

        而由这个原理所得出的结论也与我们之前所学习的相关知识相一致

        总结:

· 指针变量是用来存放地址的,地址是唯一标示一个内存单元的。

· 指针变量的大小在32位平台是4个字节,在64位平台是8个字节

二、指针和指针类型:

        我们都知道变量有许多中不同的类型,例如整形、浮点型等等,那么指针有没有类型之分呢?准确的来说:有的。

        不同类型的指针变量的定义方式相同,均为 type + *

#define _CRT_SECURE_NO_WARNINGS 1

#include<stdio.h>

int main()
{
	char* pc = NULL;
	int* pi = NULL;
	short* ps = NULL;
	long* pl = NULL;
	float* pf = NULL;
	double* pd = NULL;
	return 0;
}

        即在一般情况下,不同类型的指针变量用于存放不同类型数据的地址,但不管何种类型的指针变量内部存储的都是数据的地址。那么既然都是用来存放地址的,这么多不同种类的指针类型存在的意义是什么呢?

        其实不同类型的指针存在的意义主要是以下两点:

        1.决定了指针的步长:

        我们来看下面这段代码:

#define _CRT_SECURE_NO_WARNINGS 1

#include<stdio.h>

int main()
{
	int n = 10;
	int* pi = &n;
	char* pc = (char*)&n;
	//强制类型转换,n原本为int类型

	printf("int型 n 的存储地址为:%p\n", &n);
	printf("\n");

	printf(" pi 指向的存储地址为:%p\n", pi);
	printf("pi+1指向的存储地址为:%p\n", pi + 1);
	printf("\n");


	printf(" pc 指向的存储地址为:%p\n", pc);
	printf("pc+1指向的存储地址为:%p\n", pc + 1);
	return 0;
}

        在这段代码中,我们看到,整型指针 pi 和经过强制类型转换后的字符型指针 pc 都获取并存储了变量 n 的存储地址,在这一点上没有区别。但是我们还看到,将 pi 与 pc 分别+1后,即分别让两个指针各向后“走一步”时,它们所跳过的内存空间不同整型指针跳过了四个字节,而字符型指针只跳过了一个字节的空间

        这就是不同指针类型的第一个意义决定了指针的步长

        2.决定了对指针进行解引用时的权限大小:

        同样的我们结合代码实例来进行研究:

#define _CRT_SECURE_NO_WARNINGS 1

#include<stdio.h>

int main()
{
	int n = 0x11223344;
	int m = 0x11223344;
	//此处不是书写错误,开头的0x表示十六进制整型
	//int类型每次访问四个字节

	int* pi = &n;
	char* pc = (char*)&m;

	*pi = 0;
	//使用解引用操作符修改指针pi内存放地址对应地址内的数据
	printf("变量 n 的值为:%d\n", n);

	*pc = 0;
	//使用解引用操作符修改指针pc内存放地址对应地址内的数据
	printf("变量 m 的值为:%d\n", m);
	return 0;
}

        在这段代码中,指针 pi 为整型指针变量pc 为字符型指针变量变量n与变量m中存储的数据完全相同,均为十六进制的11223344,转换为十进制为287454020,接着我们让整型指针pi获取并存存储变量n的地址,让字符型指针获取并存储变量m的地址,然后我们通过使用解引用操作符,来修改不同类型指针变量 pi 与 pc 中存储的地址所对应地址内存储的数据。最后将被修改过的变量n与变量m的值打印出来进行反馈。我们观察到,n的值被修改为0而m则被修改为了287453952(十六进制的11223300),即整个int类型的四个字节中仅有一个字节44被修改为了00。所以我们就知道,不同的指针类型决定了在进行一次操作时所能操作的空间大小(字节数)

        这就是不同指针类型的另一个意义决定了对指针进行解引用时的权限大小

三、野指针:

        顾名思义,野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)。

        1.野指针的成因:

        ①.指针未初始化:

        指针未经初始化极易引发错误:

#define _CRT_SECURE_NO_WARNINGS 1

#include<stdio.h>

int main()
{
	int* p;
	//局部变量未初始化,默认为随机值
	*p = 20;
	return 0;
}

        ②.指针越界访问:

        当我们使用数组时,若未仔细考量在数组使用中的指针指向问题,很有可能会导致指针越界的访问:

#define _CRT_SECURE_NO_WARNINGS 1

#include<stdio.h>

int main()
{
	int arr[5] = { 0 };
	int* p = arr;
	int i = 0;
	for (i = 0; i <= 5; i++)
	{
		*(p + i) = i;
		//当指针指向的范围超出数组啊让人的范围时,p将变为野指针
	}
	return 0;
}

        ③.指针指向的空间被释放:

        这也是一类常见的野指针成因,由于牵扯到部分我们没有接触到过的知识,过这里仅做了解,将会在学习动态内存开辟时进行讲解

        2.如何规避野指针:

        我们都知道野指针危害巨大,会对我们的程序造成巨大的负面影响,那么我们在便携性程序时就应当多多注意,尽可能地避免要求指针的出,我们主要从以下五个方面入手:

        ①.使用前及时将指针初始化

        ②.使用数组时,时刻小心指针越界访问

        ③.指针指向空间被释放,应及时置空

        ④.避免返回局部变量的地址

        ⑤.指针使用之前应当检查其有效性

#define _CRT_SECURE_NO_WARNINGS 1

#include<stdio.h>

int main()
{
	int* p1 = NULL;
	int* p2 = NULL;
	//及时进行初始化
	int a = 10;
	int arr[5] = { 0 };
	p1 = &a;
	p2 = arr;
	int i = 0;
	for (i = 0; i < 5; i++)
	//使用数组时,时刻小心指针越界访问
	{
		*(p2++) = i;
		printf("数组元素 arr[%d] 的值为:%d\n", i, arr[i]);
	}
	if (p1 != NULL)
	//指针使用前检查其有效性
	{
		*p1 = 20;
	}
	printf("变量 a 的值为:%d\n", a);
	return 0;
}

        当每一个细节都注意到后,我们的程序成功且正确运行的可能性就大了许多:

四、指针运算:

        指针的运算主要可以分为三类:指针+/-整数指针-指针指针的关系运算

        1.指针+/-整数:

        我们再来看下面这一段代码:

#define _CRT_SECURE_NO_WARNINGS 1

#define N_VALUES 5

#include<stdio.h>

int main()
{
	float values[N_VALUES];
	float* vp;
	for (vp = &values[0]; vp < &values[N_VALUES];)
	{
		*vp++ = 0;
		//++的优先级高于*,且为后置++
		//这里的起到的作用即为将每个数组元素均置0
	}
	int i = 0;
	for (i = 0; i < N_VALUES; i++)
	{
		printf("数组元素 values[%d] 的值为:%d\n", i, values[i]);
	}
	return 0;
}

        在这段代码中,置零功能的每次循环中的*vp++,即表示在每次使用并执行后,使指针vp继续向后按照float类型的步长指向一步

        即指针+/-整数表示:该指针按照当前指针类型所对应的步长向后/前指向一(或多)步

        2.指针-指针:

        指针-指针有一个前提要求:两只真均指向同一块内存空间。

        有了这个前提,我们来看代码:

#define _CRT_SECURE_NO_WARNINGS 1

#define N_VALUES 5

#include<stdio.h>

int main()
{
	int arr[N_VALUES] = { 0 };
	int* p1 = &arr[0];
	int* p2 = &arr[4];
    //两指针指向同一块内存空间

	printf("指针p2 - 指针p1的值为:%d", p2 - p1);

	return 0;
}

        在这段代码中我们使指针p1与指针p2均指向了数组arr在内存中所占的空间,只不过指针p1指向数组arr的首元素地址,而p2则指向了数组arr的尾元素地址,并将两指针相减后的结果打印反馈给了我们:

        即指针-指针表示:得到的是两指针间的数据元素个数

        3.指针的关系运算:

        通过指针的关系运算,我们可以将我们的代码进行简化:

#define _CRT_SECURE_NO_WARNINGS 1

#define N_VALUES 5

#include<stdio.h>

int main()
{
	float values[N_VALUES];
	float* vp;

	for (vp = &values[N_VALUES]; vp > &values[0];)
	{
		*--vp = 0;
	}

	//通过指针的关系运算,可以将我们的代码简化为:
	for (vp = &values[N_VALUES - 1]; vp > &values[0]; vp--)
	{
		*vp = 0;
	}
	return 0;
}

        上述这段代码中两个循环语句的不同不仅仅在于指针进行减一操作的书写位置不同,更导致造成了需要对指针vp进行关系运算

        而实际上,这段代码在绝大部分编译器上都是可以顺利完成任务的,但我们在书写代码时还是应当尽可能的避免这样去书写,因为标准并不保证其可行

★标准规定:

        允许指向数组元素的指针指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。

五、指针和数组:

        为了研究指针与数组间的关系,我们来看一个例子:

#define _CRT_SECURE_NO_WARNINGS 1

#include<stdio.h>

int main()
{
	int arr[5] = { 1,2,3,4,5, };

	int* p1 = &arr;
	int* p2 = &arr[0];

	printf("  arr   指向的地址为:%p\n", arr);
	printf("&arr[0] 指向的地址为:%p\n", &arr[0]);
	printf("\n");

	printf("指针 p1 指向的地址为:%p\n", p1);
	printf("指针 p2 指向的地址为:%p\n", p2);
	return 0;
}

        运行结果显示,数组名与数组首元素地址是一样的,即数组名表示的就是数组首元素的地址(有两种特殊情况,在数组章节已经讲解过了):

        而既然可以把数组名作为地址存放到一个指针当中,那我们使用指针来访问数组元素就成为了可能:

#define _CRT_SECURE_NO_WARNINGS 1

#include<stdio.h>

int main()
{
	int arr[5] = { 1,2,3,4,5 };
	int* p = arr;
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("数组arr[%d]的存储地址为:%p <====> p+%d 指向的地址为:%p\n", i, &arr[i], i, p + i);
	}
	return 0;
}

        运行结果验证了我们的想法是正确可行的:

        所以上述示例中的 p+i计算的是数组arr下标为 i 的地址。那么我们就可以直接通过指针来访问数组

#define _CRT_SECURE_NO_WARNINGS 1

#include<stdio.h>

int main()
{
	int arr[5] = { 1,2,3,4,5 };
	int* p = arr;
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("数组元素 arr[%d] 的值为:%d\n", i, *(p + i));
	}
	return 0;
}

        最终结果是访问成功

六、二级指针:

        那我们还有一个问题,指针变量它也是变量,而变量都有自己的地址,那么指针变量的地址该存放在哪里?答案是:二级指针

#define _CRT_SECURE_NO_WARNINGS 1

#include<stdio.h>

int main()
{
	int a = 10;
	int* pa = &a;
	//整型指针pa中存放的即为变量a的地址

	int** pp = &pa;
	//二级指针pp中存放的即为指针变量pa的地址

	printf("变量 a 的值为:%d\n", **pp);
	return 0;
}

        运行结果:

        则对于二级指针的运算还有:

#define _CRT_SECURE_NO_WARNINGS 1

#include<stdio.h>

int main()
{
	int b = 20;
	int* pb = &b;
	printf("*p  指向空间内存储的值为:%d\n", *pb);
	int** pp = &pb;
	printf("**pp指向空间内存储的值为:%d\n", **pp);

	**pp = 50;
    //等价于*pa=50
	//等价于a=50

	printf("变  量   b    的  值  为:%d\n", b);
	return 0;
}

        其中 **pp 先通过 *pp 找到 pb,然后再对 pb 进行解引用操作即 *pb找到了 a,最终对a进行了重新赋值操作:

七、指针数组:

        在数组章节中,我们已经知道了存在着整型数组、字符数组等等,那指针数组又是怎样的呢?指针数组究竟是指针还是数组呢?答案是:是数组

        指针数组的本质仍为数组,它是用来存放指针的数组

#define _CRT_SECURE_NO_WARNINGS 1

#include<stdio.h>

int main()
{
	int a = 0;
	int b = 10;
	int c = 20;
	int d = 30;
	int e = 40;
	//定义五个0变量

	int* arr[5] = { &a,&b,&c,&d,&e };
	//定义指针数组

	//使用指针数组进行打印:
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf("数组元素arr[%d]中指针指向的空间存放的值为:%2d\n", i, *arr[i]);
	}
	return 0;
}

        运行结果:

        并且在这里我们千万千万要特别注意的是,指针数组中存放的各地址不一定是连续的!同样的我们写出代码,用事实来进行验证:

#define _CRT_SECURE_NO_WARNINGS 1

#include<stdio.h>

int main()
{
	int a = 0;
	int b = 10;
	int c = 20;
	int d = 30;
	int e = 40;
	//定义五个变量

	int* arr[5] = { &a,&b,&c,&d,&e };
	//定义指针数组

	//使用指针数组进行打印:
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf("数组元素arr[%d]的存储地址为:%p\n", i, arr[i]);
	}
	return 0;
}

        由于指针数组内存放的是一系列的地址,而各地址又取决于各个常/变量在创建时内存进行的分配,所以指针数组内部存放的地址不一定是连续的

八、总结:

        至此,关于指针的介绍就结束啦,指针在我们进行内存访问时经常会用到,因此各位小伙伴们在课余时间最好能够多多练习,熟练的掌握关于指针的相关知识,争取能够在编写程序时驾轻就熟的使用指针工具,为自己的代码写作加油助力。

        不知道各位小伙伴们从今天的文章中又学到了哪些知识呢?有没有哪怕一丝的进步呢?哪怕只进步一点点,今天的你也是成功的,而非碌碌无为。要么你去驾驭生命,要么是生命驾驭你,你的心态决定谁是坐骑,谁是骑师,心态的不同必然导致人格和作为的不同,因而也会谱写不同的人生!

        新人初来乍到,辛苦各位小伙伴们动动小手,三连走一走 ~ ~ ~  最后,本文仍有许多不足之处,欢迎各位看官老爷随时私信批评指正!

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

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

相关文章

【VSCode + Anaconda】VSCode [WinError 126]找不到指定模块

【VSCode Anaconda】VSCode [WinError 126]找不到指定模块问题解决一解决二问题 在 Anaconda Prompt 中的 python 环境测试&#xff0c;可以使用 import torch 命令 现在在 VSCode 中测试&#xff0c;发现相关异常 图中&#xff0c;已经选择了相应的 conda 环境的 python.exe…

分片集群中的分片集合

分片集群中的分片集合 MongoDB 中 分片集群有专门推荐的模式&#xff0c;例如 分片集合 它是一种基于分片键的逻辑对文档进行分组&#xff0c;分片键的选择对分片是非常重要的&#xff0c;分片键一旦确定&#xff0c;MongoDB 对数据的分片对应用是透明的 mongodb 分片中&#…

MySQL高级语句(三)

一、正则表达式&#xff08;REGEXP&#xff09; 1、正则表达式匹配符 字符解释举列^匹配文本的开始字符’ ^aa ’ 匹配以 aa 开头的字符串$匹配文本的结束字符’ aa$ ’ 匹配以aa结尾的字符串.匹配任何单个字符’ a.b 匹配任何a和b之间有一个字符的字符串*匹配零个或多个在它…

数据结构—树、有序二叉树

文章目录树的概述树的分类二叉树的遍历有序二叉树代码通过链表方式构建有序二叉树通过递归方式实现有序二叉树递归遍历有序二叉树中序遍历&#xff1a;先序遍历&#xff1a;后序遍历&#xff1a;删除节点1、删除叶子节点删除叶子节点总结图示2、删除只有一个子树的节点删除只有…

毕业设计-基于深度学习火灾烟雾检测识别系统-yolo

前言 &#x1f4c5;大四是整个大学期间最忙碌的时光,一边要忙着准备考研,考公,考教资或者实习为毕业后面临的就业升学做准备,一边要为毕业设计耗费大量精力。近几年各个学校要求的毕设项目越来越难,有不少课题是研究生级别难度的,对本科同学来说是充满挑战。为帮助大家顺利通过…

Spring循环依赖源码解析(深度理解)

文章目录前言本章目标一、什么是循环依赖&#xff1f;1、那么循环依赖是个问题吗&#xff1f;2、但是在Spring中循环依赖就是一个问题了&#xff0c;为什么&#xff1f;二、Bean的生命周期2.1、在Spring中&#xff0c;Bean是如何生成的&#xff1f;2.2、那么这个注入过程是怎样…

GitLab CI/CD系列教程(一)

来自&#xff1a;GitLab CI/CD系列教程&#xff08;一&#xff09;&#xff1a;Docker安装GitLab_哔哩哔哩_bilibili 1. 创建虚拟机并连接Xterm 创建一个4G内存的虚拟机&#xff0c;否则很容易启动不了&#xff0c;报502 虚拟机的创建看这篇&#xff1a; VMware16的安装及VM…

基于java+ssm+vue+mysql的网上书店

项目介绍 本网上系统是针对目前网上的实际需求&#xff0c;从实际工作出发&#xff0c;对过去的网上系统存在的问题进行分析&#xff0c;结合计算机系统的结构、概念、模型、原理、方法&#xff0c;在计算机各种优势的情况下&#xff0c;采用目前最流行的B/S结构和java中流行的…

从0开始搭建vue2管理后台基础模板

网站主要完成&#xff1a;侧边菜单栏、页面标签卡、内容栏 源代码gitee地址&#xff1a;https://gitee.com/zhao_liangliang1997/navigation-bar 一、起步 1、创建vue项目 vue create 项目名2、引入element 3、其他安装 1、首先需要安装如下 cnpm install vuex cnpm install…

DockerCompose安装、使用 及 微服务部署实操

1 什么是DockerCompose DockerCompose是基于Compose文件帮助我们快速的部署分布式应用。 解决容器需手动一个个创建和运行的问题&#xff01; DockerCompose本质上也是一个文本文件&#xff0c;其通过指令定义集群中的每个容器如何运行。我们可以将其看做是将多个docker run…

Ansible 自动化运维工具的使用

目录 一、Ansible简介 二、Ansible 的安装和使用 1.下载 2.使用 三、Ansible命令和模块 1.命令格式 2.命令行模块 &#xff08;1&#xff09;command 模块 &#xff08;2&#xff09;shell 模块 &#xff08;3&#xff09;cron 模块 &#xff08;4&#xff09;user …

多线程 3

多线程 3 : 文章目录1.线程安全2. 产生线程安全的原因3. synchronized - 加锁操作4.可重入5.死锁问题6. volatile 关键字7.wait 和 notify1.线程安全 为啥会出现线程安全 &#xff1f;   罪魁祸首&#xff0c;还是多线程的抢占式执行&#xff0c; 正因为抢占式执行&#xff0c…

Java项目:SSM场地预订管理系统

作者主页&#xff1a;源码空间站2022 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文末获取源码 项目介绍 本项目分为前后台&#xff0c;前台为普通用户登录&#xff0c;后台为管理员登录&#xff1b; 用户角色包含以下功能&#xff1a; 按分类查看场…

【车载开发系列】UDS诊断---通信控制($0x28)

【车载开发系列】UDS诊断—通信控制&#xff08;$0x28&#xff09; UDS诊断---通信控制&#xff08;$0x28&#xff09;【车载开发系列】UDS诊断---通信控制&#xff08;$0x28&#xff09;一.概念定义二.实现原理三.应用场景四.子功能五.报文格式1&#xff09;请求报文2&#xf…

自动导入指定文件夹内的文献到 Endnote 中

简介 最近正着手写一篇综述文章&#xff0c;来整体把握下自己研究领域的历史、方法、最新进展与趋势。由于需要对相关文献进行搜集、阅读和分类。庄小编使用 EndNote 来进行管理文献。 在使用较长时间后&#xff0c;整理了几个超级好用的小技巧。比如&#xff1a;自动导入指定…

pikachu靶场-upload-速通

upload-速通client checkMIME typegetimagesizeclient check 最简单的&#xff0c;先上传一张含有一句话木马的图片&#xff0c;抓包修改图片后缀为php&#xff0c;放包发送就行 访问并确认该上传文件是否以php形式解析 蚁剑直连&#xff1a; MIME type 后端php检查上传文…

基于MSER的高速公路交通标志提取matlab仿真

目录 1.算法描述 2.仿真效果预览 3.MATLAB核心程序 4.完整MATLAB 1.算法描述 自然场景下的文本检测是自然场景图像信息提取的基础,在车牌识别、实时翻译、图像检索等领域具有广泛的应用价值及研究意义。基于连通区域的方法是自然场景文本检测中最为常见的方法,其中最大稳定…

[附源码]Python计算机毕业设计SSM街舞公司管理系统(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Java Script 内置对象(三) --------- Array 对象

判断是否为数组有两种方式&#xff0c;instanceof 和 Array.isArray( 参数 )&#xff0c;两者判断方法均为如果是数组则返回 true&#xff0c;不是数组则返回 **false&#xff0c;**其中第二个方法为H5新增加的方法 var arr[]; var obj{}; console.log(arr instanceof Arra…

微服务入门案例

boot与cloud版本 springboot:提供了快速开发微服务的能力 springcloud提供了微服务治理的能力&#xff08;服务注册与发现、服务降级、限流、熔断、网关、负载均衡、配置中心...&#xff09;&#xff0c;为微服务开发提供了全家桶服务 springboot的版本查看地址&#xff1a;Spr…