C语言初阶(6) - 指针

news2024/10/5 19:12:17

目录

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.coloradmin.cn/o/1660043.html

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

相关文章

离线使用evaluate

一、目录 步骤demorouge-n 含义 二、实现 步骤 离线使用evaluate: 1. 下载evaluate 文件&#xff1a;https://github.com/huggingface/evaluate/tree/main2. 离线使用 路径/evaluate-main/metrics/rougedemo import evaluate离线使用evaluate: 1. 下载evaluate 文件&…

Android 百度语音识别(详细步骤+源码),京东android面试题

改好之后&#xff0c;请注意&#xff0c;每个人都是不一样&#xff0c;你如果发现你创建的应用的配置的值和我创建的是一模一样的&#xff0c;你马上去百度提BUG&#xff0c;他们的程序员要就要下岗了~ OK&#xff0c;现在配置也完成了&#xff0c;接下来就是使用了。 ③ 使用…

五一超级课堂---Llama3-Tutorial(Llama 3 超级课堂)---第四节Llama 3 高效部署实践(LMDeploy 版)

课程文档&#xff1a; https://github.com/SmartFlowAI/Llama3-Tutorial 课程视频&#xff1a; https://space.bilibili.com/3546636263360696/channel/collectiondetail?sid2892740&spm_id_from333.788.0.0 操作平台&#xff1a; https://studio.intern-ai.org.cn/consol…

浅谈现代消息队列与云存储

一、前言 1970 年代末&#xff0c;消息系统用于管理多主机的打印作业&#xff0c;这种削峰解耦的能力逐渐被标准化为“点对点模型”和稍复杂的“发布订阅模型”&#xff0c;实现了数据处理的分布式协同。随着时代的发展&#xff0c;Kafka&#xff0c;Amazon SQS&#xff0c;Ro…

charts3D地球--添加航线

要在地球视角下画出海运路线图 方案 添加 globl 地球创建geo地理坐标系创建canvas对象用于承载地图世界地图this.worldChart//初始化canvas节点let cav = document.createElement("canvas");this.$echarts.registerMap("world", geoJson);this.worldChart…

AVL树的原理及其实现

文章目录 前言了解AVL树AVL树的特点AVL树的节点调整方案右单旋为什么要右单旋呢&#xff1f;右单旋代码 左单旋为什么要左单旋&#xff1f;左单旋代码 左右双旋左右双旋之后平衡因子的情况左右双旋代码实现 右左双旋右左双旋代码&#xff1a; 简单测试 前言 回顾我们对于二叉搜…

保研面试408复习 4——操作系统、计网

文章目录 1、操作系统一、文件系统中文件是如何组织的&#xff1f;二、文件的整体概述三、UNIX外存空闲空间管理 2、计算机网络一、CSMA/CD 协议&#xff08;数据链路层协议&#xff09;二、以太网MAC帧MTU 标记文字记忆&#xff0c;加粗文字注意&#xff0c;普通文字理解。 1、…

值得推荐的10+REST API测试工具

什么是API&#xff1f; API是一个软件解决方案&#xff0c;作为中介&#xff0c;使两个应用程序能够相互交互。以下一些特征让API变得更加有用和有价值&#xff1a; 遵守REST和HTTP等易于访问、广泛理解和开发人员友好的标准。API不仅仅是几行代码&#xff1b;这些是为移动开…

qml 和 c++类的数据交互

1、 新建一个需要交互的C++类 1)添加QObject头文件 2)添加自QObject的继承 3)添加Q_OBJECT宏 4)使用Q_PROPERTY,定义两个交互的属性,并设置读写的方法和变更属性的信号。 5)添加方法、槽函数和变量 2、在main.cpp中添加实例化对象的QML上下文 1)添加需要QML交互的…

Kubernetes学习-集群搭建篇(一) 搭建Master结点

&#x1f3f7;️个人主页&#xff1a;牵着猫散步的鼠鼠 &#x1f3f7;️系列专栏&#xff1a;Kubernetes渐进式学习-专栏 &#x1f3f7;️个人学习笔记&#xff0c;若有缺误&#xff0c;欢迎评论区指正 目录 1. 前言 2. 集群搭建方式 3. 环境说明 4. 利用kubeadm初始化Ma…

应该在哪里找海外ip代理?

出于学习工作&#xff0c;或者游戏娱乐的需求&#xff0c;许多人需要使用海外代理ip。那么我们该如何寻找到合适的、正规的、安全的海外代理ip呢&#xff1f; 首先&#xff0c;我们需要明白使用海外IP代理可能带来的风险&#xff0c;包括隐私泄露、网络速度变慢、安全风险以及可…

百融云创回购计划加速落实 机构看好中长期吸引力

单日回购近400万港元B类股份&#xff0c;一站式服务的AI科技领航者百融云创&#xff08;百融云-W,6608.HK&#xff09;的回购计划正在加速落实。 此前&#xff0c;在百融云创2023年年度业绩公告的同时&#xff0c;该公司一并披露将在2024年不时在公开市场购回总金额不超过2.5亿…

原生微信小程序canvas签名功能

半个月前百度搜出来的&#xff0c;没存书签现在不知道是哪篇文章了&#xff0c;再搜也没搜出来那篇文章&#xff0c;还好当时把代码复制到本地跑了一下&#xff0c;现在再发csdn存一下。 sign.js Page({data: {ctx: null,width: null,height: null,drawCount: 0,drawState: &…

WebStorm开发插件

WebStorm开发插件 开发 WebStorm 插件是一项令人兴奋的任务&#xff0c;它可以帮助提升开发效率&#xff0c;定制 IDE 来满足个人或团队的需求。在这份指南中&#xff0c;我将向你介绍如何开始开发 WebStorm 插件&#xff0c;并提供一些实用的技巧和建议。 1. 准备工作 在开…

“幽灵“再临!新型攻击瞄准英特尔CPU;微软Outlook漏洞被俄利用,网络间谍攻击捷克德国实体 | 安全周报0510

1. 微软Outlook漏洞被俄罗斯APT28利用&#xff0c;捷克德国实体遭网络间谍攻击&#xff01; 捷克和德国于周五透露&#xff0c;他们成为与俄罗斯有关的APT28组织进行的长期网络间谍活动的目标&#xff0c;此举遭到欧洲联盟&#xff08;E.U.&#xff09;、北大西洋公约组织&…

视频拼接融合产品的产品与架构设计(二)

视频拼接融合产品的产品与架构设计一 以上是第一期&#xff0c;以前思考的时候还是比较着急&#xff0c;现在思考的更多了&#xff0c;现实世界的拼接更加需要我们沉下心来做&#xff0c;尤其是对于更多画面&#xff0c;画面更加清晰怎么做 本篇章不在于其他功能&#xff0c;在…

SpringBoot的图片上传

简介 该文档旨在介绍一个基于Spring Boot框架的简单文件上传功能的实现方式。本文档将详细介绍相关代码的功能和配置以及如何使用它们。 样例 技术栈 Spring Boot&#xff1a;一个用于快速开发基于Spring的应用程序的框架。Thymeleaf&#xff1a;一个用于在Web应用程序中创建…

孔板流量计和孔板流量计真的不一样

孔板流量计和孔板流量计真的不一样&#xff0c;无论您是追求品质&#xff0c;还是注重实用功能&#xff0c;我们的产品都能让您心动不已。让您轻松享受到现代科技所带来的便利&#xff0c;尽情展现自己不一样的魅力。 用途【1-5-9】 孔板流量计为煤矿瓦斯抽放而设的计算瓦斯抽…

vue3.0(五) reactive全家桶

文章目录 1 reactive1.1 reactive的应用1.2 reactive的特点1.3 reactive的注意1.4 reactive的局限性 2 toRefs3 isReactive4 shallowReactive5 readonly5.1 readonly 详细信息5.2 readonly函数创建一个只读的响应式对象5.3 如何修改嵌套在只读响应式对象中的对象? 6 isReadonl…

SG3225EEN在PAM4光模块和400G,QSFP-DD光模块中的应用

爱普生晶振SG3225EEN&#xff0c;156.25MHz在PAM4光模块和QSFP-DD光模块中的应用。光模块市场已发展至400G光模块&#xff0c;那么PAM4光模块和400G QSFPDD光模块有哪些区别呢?SG3225EEN又是怎么应用在PAM4光模块和QSFP-DD光模块中的呢? 首先介绍的是PAM4光模块:PAM4是PAM(脉…