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

news2025/1/4 17:39:57

目录

前言:

一、指针是什么:

        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/62113.html

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

相关文章

[附源码]计算机毕业设计基于Springboot的项目管理系统

项目运行 环境配置&#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…

【Autopsy数字取证篇】Autopsy案例更改时区

【Autopsy数字取证篇】Autopsy案例更改时区 Autopsy—【蘇小沐】 文章目录【Autopsy数字取证篇】Autopsy案例更改时区1.实验环境2.Autopsy下载安装&#xff08;一&#xff09;"添加数据源"处修改&#xff08;二&#xff09;"数据分析完成"处修改总结1.实验…

认证服务------功能实现逻辑

认证服务分为两种&#xff0c;一种是社交登录&#xff0c;一种是单点登录&#xff0c;都交给认证中心&#xff08;OAuth2.0&#xff09;来处理&#xff0c;用一个专门的服务来作这个认证中心&#xff0c;所有的登录注册都是由这个认证中心来处理的&#xff0c;由于注册要发短信…

【负荷预测】基于双向LSTM模型进行电力需求预测(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️❤️&#x1f4a5;&#x1f4a5;&#x1f4a5; &#x1f389;作者研究&#xff1a;&#x1f3c5;&#x1f3c5;&#x1f3c5;主要研究方向是电力系统和智能算法、机器学…

html大学生网站开发实践作业:传统文化网页设计题材【绒花6页】HTML+CSS+JavaScript

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

图解curator如何实现zookeeper分布式锁

文章目录一、前言二、curator分布式锁种类三、Zookeeper分布式锁概述1、Zookeeper分布式锁实现思路2、Zookeeper分布式锁解决的问题3、Zookeeper分布式锁优缺点&#xff1f;四、InterProcessMute实现分布式锁原理1、加锁流程&#xff08;acquire()方法&#xff09;0&#xff09…

【消息中间件】为什么选择RocketMQ及SpringBoot整合使用案例

目录 一、为什么选择RocketMQ 1、为什么是为什么选择RocketMQ 2、RocketMQ、ActiveMQ和Kafka之间的比较 2.1、对比1 2.2、对比2&#xff0c;接着上表 二、使用案例 1、引入依赖 2、编写启动类 3、编写application.yml配置文件 4、创建rocketmq文件夹 4.1、创建生产者…

YOLO算法创新改进系列项目汇总(入门级教程指南)

&#x1f680;&#x1f680;&#x1f680;——YOLO算法创新改进系列项目汇总——&#x1f384;&#x1f384;&#x1f384; &#x1f680; YOLO算法创新改进系列项目汇总 &#x1f384;&#x1f388; &#x1f340; 改进YOLOv5/YOLOv7——魔改YOLOv5/YOLOv7提升检测精度&#x…

Redis原理篇——内存回收

Redis是基于内存存储的&#xff0c;性能强。Redis的性能瓶颈也在于内存&#xff0c;但是单节点内存不宜过大&#xff0c;会影响持久化或主从同步性能。 通过配置文件来设置Redis的最大内存&#xff1a; 一、过期策略 Redis是键值类型的数据库&#xff0c;所有的key和value保…

MySQL高级语句(二)

一、VIEW&#xff08;视图&#xff09; 1、 概念 可以被当作是虚拟表或存储查询 视图跟表格的不同是&#xff0c;表格中有实际储存资料&#xff0c;而视图是建立在表格之上的一个架构&#xff0c;它本身并不实际储存资料。 临时表在用户退出或同数据库的连接断开后就自动消…

数据结构—链表

文章目录链表&#xff08;头插法、尾插法、单链表反转&#xff09;二分查找算法&#xff1a;哈夫曼编码构建链表insert()创建链表&#x1f447;【1】尾插法【2】头插法【3】遍历输出链表【4】输出链表的长度【5】查找链表上是否有该元素【6】指定位置插入数据链表经典面试题【1…

一文进阶什么是负载均衡,通俗易懂的全文解析

文章目录1 写在前面2 引言3 概念4 分类4.1 根据载体类型分类4.3 根据地域范围分类4.4 根据 OSI 网络模型分类4.5 对比(四层和七层)5 算法与实现5.1 随机法(Random)5.2 轮询法(Round Robin)5.3 加权轮询法(Weighted Round Robin)5.4 加权随机法(Weighted Random)5.5 最快响应速度…

从JDK8到JDK18,Java垃圾回收的详细解答

经历了数千次改进&#xff0c;Java 的垃圾回收在吞吐量、延迟和内存大小方面有了巨大的进步。 2014 年3 月 JDK 8 发布&#xff0c;自那以来 JDK 又连续发布了许多版本&#xff0c;直到今日的 JDK 18 是 Java 的第十个版本。借此机会&#xff0c;我们来回顾一下 HotSpot JVM 的…

python隶属关系图模型:基于模型的网络中密集重叠社区检测方法

隶属关系图模型 是一种生成模型&#xff0c;可通过社区联系产生网络。下图描述了一个社区隶属关系图和网络的示例&#xff08;图1&#xff09;。最近我们被客户要求撰写关于社区检测的研究报告&#xff0c;包括一些图形和统计输出。 图1.左&#xff1a;社区关系图&#xff08;圆…

ARM 汇编写启动代码之开 iCache

一、什么是 cache&#xff0c;有什么用 cache是一种内存&#xff0c;叫高速缓存。 从容量来说&#xff1a;CPU < 寄存器 < cache < DDR 从速度来说&#xff1a;CPU > 寄存器 > cache > DDRcache 的存在&#xff0c;是因为寄存器和 ddr 之间速度差异太大&a…

亚马逊云科技——云原生主题容器入门笔记

嗨&#xff0c;大家好&#xff0c;我是异星球的小怪同志 一个想法有点乱七八糟的小怪 如果觉得对你有帮助&#xff0c;请支持一波。 希望未来可以一起学习交流。 目录 一、容器入门课程 二、容器入门课堂笔记 1.容器背后的发展历史 2.区分容器与逻辑服务器的虚拟机 3.容…

号称 Java 圣经,Github 上爆火的 1058 页 JVM 全栈小册到底有什么魅力?

对于 JVM&#xff0c;我想大部分小伙伴都是要面试了才会去学&#xff0c;其余时间基本不会去看&#xff08;掐指一算&#xff0c;你们书架上面的深入理解 Java 虚拟机第三版应该都一层灰了吧【手动狗头】&#xff09;。但值得一说的是&#xff0c;当你工作多年之后&#xff0c;…

谷歌要完,百度也危了

文 | 天于刀刀当我们在抱怨搜索引擎的时候我们具体在说些什么&#xff1f;也许是饱受诟病的广告&#xff1f;或者是不合理的网页排序&#xff1f;又或是一种最直观的感觉——不好使。但是从来没有人抱怨过搜索引擎这一个模式。尽管这些年&#xff0c;也诞生一些诸如Magi这样让人…

Nginx配置实例-负载均衡

随着互联网信息的爆炸性增长&#xff0c;负载均衡&#xff08;load balance&#xff09;已经不再是一个很陌生的话题&#xff0c; 顾名思义&#xff0c;负载均衡即是将负载分摊到不同的服务单元&#xff0c;既保证服务的可用性&#xff0c;又保证响应 足够快&#xff0c;给用户…

Nginx安装搭建和环境准备教程(Ubantu)

本文以Ubantu18.08为例&#xff1a; 首先进入虚拟机中升级apt-get&#xff1a; apt-get update nginx进行安装&#xff1a; apt install nginx 使用命令查看nginx是否启动&#xff1a; systemctl status nginx nginx已经启动&#xff0c;可以到前端页面去访问是否真的已经启…