突破编程_C++_基础教程(指针(一))

1 指针的基础概念

指针是 C++ 的核心之一,使用 C++ 语言构建的程序之所以性能强悍,有很大部分原因是体现在使用指针直接操作内存。当然这样的工具是一把双刃剑,错误的指针操作可能会导致程序崩溃或者数据损坏。
指针主要有四个方面的用途:
(1)动态内存分配:使用 new 操作符在堆上分配内存。
(2)传递数据:通过指针传递大型数据对象可以显著提高程序的效率(比如使用指针作为函数参数)。
(3)回调函数:指针可以用于传递函数的地址,函数式编程正是建立在这个功能的基础上。
(4)优化性能:指针可以直接访问内存,避免了一些额外的开销,如复制数据或者查找数据等。
指针本身是一个变量,其值为另一个变量的内存地址,因此,要掌握指针的原理与作用,需要从理解内存地址开始。

1.1 内存地址

内存地址是指计算机内存中存储变量或对象的地址。内存空间大小就是寻址能力,即能访问到多少个地址,比如 32 位机器内存空间大小就是 2^32 = 4294967296,也就是 4 GB 。每个变量或对象在内存中都有一个唯一的地址,通过该地址可以访问和操作该变量或对象。注意一 个内存地址对应一个字节,以 int 类型的变量为例,其占据 4 个内存地址,其中首个内存地址就是这个变量的地址。

#include <iostream>int main()
{int vals[4]{};printf("val1 address = %p\n", &vals[0]);printf("val2 address = %p\n", &vals[1]);printf("val3 address = %p\n", &vals[2]);printf("val4 address = %p\n", &vals[3]);return 0;
}

上面代码的输出为:

val1 address = 0000005420F6F978
val2 address = 0000005420F6F97C
val3 address = 0000005420F6F980
val4 address = 0000005420F6F984

为了能够说明 1 个 int 类型的变量占据 4 个内存地址,我们在上面的代码中使用占据连续内存的数组来做测试,由这个输出可以看出:数组 vals 的第一个元素所占据的内存地址由 0000005420F6F9780000005420F6F97B (再往下的一个地址就是第二个元素的首地址 0000005420F6F97C),刚好是 4 个内存地址,其首个内存地址 0000005420F6F978 就是这个数组 vals 的第一个元素的地址(同时也是这个数组变量 vals 的地址)。

1.2 指针是什么

指针是一种变量,它存储的是其他变量的内存地址。通过指针,我们可以间接地访问和操作存储在内存中的变量。
由这个定义可知,指针既然是一个变量,那么它本身也需要占用内存,即有自己对应的内存地址。如下为样例代码( x64 平台编译):

#include <iostream>int main()
{void* ptr = nullptr;printf("ptr address = %p\n", &ptr);printf("ptr address size = %llu\n", sizeof(ptr));printf("ptr value = %p\n", ptr);return 0;
}

上面代码的输出为:

ptr address = 000000196B5CF678
ptr address size = 8
ptr value = 0000000000000000

其中,指针 ptr 虽然指向的是一个空地址,但是其作为一个变量,依然有自己的内存地址(000000788E9AF638)。另外,ptr address size = 8 表明使用 x64 平台编译时,指针所占用的内存大小为 8 个字节( 32 位平台编译是 4 个字节),刚好可以保存一个内存地址,这就是指针能够存储其他变量的内存地址的原理。

2 指针的基本使用

指针是一种变量,所以在使用前和其他类型变量一样,也需要定义与初始化:指针变量定义时前面会有一个星号(*)。例如,int *ptr; 意思是定义了一个指向整数的指针。指针变量在使用之前必须被初始化,否则其值是未定义的,这个时候指向的是一个随机的内存地址,对其操作很容易引起程序崩溃。通过在指针变量前加上星号(*)可以访问指针所指向的对象,相当于操作这个对象本体。

2.1 指针的定义与初始化

指针的定义和初始化可以通过以下方式完成:

#include <iostream>int main()
{int val = 1;			// 定义一个整型变量 val,并初始化为 1  int *ptr = &val;		// 定义一个指向整型的指针 ptr,并将它初始化为变量 val 的地址printf("val address = %p\n", &val);printf("ptr address = %p\n", &ptr);printf("ptr value = %p\n", ptr);return 0;
}

上面代码的输出为:

val address = 00000035076FFCB4
ptr address = 00000035076FFCD8
ptr value = 00000035076FFCB4

其中,指针 ptr 的值等于整型变量 val 的地址。第 6 行 int *ptr = &val; 中的 & 符号是取地址符,用于获取变量的内存地址。基本类型( int 、float 等)、结构体类型( struct )以及类类型( class )的地址获取都需要使用该符号。

2.2 解引用

*操作符是 C++ 的解引用操作符,用于获取指针所指向的对象,对其操作相当于对指针所指向对象的操作:
指针的定义和初始化可以通过以下方式完成:

#include <iostream>int main()
{int val1 = 1;	int *ptr = &val1;	*ptr = 2;					//该表达式相当于 val1 = 2;	int val2 = *ptr;			//该表达式相当于 int val2 = val1;	printf("val1 = %d\n", val1);printf("val2 = %d\n", val2);return 0;
}

上面代码的输出为:

val1 = 2
val2 = 2

其中,*ptr 在上面程序的运行过程中就是整型变量 val1 ,不管是对其做赋值操作(*ptr = 2;),还是将其用于其他变量的初始化(int val2 = *ptr;),都相当于直接操作整型变量 val1 自身。 对于结构体和类,一般是使用箭头操作符 -> 来操作对象的成员变量或者成员函数,但是根据前面所描述的解引用概念,使用解引用操作符也可以起到相同作用:

#include <iostream>
#include <string>using namespace std;class Student
{
public:Student() {};Student(string name):m_name(name) {};~Student() {};public:string getName(){return m_name;}private:string m_name;
};int main()
{Student st("zhangsan");Student *ptr = &st;string name1 = st.getName();string name2 = ptr->getName();string name3 = (*ptr).getName();		//使用解引用操作符return 0;
}

注意上面的语句 string name3 = (*ptr).getName();,同样可以调用对象 st 的成员函数。只是由于 C++ 提供了更方便的箭头操作符 -> ,所以一般我们才不会如此使用。

2.3 指向数组的指针

指向数组与上面章节的指向基本类型( int 、float 等)、结构体类型( struct )以及类类型( class )的使用方式有所不同,数组名本身就是数组的首地址,所以无需做取地址操作,如下为样例代码:

#include <iostream>int main()
{int vals[6] = { 1,2,3,4,5,6 };printf("%p \n", vals);printf("%p \n", &vals);int* ptr = vals;for (size_t i = 0; i < 6; i++){printf("%d ", *(ptr+i));}return 0;
}

上面代码的输出为:

vals address = 000000C03976F8C8
&vals address = 000000C03976F8C8
1 2 3 4 5 6

从上面输出可以看出,数组名 vals 与对数组名取地址 &vals 所得到的内存地址是一样的,所以如果用指针指向某个数组,直接将数组名赋值给指针即可。第 13 行 printf("%d ", *(ptr+i)); 中的 *(ptr+i) 是指针的运算,在下面章节会详细讲解。
指针不光可以指向整个数组,还可以指向数组中的某一个元素,如下:

#include <iostream>int main()
{int vals[6] = { 1,2,3,4,5,6 };printf("before modification, vals[2] = %d \n", vals[2]);int* ptr = &vals[2];			//指向的数组第 3 个元素*ptr = 10;		//将其所指向的数组第 3 个元素的值修改为 10printf("after modification, vals[2] = %d \n", vals[2]);return 0;
}

上面代码的输出为:

before modification, vals[2] = 3
after modification, vals[2] = 10

注意第 10 行 int* ptr = &vals[2]; 这里是指向数组里面的一个整型元素,所以一定要用取地址操作符。

2.4 指向函数的指针

C++中的函数也有地址(调用函数的本质就是跳转到这个函数的地址,然后执行里面的函数体)。因此,可以声明指向函数的指针,并使用这个指针调用函数。指向函数的指针也被称作是函数指针,其定义方式为:

函数返回值类型 (`*` 指针变量名) (函数参数列表);

函数返回值类型:表示该指针变量所指向函数的返回值类型。
指针变量名:表示该指针变量的名称。
函数参数列表:表示该指针变量所指向函数的参数列表。
为了使用方便,一般会用关键字 typedef 来定义函数指针,即:typedef 函数返回值类型 (* 指针变量名) (函数参数列表) 。例如:

typedef int (*ADD)(int,int);
ADD addFunc;

使用这种方式可以目标函数看作为一个类型,然后再用它去定义指针,增强复用性。
对于无参数或者无返回值的函数,需要使用用 void 关键字,例如:

typedef void (*TESTFUNC)(void); 	//无参数和返回值

2.4.1 指向全局函数的函数指针

以如下代码为例:

#include <iostream>int add(int a, int b)
{int sum = a + b;return sum;
}int main()
{typedef int(*ADDFUNC)(int, int);ADDFUNC f1 = add;int sum1 = f1(1, 2);			//直接使用函数名int sum2 = (*f1)(1, 2);			//取函数地址printf("sum1 = %d\n",sum1);printf("sum2 = %d\n", sum2);return 0;
}

上面代码的输出为:

sum1 = 3
sum2 = 3

特别注意的是,因为函数名本身就可以表示该函数地址(指针),因此在获取函数指针时,可以直接用函数名,也可以取函数的地址。因此,上面代码中 int sum1 = f1(1, 2); 以及 int sum2 = (*f1)(1, 2); 作用是相同的。

2.4.2 指向对象成员函数的函数指针

以如下代码为例:

#include <iostream>class MyAdd 
{
public:MyAdd() {}~MyAdd() {}public:int add(int a, int b){int sum = a + b;return sum;}};int main()
{MyAdd myAddObj;typedef int(MyAdd::*ADDFUNC)(int, int);ADDFUNC f1 = &MyAdd::add;int sum = (myAddObj.*f1)(1, 2);printf("sum = %d\n", sum);return 0;
}

上面代码的输出为:

sum = 3

注意:对象的成员函数属于类,所以其存储位置在对象外的空间中,由所有的类对象共享。因此, MyAdd 类中的 add() 成员函数,不是属于 myAddObj 对象的,而是属于 MyAdd 类。所以使用 &类名::成员函数名 的形式将该成员函数赋给函数指针。

2.4.3 回调函数

回调函数是函数指针的一个重要应用场景,比如在使用 C++ 的容器类时,经常会自定义回调函数用以实现定制化功能。以 vector 的自定义排序为例,代码如下:

#include <iostream>
#include <vector>
#include <algorithm>using namespace std;struct Student 
{string id;double score;
};bool compareByScore(Student& stu1, Student& stu2)
{return stu1.score < stu2.score;
}int main()
{vector<Student> students;students.emplace_back(Student{ "s1",98.2 });students.emplace_back(Student{ "s2",97.6 });students.emplace_back(Student{ "s3",92.8 });students.emplace_back(Student{ "s4",95 });students.emplace_back(Student{ "s5",99 });printf("before sort\n");for (size_t i = 0; i < students.size(); i++){printf("%s(%lf) ", students[i].id.c_str(), students[i].score);}printf("\n");sort(students.begin(), students.end(), compareByScore);printf("after sort\n");for (size_t i = 0; i < students.size(); i++){printf("%s(%lf) ", students[i].id.c_str(), students[i].score);}printf("\n");return 0;
}

上面代码的输出为:

before sort
s1(98.200000) s2(97.600000) s3(92.800000) s4(95.000000) s5(99.000000)
after sort
s3(92.800000) s4(95.000000) s2(97.600000) s1(98.200000) s5(99.000000)

其中,函数 compareByScore 便作为一个函数指针的入参传递给函数 sort

2.4.4 函数指针和指针函数的区别

函数指针和指针函数是两种不同的编程概念,前者是一个指针,后者是一个函数,除了名字比较容易混淆,实际上是完全不同的概念。
上面内容已经说明了函数指针的含义与作用,指针函数的定义如下:
(1)指针函数本身就是一个函数,其返回的类型是指针。
(2)指针函数用于返回指针类型的值,例如动态分配的对象或数组的指针。

2.5 指向指针的指针

指针可以指向所有数据类型的变量(基本类型、结构体类型、类类型等),而指针自身也是一种变量,所以指针自然也可以指向指针。把指向指针的指针理解透彻,基本上也就能掌握了指针的精髓。如下为样例代码:

#include <iostream>int main()
{int val1 = 1;int *ptr1 = &val1;int **ptr2 = &ptr1;printf("ptr1 address = %p\n", &ptr1);printf("ptr1 address = %p\n", &(*ptr2));printf("ptr2 value = %p\n", ptr2);return 0;
}

上面代码的输出为:

ptr1 address = 000000C4A839F758
ptr1 address = 000000C4A839F758
ptr2 value = 000000C4A839F758

由结果可以看出,指向指针的指针变量 ptr2 保存了指针变量 ptr1的地址( 000000C4A839F758 )。 其中代码第 10 行 int **ptr2 = &ptr1; 定义了一个指向指针的指针,这里用了两个星号*,其保存的值就是指针变量 ptr1的地址。
第 11、 12、 13 行代码尤为重要:
第 11 行代码 printf("ptr1 address = %p\n", &ptr1); ,其中的 &ptr1 是对指针变量 ptr1 做取地址操作。
第 12 行代码 printf("ptr1 address = %p\n", &(*ptr2)); ,其中的 (*ptr2) 是对指针变量 ptr2 做解引用操作,再对其做取地址操作,相当于直接对指针变量 ptr1 做取地址操作。
第 13 行代码 printf("ptr2 value = %p\n", ptr2); ,对指向指针的指针取值,直接用其变量名即可。

2.6 创建动态内存

使用指针可以在堆中创建内存空间(先在堆中申请一块内存空间,然后将其首地址返回给一个指针,后面通过该指针便可读写这一块内存),其创建和销毁过程都需要手动控制。 C++ 使用 new 或者 new[] 操作符在堆中创建一块内存空间,使用 delete 或者 delete[] 释放这块申请的内存空间。如下:

int* ptr = new int;

这一行代码定义了一个指向整型的指针变量 ptr ,并且使用 new 操作符在堆中创建一个 int 类型的内存空间,并将该空间首地址返回指针变量 ptr
如果想将这个内存空间赋值为 1 ,可以做如下操作:

*ptr = 1;

在使用完这个内存空间后,一定要将其释放(避免内存泄露),并且将指针变量 ptr 赋值 nullptr(避免悬垂指针,它所指向的内存空间已经被释放):

delete ptr;
ptr = nullptr;

注意释放的操作不能再次执行,如果再做一次 delete ptr; 则会导致程序崩溃。

2.7 指针的运算

指针为什么一定要定义类型(即使无类型,也需要使用 void 做定义),这个要求的一个来源就是指针运算需要按照类型做处理:

#include <iostream>int main()
{int val1 = 1;short val2 = 2;int *ptr1 = &val1;short *ptr2 = &val2;printf("before adding, ptr1 value = %p\n", ptr1);printf("before adding, ptr2 value = %p\n", ptr2);ptr1++;ptr2++;printf("after adding, ptr1 value = %p\n", ptr1);printf("after adding, ptr2 value = %p\n", ptr2);return 0;
}

上面代码的输出为:

before adding, ptr1 value = 0000000BBC54F614
before adding, ptr2 value = 0000000BBC54F634
after adding, ptr1 value = 0000000BBC54F618
after adding, ptr2 value = 0000000BBC54F636

从上面代码运行的结果可以看出:不同类型的指针变量,其运算的步长由其类型确定。 int 类型的指针变量,对其做 ++ 操作后,该变量的值增加了 4 ,指向下一个 int 变量。 short 类型的指针变量,对其做 ++ 操作后,该变量的值增加了 2 ,指向下一个 short 变量。

2.7.1 指针的加减运算

指针的加减运算通常用于对数组的操作,如下为样例代码:

#include <iostream>int main()
{int vals[6] = { 1,2,3,4,5,6 };int* ptr = vals;for (size_t i = 0; i < 6; i++){printf("%d ", *(ptr + i));}return 0;
}

上面代码的输出为:

1 2 3 4 5 6

上面代码的核心语句是 printf("%d ", *(ptr + i));,其中 ptr + i 是指向数组中的第 i 个元素的地址,再加上前面的星号 * ,则完成了对其的解引用操作,最终获取到了对应数组元素的值。

2.7.2 指针的赋值操作

指针的赋值操作也是一个在开发中常见的操作。其作用是将一个指针的值(这个值是内存中某一个变量的地址)赋给另一个指针。如下为样例代码:

#include <iostream>int main()
{int val1 = 1;int *ptr1 = &val1;int *ptr2 = ptr1;printf("ptr1 value = %p\n", ptr1);printf("ptr2 value = %p\n", ptr2);return 0;
}

上面代码的输出为:

ptr1 value = 0000006FCF3CFBC4
ptr2 value = 0000006FCF3CFBC4

赋值操作后, 指针变量 ptr2 的值就等于指针变量 ptr1 的值。

3 使用指针的注意点

3.1 常量指针与指针常量

常量指针(const pointer)和指针常量(pointer to const)是两个不同的概念,常量指针指的是其指向变量的值不可改变,但是指针本身是可以改变的,可以指向其他变量;指针常量指的是指针本身是常量,其不可以再指向其他变量。
常量指针的样例代码:

const int val1 = 1;
int *ptr1 = &val1;			//错误:必须使用常量指针
const int *ptr1 = &val1;	//OK
*ptr1 = 2;	

指针常量的样例代码:

int val1 = 1;
int val2 = 2;
int const *ptr1 = &val1;	//OK
*ptr1 = &val2;				//错误:指针本身是常量,其不可以再指向其他变量。

3.2 使用 nullptr

前面章节的代码中,多处使用了 nullptr 关键字,该关键字是在 C++11 标准中引入的,用于表示空指针。在 C++11 及以后的版本中,nullptr 替代了 C++98/03 中的 NULL 或 0 作为空指针的表示。该关键字可以避免函数重载问题,如下为样例代码:

void overLoadFunc(int* val);
void overLoadFunc(int val);int main()
{overLoadFunc( NULL );  // 期待调用 overLoadFunc(int* val); 但实际调用却是 overLoadFunc(int val);
}

上面代码中的 overLoadFunc( NULL ); 实际调用的是 overLoadFunc(int val); 。其原因是 NULL 本身就是整数 0 ,因此进入了整型参数的重载函数。

3.2 野指针出现的原因

野指针出现的原因主要有以下三种:
(1)指针变量未初始化。局部指针变量的默认值是一个随机值,如果此时访问该指针则会引起程序崩溃。所以,指针变量在创建的同时应当被初始化,要么将指针设置为 nullptr ,要么让它指向合法的内存( new 出来的对象或者现有的一个对象)。
(2)释放内存后没有将指针设置为 nullptr 。不管是 free 还是 delete 在释放内存时,只是把指针所指的内存给释放掉了,但此时指针的值依然是之前内存空间的首地址。此时访问该指针则会引起程序崩溃。
(3)指针操作超越变量作用范围。栈内存在函数结束时会被释放,如果将其内存地址通过指针返回给调用者,此时再访问则会引起程序崩溃。

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

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

相关文章

java05 数组

一 概念介绍 指的是一种容器&#xff0c;可以同来存储同种数据类型的多个值。 但是数组容器在存储数据的时候&#xff0c;需要结合隐式转换考虑。 比如&#xff1a; 定义了一个int类型的数组。那么boolean。double类型的数据是不能存到这个数组中的&#xff0c; 但是byte类…

Java线程同步的方法和例子

在Java中&#xff0c;线程同步是一种机制&#xff0c;用于确保多个线程可以安全地访问共享资源&#xff0c;而不会发生数据不一致或数据损坏的情况。线程同步的主要方法包括&#xff1a; synchronized关键字&#xff1a;这是Java中最常用的线程同步方法。它用于方法或代码块&a…

Orange3数据预处理(分组组件)

Group By是Orange3中一个非常有用的组件&#xff0c;它允许用户对数据集进行聚合操作&#xff0c;类似于SQL中的GROUP BY语句或Pandas库中的groupby方法。以下是Group By组件的一些核心功能介绍&#xff1a; 1. Mean (平均数): 数据值的总和除以数据项的数量&#xff0c;显示数…

Megatron-LM源码系列(七):Distributed-Optimizer分布式优化器实现Part2

1. 使用入口 DistributedOptimizer类定义在megatron/optimizer/distrib_optimizer.py文件中。创建的入口是在megatron/optimizer/__init__.py文件中的get_megatron_optimizer函数中。根据传入的args.use_distributed_optimizer参数来判断是用DistributedOptimizer还是Float16O…

【持续学习系列(九)】《Continual Learning with Pre-Trained Models: A Survey》

一、论文信息 1 标题 Continual Learning with Pre-Trained Models: A Survey 2 作者 Da-Wei Zhou, Hai-Long Sun, Jingyi Ning, Han-Jia Ye, De-Chuan Zhan 3 研究机构 National Key Laboratory for Novel Software Technology, Nanjing University; School of Artifici…

C#,雅各布斯塔尔—卢卡斯(Jacobsthal Lucas Number)的算法与源代码

1 雅各布斯塔尔序列 雅各布斯塔尔序列是一个与斐波那契序列类似的加法序列&#xff0c;由递归关系JnJn-12Jn-2定义&#xff0c;初始项J00&#xff0c;J11。序列中的一个数字称为雅可布沙尔数。它们是卢卡斯序列Un&#xff08;P&#xff0c;Q&#xff09;的一种特殊类型&#x…

股票交易

这里尝试利用单调队列优化&#xff0c;这里不好直接用单调队列的原因是因为(以买为例)\(-ap[i]*k_1\)不是只与下标有关的 所以解决方案一&#xff1a;我们将下标变成一个整体&#xff0c;再把后面的代价换掉然后将与下标无关的直接提出去 解决方案二&#xff1a;利用“蚯蚓”那…

<设计模式>单例模式懒汉和饿汉

目录 一、单例模式概述 二、懒汉模式和饿汉模式 1.饿汉模式 1.1代码实现 1.2实现细节 1.3模式优劣 2.懒汉模式 2.1代码实现 2.2实现细节 2.3模式优劣 三、多线程下的线程安全问题 1.懒汉和饿汉线程安全问题分析 1.1安全的饿汉模式 1.2不安全的懒汉模式 2.懒汉线程…

YOLOv5算法进阶改进(15)— 引入密集连接卷积网络DenseNet

前言:Hello大家好,我是小哥谈。DenseNet(密集连接卷积网络)是一种深度学习神经网络架构,它在2017年由Gao Huang等人提出。DenseNet的核心思想是通过密集连接(dense connection)来促进信息的流动和共享。在传统的卷积神经网络中,每个层的输入只来自于前一层的输出。而在…

Linux下编译EtherCAT主站SOEM-1.4.1

目录 1、SOEM下载 2、CMake安装​​​​​​ 3、编译 环境&#xff1a;Ubuntu1604. 1、SOEM下载 最新版为SOEM-v1.4.0&#xff0c;可以从github下载地址&#xff1a; https://github.com/OpenEtherCATsociety/SOEM 2、CMake安装​​​​​​ 3、编译 解压文件&#xff0c…

BeanUtils.copyProperties()用法总结

BeanUtils.copyProperties()用法总结 大家好&#xff0c;我是免费搭建查券返利机器人赚佣金就用微赚淘客系统3.0的小编&#xff0c;今天我们来谈一谈在Java开发中常用的Bean属性拷贝工具——BeanUtils.copyProperties()&#xff0c;并总结其用法和注意事项。 1. 什么是BeanUt…

WebSocket学习笔记以及用户与客服聊天案例简单实现(springboot+vue)

一&#xff1a;介绍&#xff1a; 二&#xff1a;http协议与websocket对比&#xff1a; 三&#xff1a;websocket协议&#xff1a; 四&#xff1a;实现&#xff1a; 4.1客户端&#xff1a; 4.2服务端&#xff1a; 五&#xff1a;案例&#xff1a; 环境&#xff1a;做一个书店…

分布式任务调度框架XXL-JOB详解

分布式任务调度 概述 场景: 如12306网站根据不同车次设置放票时间点&#xff0c;商品成功发货后向客户发送短信提醒等任务,某财务系统需要在每天上午10天前统计前一天的账单数据 任务的调度是指系统为了完成特定业务&#xff0c;基于给定的时间点&#xff0c;时间间隔&#…

bat脚本 分片抓包并上传到ftp

在批处理脚本中实现分片抓包并上传到FTP服务器是一项比较复杂的任务&#xff0c;因为批处理脚本本身并不直接支持文件分片或FTP上传。但是&#xff0c;你可以结合使用一些外部工具和命令来实现这一功能。 首先&#xff0c;你需要一个用于分片的工具。WinPcap提供了一些工具和库…

【前端】弹框组件

vue2项目&#xff0c;封装的第一版弹框组件使用基于elment-ui 在vue同级创建components/dialog文件夹 里面放paramDialog.vue 父组件引入 dialogVisible要是改变量名&#xff0c;这个也记得改 <ParamDialog :title"title" :dialogVisible.sync"dialogVisibl…

onnx转换为rknn置信度大于1,图像出现乱框问题解决

前言 环境介绍&#xff1a; 1.编译环境 Ubuntu 18.04.5 LTS 2.RKNN版本 py3.8-rknn2-1.4.0 3.单板 迅为itop-3568开发板 一、现象 采用yolov5训练并将pt转换为onnx&#xff0c;再将onnx采用py3.8-rknn2-1.4.0推理转换为rknn出现置信度大于1&#xff0c;并且图像乱框问题…

C#下“Bitmap”转换为“BitmapImage”,“Bitmap”转换成“BitmapSource”

在实际开发中时常遇到“Bitmap”转换为“BitmapImage”&#xff0c;“Bitmap”转换成“BitmapSource”格式转化的开发&#xff0c;特此记录。 1、“Bitmap”转换为“BitmapImage” 要将 System.Drawing.Bitmap 转换为 System.Windows.Media.Imaging.BitmapImage&#xff0c;我…

【服务器】RAID(独立磁盘冗余阵列)

RAID&#xff08;独立磁盘冗余阵列&#xff09; 一、RAID的介绍二、RAID的分类#2-1 RAID 02-2 RAID 1#2-3 RAID 32-4 RAID 52-5 RAID 62-6 RAID 10(先做镜像&#xff0c;再做条带化)2-7 RAID 01&#xff08;先做条带&#xff0c;再做镜像&#xff09;2-8 RAID比较 三、磁盘阵列…

代码随想录刷题第24天

今天正式进入回溯。看了看文章介绍&#xff0c;回溯并不是很高效的算法&#xff0c;本质上是穷举操作。代码形式较为固定。 第一题为组合问题&#xff0c;用树形结构模拟&#xff0c;利用回溯算法三部曲&#xff0c;确定终止条件与单层逻辑&#xff0c;写出如下代码。 不难发现…

负载均衡下webshell连接

目录 一、什么是负载均衡 分类 负载均衡算法 分类介绍 分类 均衡技术 主要应用 安装docker-compose 2.1上传的文件丢失 2.2 命令执行时的漂移 2.3 大工具投放失败 2.4 内网穿透工具失效 3.一些解决方案 总结 一、什么是负载均衡 负载均衡&#xff08;Load Balanc…