C语言初阶(6) - 指针

目录

1.指针是什么?

2. 指针和指针类型

2.1 指针 +- 整数

2.2 指针的解引用

3. 野指针

3.1 野指针成因

3.2 如何规避野指针

4. 常量指针和指针常量 (const)

4.1.常量指针

4.2.指针常量

5. 指针运算

5.1 指针+-整数

5.2 指针-指针

5.3指针的关系运算

6. 指针和数组

7.二级指针

8.指针数组


1.指针是什么?

理解指针的两个要点:
1. 指针是内存中一个最小单元的编号,也就是地址。
2. 平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量。
总结:指针就是地址,口语中说的指针通常指的是指针变量

我们可以理解:

内存单元 -> 编号 -> 地址 -> 指针

int a = 10;

如果VS2019中的地址存储的内容如下:

地址: 0x032FFB24 -> 地址里面存放的数据(0a 00 00 00)

说明此VS2019是按照小端字节序存储的

//00000000 00000000 00000000 00000101

//     00              00             00             0a

也可以这样理解地址的产生:

32位CPU - 32根地址线 - 物理的电线 - 通电 - 1/0  (产生32个数)

64位CPU - 64根地址线 - 物理的电线 - 通电 - 1/0  (产生64个数)

      对CPU来说,他执行到某个特定的编码数值,就会执行特定的操作。比如2+3,其实就是通过总线把数据2和3从内存里读入,然后放到寄存器上,再用加法器相加这两个数值并将结果放入到寄存器里,最后将这个数值回写到内存中,以此循环往复,一行行执行机器码直到退出。

指针变量
我们可以通过&(取地址操作符)取出变量的内存起始地址,把地址可以存放到一个变量中,这个变量就是 指针变量.
#include <stdio.h>
int main()
{int a = 10;//在内存中开辟一块空间int* p = &a;//这里我们对变量a,取出它的地址,可以使用&操作符。//a变量占用4个字节的空间,这里是将a的4个字节的第一个字节的地址//存放在p变量中,p就是一个之指针变量。*p = 1;printf("%d\n", a);return 0;
}
原来a的值是10,通过指针变量pa,解引用也可以改变a的值。
总结:
指针变量,用来存放地址的变量。(存放在指针中的值都被当成地址处理)。
那这里的问题是:
  • 一个小的单元到底是多大?(1个字节)
  • 如何编址?
经过仔细的计算和权衡我们发现一个字节给一个对应的地址是比较合适的。
对于32位的机器,假设有32根地址线,那么假设每根地址线在寻址的时候产生高电平(高电压)和低电平(低电压)就是(1或者0);
那么32根地址线产生的地址就会是:
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000001
.......
11111111   11111111   11111111   11111111
这里就有 2 32 次方个地址。
每个地址标识一个字节,那我们就可以给
2^32Byte == 2^32/1024KB == 2^32/1024/1024MB==2^32/1024/1024/1024GB == 4GB)
4G 的空闲进行编址。
同样的方法,那 64 位机器,如果给 64 根地址线,那能编址多大空间,自己计算。
这里我们就明白:
在32位的机器上,地址是32个0或者1组成二进制序列,那地址就得用4个字节的空间来存储,所以
一个指针变量的大小就应该是4个字节。
那如果在64位机器上,如果有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地
址。
总结:
指针是用来存放地址的,地址是唯一标示一块地址空间的。
指针的大小在 32 位平台是 4 个字节,在 64 位平台是 8 个字节

2. 指针和指针类型

指针是一个特殊的变量,它里面存储的数值被解释成为内存里的一个地址。要搞清一个指针需要搞清指针的四方面的内容:指针的类型、指针所指向的类型、指针的值或者叫指针所指向的内存区、指针本身所占据的内存区。让我们分别说明。

先声明几个指针放着做例子:
例一:

  1. (1)int*ptr;
  2. (2)char*ptr;
  3. (3)int**ptr;
  4. (4)int(*ptr)[3];
  5. (5)int*(*ptr)[4];
2.1.指针的类型

从语法的角度看,你只要把指针声明语句里的指针名字去掉,剩下的部分就是这个指针的类型。这是指针本身所具有的类型。让我们看看例一中各个指针的类型:
(1)int*ptr;          //指针的类型是int*
(2)char*ptr;      //指针的类型是char*
(3)int**ptr;        //指针的类型是int**
(4)int(*ptr)[3];   //指针的类型是int(*)[3]
(5)int*(*ptr)[4];  //指针的类型是int*(*)[4]
怎么样?找出指针的类型的方法是不是很简单?

2.2.指针所指向的类型
当你通过指针来访问指针所指向的内存区时,指针所指向的类型决定了编译器将把那片内存区里的内容当做什么来看待。
从语法上看,你只须把指针声明语句中的指针名字和名字左边的指针声明符*去掉,剩下的就是指针所指向的类型。例如:
(1)int*ptr;         //指针所指向的类型是int
(2)char*ptr;      //指针所指向的的类型是char
(3)int**ptr;        //指针所指向的的类型是int*
(4)int(*ptr)[3];   //指针所指向的的类型是int()[3]
(5)int*(*ptr)[4];  //指针所指向的的类型是int*()[4]
这里可以看到,指针的定义方式是: type + * 。
其实:
char* 类型的指针是为了存放 char 类型变量的地址。
short* 类型的指针是为了存放 short 类型变量的地址。
int* 类型的指针是为了存放 int 类型变量的地址。
在指针的算术运算中,指针所指向的类型有很大的作用。
指针的类型(即指针本身的类型)和指针所指向的类型是两个概念。当你对C越来越熟悉时,你会发现,把与指针搅和在一起的"类型"这个概念分成"指针的类型"和"指针所指向的类型"两个概念,是精通指针的关键点之一。
2.3.指针的值----或者叫指针所指向的内存区或地址

指针的值是指针本身存储的数值,这个值将被编译器当作一个地址,而不是一个一般的数值。在32 位程序里,所有类型的指针的值都是一个32 位整数,因为32 位程序里内存地址全都是32 位长。

指针所指向的内存区就是从指针的值所代表的那个内存地址开始,长度为sizeof(指针所指向的类型)的一片内存区。

以后,我们说一个指针的值是XX,就相当于说该指针指向了以XX为首地址的一片内存区域;我们说一个指针指向了某块内存区域,就相当于说该指针的值是这块内存区域的首地址。

指针所指向的内存区和指针所指向的类型是两个完全不同的概念。在例一中,指针所指向的类型已经有了,但由于指针还未初始化,所以它所指向的内存区是不存在的,或者说是无意义的。
以后,每遇到一个指针,都应该问问:这个指针的类型是什么?指针指的类型是什么?该指针指向了哪里?(重点注意)

那指针类型的意义是什么?
         指针类型决定了指针向前或向后走一步,走多大距离(单位是字节)。
2.4 指针本身所占据的内存区

指针本身占了多大的内存?你只要用函数sizeof(指针的类型)测一下就知道了。在32 位平台里,指针本身占据了4 个字节的长度。指针本身占据的内存这个概念在判断一个指针表达式(后面会解释)是否是左值时很有用。

2.5 指针 +- 整数
#include <stdio.h>
//演示实例
int main()
{int n = 10;char* pc = (char*)&n;int* pi = &n;printf("%p\n", &n);printf("%p\n", pc);printf("%p\n", pc + 1);//增加了1个字节printf("%p\n", pi);printf("%p\n", pi + 1);//增加了4个字节return 0;
}
总结:指针的类型决定了指针向前或者向后走一步有多大(距离)。

举例说明:创建一个数组,存入10个数,利用指针倒着打印出来。

#include<stdio.h>
int main()
{int arr[10] = { 0 };int* p = arr;int i = 0;for (i = 0; i < 10; i++){*(p + i) = i + 1;printf("%d ", *(p+i));}printf("\n");//倒着打印int* q = &arr[9];for (i = 0; i < 10; i++){printf("%d ", *q);q--;}return 0;
}

int* 类型的指针+1 就等于指针向前4个字节。

2.6 指针的解引用

1.指针类型决定了在解引用的是一次能访问几个字节(指针的权限);

字符指针解引用会访问1个字节

char* p  ->  1个字节

整形指针解引用会访问4个字节

//演示实例
#include <stdio.h>
int main()
{int n = 0x11223344;char* pc = (char*)&n;printf("%x\n", *pc);int* pi = &n;printf("%x\n", *pi);*pc = 0;  //重点在调试的过程中观察内存的变化。*pi = 0;  //重点在调试的过程中观察内存的变化。return 0;
}
总结:
指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节)。
比如: char* 的指针解引用就只能访问一个字节,而 int* 的指针的解引用就能访问四个字节。

3. 野指针

概念: 野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
3.1 野指针成因
(1)  指针未初始化
这种问题很常见,如下实例:
#include <stdio.h>
int main()
{ int *p;//局部变量指针未初始化,默认为随机值*p = 20;return 0;
}

代码int* p没有初始化,所以p变量中存的地址并没有指向我们当前程序的空间,而是指向内存中随机的空间,因此要*p要访问这块内存空间肯定是出错的!

这就是p的野指针。

(2) 指针越界访问

越界访问也很常见,我们平常在遍历数组中,不注意就会遇到这样的问题:

#include <stdio.h>
int main()
{int arr[5] = {0};int *p = arr;int i = 0;for(i=0; i<6; i++){//当指针指向的范围超出数组arr的范围时,p就是野指针*(p++) = i;}return 0;
}
初始化数组arr,让指针变量p等于数组首元素的地址,数组本身有 5个元素,但是在for循环中确 循环6次,i分别是0 1 2 3 4 5 ,本身i是0~4的,但是循环6次就会导致 非法访问内存,因此第6个数打印出来是随机数,这也是野指针的问题。
(3). 指针指向的空间释放
这个问题也是经常会遇到的问题,具体看下面的例子:
#include<stdio.h>
int* test()
{int x = 20;return &x;
}
int main()
{int* p = test();*p = 30;return 0;
}

test函数返回值是x的地址,main函数中用指针变量p接收x的地址,但是x变量进入test函数创建,而出了test函数会销毁,这时在改变*p的值,即使用x的地址,则是非法访问了,也会造成野指针的问题。

3.2 如何规避野指针
1. 指针初始化
比如上方的例子中,不能直接int* p, 必须要初始化,int a = 10; int* p = &a;从而规避野指针问题。
2. 小心指针越界问题
在我们使用数组时,一定要注意数组的 元素个数 以及我们所 循环的次数 ,避免粗心而导致的越界访问。
3. 指针指向空间释放及时置 NULL
在我们 不使用指针变量p时 ,int* p = NULL; 置为空,在接下来想要使用p时,用if语句:if(p!=NULL) .........能够很好的避免野指针。
4. 避免返回局部变量的地址
就像上面也指针成因的第三条所举的例子,避免返回局部变量的地址。
5. 指针使用之前检查有效性
像if(p!=NULL) .........就是在检查指针的有效性。
#include <stdio.h>
int main()
{int *p = NULL;//....int a = 10;p = &a;if(p != NULL){*p = 20;}return 0;
}

4. 常量指针和指针常量 (const)

常量指针和指针常量,ta们都与  const 这个关键字有关,下面举几个例子,看看大家能不能分清其中的区别:
const int* pa = &a;
int const* pa = &a;
int* const pa = &a;
const int* const pa = &a;
4.1.常量指针
常量指针的概念:指针 所指空间的值不能发生改变,不能通过指针解引用修改指针所指向空间的值,但是 指针的指向是可以改的
4.2.指针常量
指针常量的概念:指针本身就是一个常量,即 指针的指向不能发生改变,但是 指针所指向空间的值是可以改变的,可以通过解引用改变指针所指向空间的值。
具体可以理解为:
常量指针这个名字,以指针结尾,所以是具有指针性质的,因此可以改变指向,又因为是常量指针,所以指向的值是常量的,即是不能改变的。
再看指针常量这个名字,以常量结尾,应该具有常量的一些性质,所以自然不能改变指针的指向,只能修改指针所指向的值。
所以怎么区分 const 加到那个位置才叫常量指针,加到哪个位置叫指针常量?
const 如果在*的左边,那就是常量指针;
const 如果在*的右边,那就是指针常量;
为什么要这样记呢,可以思考一下,*在指针中有解引用的作用
//const 放在*的左边(可以理解为const修饰*)
//const 修饰的指针指向的内容,表示指针指向的内容不能通过指针来改变;
//但是指针变量本身是可以改变的。
(不能修改指针所指向空间的值, 但是指针的指向确是没有限制的)
//cosnt 放在*的右边(可以理解为const修饰的是pa)
//const修饰的指针变量本身,指针变量的内容不能被修改;
//但是指针指向的内容是可以通过指针来改变的。
(指针变量不能改变他的指向,但是指针所指向空间的值是不受限制的)
那么上面的几个例子已经有答案了,只需要看cosnt修饰的是*还是指针变量。
常量指针:const int* pa = pa; int const *pa = &a;
指针常量:int* const pa = &a;
如果*左右两边都加上cosnt , 说明这个指针指向和内容都不可改变。

5. 指针运算

5.1 指针+-整数

举例说明指针加减整数的情况:

#include<stdio.h>
int main()
{int arr[8] = { 1,2,3,4,5,6,7,8 };int* p = &arr[0];int i = 0;for (i = 0; i < 8; i++){printf("%d ", *p++);}return 0;
}

指针变量p是数组第一个元素的地址,用for循环打印数组各个元素,*的优先级高于++,所以每次执行*p打印完数组中元素后,p++在指向数组下一个元素,从而循环5次打印出数组中5个元素。

5.2 指针-指针

指针-指针的操作得到的是指针和指针之间元素个数,当然前提是两个指针必须指向同一块空间;举个栗子说明:

#include<stdio.h>
int main()
{int arr[8] = { 1,2,3,4,5,6,7,8 };int* p1 = &arr[0];int* p2 = &arr[7];printf("%d\n", p2 - p1);return 0;
}

如图所示表示数组中8个元素在内存中的存储,其中p1指向数组的第一个元素的地址,p2指向数组的第八个元素的地址,p2-p1,表示两个指针之间的元素个数,可以看到间隔7个元素,所以打印结果为7。

5.3指针的关系运算

指针的运算关系就是指针之间进行大小的比较,从而实现某些功能,例如:

#include<stdio.h>
int main()
{int arr[8] = { 1,2,3,4,5,6,7,8 };int* p = &arr[0];int i = 0;for (i = 0; p < &arr[8]; p++){printf("%d ", *p);}return 0;
}

如图所示即为指针的关系运算,下方所画的图可以清楚的解释: 

p刚开始是数组首元素的地址,接着通过p<&arr[8]的比较,满足则打印该地址的下的元素,接着p++,进行下一轮的比较,直到不满足p<&arr[8]为止。

这里可能有人会疑惑,上文说到不能越界吗,数组只有8个元素,下标最大为7,怎么可以使用arr[8]呢,其实这里并没有越界,上文是使用了越界的地址,已经非法访问内存了,而这里只是将这个地址写出来,和指针变量p进行关系运算,并没有使用这个地址,所以并不存在野指针的问题。

规定:允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较(如上方例子),但是不允许与指向第一个元素之前的内存位置的指针相比较。

6. 指针和数组

看一个例子:

#include <stdio.h>
int main()
{int arr[10] = {1,2,3,4,5,6,7,8,9,0};printf("%p\n", arr);printf("%p\n", &arr[0]);return 0;
}

运行结果:

可以看见数组名和数组首元素的地址是一样的。

结论: 数组名表示数组首元素的地址。(2种情况除外,数组章节讲解了)

这样写代码是可以的:

int arr[10] = { 1,2,3,4,5,6,7,8,9,10};

int* p = arr; //p存放的是数组首元素的地址 

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

例如:

#include <stdio.h>
int main()
{int arr[] = { 1,2,3,4,5,6,7,8,9,0 };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的地址。

那我们就可以直接通过指针来访问数组。

如下:

int main()
{int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };int *p = arr; //指针存放数组首元素的地址int sz = sizeof(arr) / sizeof(arr[0]);int i = 0;for (i = 0; i<sz; i++){printf("%d ", *(p + i));}return 0;
}

7.二级指针

指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪里?
这就是 二级指针

对于二级指针的运算有:

  • *ppa通过对ppa中的地址进行解引用,这样找到的是pa,*ppa其实访问的就是pa.

int b = 20;

*ppa  = &b; //等价于 pa = &b;

  • **ppa先通过*ppa 找到pa ,然后对pa 进行解引用操作:*pa, 那找到的是a。

**ppa = 30;

//等价于 *pa = 30

//等价于 a = 30

8.指针数组

指针数组是指针还是数组?

答案:是数组。是存放指针的数组。

数组我们已经知道整形数组,字符数组。

int arr1[5];                //整形数组 - 存放整形的数组

char arr2[6];             //字符数组 - 存放字符的数组

指针数组 - 存放指针的数组

int a = 10;

int b = 20;

int c = 30;

int* arr[5] = { &a, &b, &c};  //存放整形指针的数组

int i = 0;

for(i=0;  i<3;  i++)

{
        printf("%d ", *(arr[i]));

那指针数组是怎样的?

int* arr3[5]; //是什么?

arr3是一个数组,有五个元素,每个元素是一个整形是指针。

指针就介绍到这里。


补充:

CPU位数的含义
        上面这个流程里,最重要的几个关键词,分别是CPU寄存器,总线,内存。

        CPU的寄存器,说白了就是个存放数值的小盒子,盒子的大小,叫位宽。32位CPU能放入最大2^32的数值。64位就是最大2^64的值。这里的32位位宽的CPU就是我们常说的32位CPU,同理64位CPU也是一样。

        而CPU跟内存之间,是用总线来进行信号传输的,总线可以分为数据总线,控制总线,地址总线。功能如其名,举个例子说明下他们的作用吧。在一个进程的运行过程中,CPU会根据进程的机器码一行行执行操作。

        比如现在有一行是将A地址的数据与B地址的数据相加,那么CPU就会通过控制总线,发送信号给内存这个设备,告诉它,现在CPU要通过地址总线在内存中找到A数据的地址,然后取得A数据的值,假设是100,那么这个100,就会通过数据总线回传到CPU的某个寄存器中。B也一样,假设B=200,放到另一个寄存器中,此时A和B相加后,结果是300,然后控制CPU通过地址总线找到返回的参数地址,再把数据结果通过数据总线传回内存中。这一存一取,CPU都是通过控制总线对内存发出指令的。

总线,也可以理解为有个宽度,比如宽度是32位,那么一次可以传32个0或1的信号,那么这个宽度能表达的数值范围就是0到2^32这么多。

32位CPU的总线宽度一般是32位,因为刚刚上面提到了,CPU可以利用地址总线在内存中进行寻址操作,那么现在这根地址总线,最大能寻址的范围,也就到2^32,其实就是4G

64位CPU,按理说总线宽度是64位,但实际上是48位(也有看到说是40位或46位的,没关系,你知道它很大就行了),所以寻址范围能到2^48次方,也就是256T

本小节到这里就结束了。

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

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

相关文章

[openwrt-21.02]MT7981+MT7976 WiFi debug指令

openwrt版本信息 NAME="OpenWrt" VERSION="21.02-SNAPSHOT" ID="openwrt" ID_LIKE="lede openwrt" PRETTY_NAME="OpenWrt 21.02-SNAPSHOT" VERSION_ID="21.02-snapshot" HOME_URL="https://openwrt.org/&qu…

【Python】selenium爬虫常见用法和配置,以及常见错误和解决方法

欢迎来到《小5讲堂》 这是《Python》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解。 温馨提示&#xff1a;博主能力有限&#xff0c;理解水平有限&#xff0c;若有不对之处望指正&#xff01; 目录 前言无执行文件代码报错信息错误路径手动下载自动下载 选项配置Ch…

Ansible 自动化运维工具 - 了解和模块应用

目录 一. Ansible 的相关知识 1.1 Ansible 工具的简介 1.2 Ansible的四大组件 1.3 运维自动化工具 1.4 Ansible 和其它自动化运维工具对比 1.5 Ansible 的优缺点 二. Ansible 环境安装部署 2.1 管理端安装 ansible 2.2 配置主机清单 三. ansible 命令行模块 3.1 comm…

在编程中,什么是类的继承?

什么是继承 举个栗子&#xff0c;你是某披萨店的员工&#xff0c;你每天的工作就是在一张基础面饼上涂抹不同的香料制作不同的口味&#xff0c;假如某位顾客点了一份番茄味的香肠披萨&#xff0c;你可以直接在这张基础面饼上摊好香肠和番茄酱&#xff0c;而不是从头制作&#x…

拷贝构造、赋值运算符、运算符重载

&#x1f436;博主主页&#xff1a;ᰔᩚ. 一怀明月ꦿ ❤️‍&#x1f525;专栏系列&#xff1a;线性代数&#xff0c;C初学者入门训练&#xff0c;题解C&#xff0c;C的使用文章&#xff0c;「初学」C&#xff0c;linux &#x1f525;座右铭&#xff1a;“不要等到什么都没有了…

Electron 报错:WinState is not a constructor

文章目录 问题分析 问题 在使用 electron-win-state 库时报错如下 代码如下&#xff1a; const WinState require(electron-win-state) const winState new WinState({ defaultWidth: 800,defaultHeight: 600,// other winState options, see below })const browserWindow…

京东手势验证码-YOLO姿态识别+Bézier curve轨迹拟合

这次给老铁们带来的是京东手势验证码的识别。 目标网站&#xff1a;https://plogin.m.jd.com/mreg/index 验证码如下图: 当第一眼看到这个验证码的时候&#xff0c;就头大了&#xff0c;这玩意咋识别&#xff1f;&#xff1f;&#xff1f; 静下心来细想后的一个方案&#xf…

原来pip是有默认路径的。

今天一直报错&#xff1a; bash: /root/data1/anaconda3/envs/li_3_10/bin/pip: /root/lsc/anaconda3/envs/li_3_10/bin/python: bad interpreter: No such file or directory 原来是root/data1/anaconda3/envs/li_3_10/bin/pip: 这个位置的pip 自身带默认路径&#xff0c;然…

Python 正则表达式(一)

文章目录 概念正则函数match函数正则表达式修饰符意义&#xff1a; 常用匹配符限定符原生字符串边界字符 概念 正则表达式是对字符串操作的一种逻辑公式&#xff0c;就是用事先定义好的一些特定字符、及这些特定字符的组合&#xff0c;组成一个“规则字符串”&#xff0c;这个…

【问题解决】本地pnpm版本与packageManager中pnpm版本不一致

问题&#xff1a;ERR_PNPM_BAD_PM_VERSION  This project is configured to use v8.6.10 of pnpm. Your current pnpm is v9.1.0 解决&#xff1a;If you want to bypass this version check, you can set the “package-manager-strict” configuration to “false” or set…

Navicat 17 的数据分析

上周的博客预告了 Navicat 17&#xff08;英文版&#xff09;即将发布&#xff0c;目前正在测试阶段&#xff0c;并计划于 5 月 13 日发布。如我们所见&#xff0c;版本 17 推出了众多令人兴奋的新功能。其中最大亮点是数据分析工具&#xff0c;只需点击按钮&#xff0c;即可为…

万字长文——前端开发必看的KeepAlive原理详解

前言 本文将从原理应用源码(Vue2和Vue3)的角度全面介绍 组件&#xff0c;全文共计16000字&#xff0c;阅读时间大概30min&#xff0c;建议码住在看&#xff0c;相信看完本文的你会对该组件有一更深刻的认识。 一、<KeepAlive>是什么&#xff1f; <KeepAlive>是一个…

【数据结构】单链表和双链表

文章目录 一、链表的概念及结构二、链表的分类三、无头单向非循环链表1.单链表创建2.尾插和头插3.尾删和头删4.打印5.查找6.插入7.删除8.销毁 四、带头双向循环链表1.双链表的创建2.初始化3.判断链表是否为空4.尾插和头插5.尾删和头删6.查找7.插入8.删除9.销毁 五、总结链表和顺…

[力扣题解]93. 复原 IP 地址

题目&#xff1a;93. 复原 IP 地址 思路 回溯法&#xff1b; 特别的是&#xff0c;用pointNum来记录.的数量&#xff0c;并且没有创建path&#xff0c;而是直接在原来的strings中插入.&#xff1b; 同时&#xff0c;在判断子串合法性的时候&#xff0c;0是合法的&#xff0c;…

Java中使用alibaba的easyexcel中的方法实现csv模板下载功能

系列文章目录 文章目录 系列文章目录一、EasyExcelUtil工具 一、EasyExcelUtil工具 /*** param response 响应* param fileName 文件名称* param sheetName sheet名称* param headNameList 头部名称* param <T>* throws IOException*/public static <T>…

基于Springboot+Vue的Java项目-车辆管理系统开发实战(附演示视频+源码+LW)

大家好&#xff01;我是程序员一帆&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;Java毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计 &am…

Spring自动装配:解析原理与实践

在Spring框架中&#xff0c;自动装配是一种强大的特性&#xff0c;它能够根据一定的规则自动地将bean装配到Spring容器中&#xff0c;从而简化了配置和开发过程。本文将深入探讨Spring自动装配的原理和实践&#xff0c;帮助程序员更好地理解和应用这一重要特性。 1. 什么是自动…

35个矩阵账号,如何通过小魔推打造2704万+视频曝光?

在如今的短视频时代&#xff0c;矩阵发布的作用被发挥到极致&#xff0c;通过各个短视频平台的流量分发&#xff0c;虽然视频质量不如那些头部的IP&#xff0c;但是在视频数量上却能做到轻松碾压&#xff0c;让自己的品牌与门店有更多的声量&#xff0c;这就是如今短视频平台对…

安卓实现视频录制与显示和翻转摄像头

权限&#xff1a; <!-- 相机权限 --> <uses-featureandroid:name"android.hardware.camera"android:required"false" /> <uses-permission android:name"android.permission.CAMERA" /><!-- 录音权限&#xff08;包括麦克…

2024好用的网页客服系统推荐?

2024好用的网页客服系统推荐&#xff1f;Zoho SalesIQ是一款强大的实时聊天工具&#xff0c;专为网站和在线商店设计。它提供了一套全面的功能&#xff0c;帮助企业实时解决客户问题&#xff0c;提高转化率和客户满意度。 实时监控 Zoho SalesIQ能够实时监控网站的访问者活动&…