重生之我要学c++第二课

news2024/11/22 19:35:17

    在上期内容,我们讲述了c++相比于C语言的更简易之处,本期就让我们继续学习相关的知识,了解c++宇宙

引用(起别名)

   

引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空
间,它和它引用的变量共用同一块内存空间。

举个例子,诸葛亮,字孔明,号卧龙先生,你可以叫他诸葛亮,这是他的本名,也可以叫他卧龙先生,这是他的别名。

话不多说上代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;

int main()
{
	int a = 10;
	int& b = a;//<====定义引用类型
	cout << &b << endl;
	cout << &a << endl;
	return 0;
}

看第七行和第八行,我们定义了一个变量a,像第八行那样的操作,就是给a起一个别名,b同a指向的是同一块地址,我们可以验证一下:

运行截图:

可以发现a和b的地址是一样的,也就可以得到一个结论,b值的改变,会导致a的变化

注意:引用类型必须和引用实体是同种类型的

现在大家有一个疑问,这个起外号有啥用呢? 可以告诉大家,在普通场景下,引用操作毫无意义,但是在以下的场景下,作用巨大:

1.输出型参数

   我们以前写一个交换函数,是不是得用指针,因为形参的改变不影响实参,C语言阶段不需要传地址才能实现,而现在我们只需要简单的引用:

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;

void swap(int& ra, int& rb)
{
	int t = ra;
	ra = rb;
	rb = t;
	cout << ra << rb << endl;
}
int main()
{
	int a ,b;
	cin >> a >> b ;
	swap(a, b);
	
	return 0;
}

现在我们不需要复杂的指针引用,只需要把传进的参数换成别名,就可以直接对数值进行更改,因为别名和本身是指向同一个地址,二者相互影响。

引用特性

1. 引用在定义时必须初始化
也就是:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;

int main()
{
	int a = 1;
	int& b;//错误,要进行初始话
	int& b = a;//正确,在定义的地方初始化
	cout << b << endl;
	return 0;
}
2. 一个变量可以有多个引用
    这句话是什么意思呢,通俗的说就是可以给别名起别名,给外号起外号
上代码:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;

int main()
{
	int a = 10;
	int& b = a;//b是a的别名
	int& c = b;//c是b的别名
	//打印看看他们的地址是否相同:
	cout << &a << &b << &c << endl;
	return 0;
}

    仔细阅读代码会发现,我们定义了一个变量a,然后给a取了别名叫b,又给b取了个别名叫c,这样做是完全允许的。谁说人只能由=有一个外号?我不仅仅叫诸葛亮,也叫诸葛孔明,亦可以叫卧龙先生,这三者都是我。

    那么改变c的数值会不会改变b呢,亦会不会改变c呢?让我们运行以下代码:

运行截图:

地址完全一样,这其中的意思相信大家都明白了,三者任意一个的改变,都会改变其他,就像是你问诸葛亮先生吃饭了吗?他回答吃了,那么孔明也吃了,卧龙先生也吃过了。哈哈哈哈是不是很形象。

3. 引用一旦引用一个实体,再不能引用其他实体

当一个变量名称为另一个变量的别名的时候,就不能在称为别的变量的别名:

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
int main()
{
	int a = 10;
	int& b = a;//b是a的别名

	int x = 1;
	b = x;
	cout << b << endl;
	return 0;
}

大家阅读这段代码,b是a的别名,思考一下b=x这句话,是给b赋x的值还是起别名

答案是赋值

这就是 “引用一旦引用一个实体,再不能引用其他实体”的含义。(只能和一个人结婚)

引用复杂的使用场景

1.引用做返回值

大家来看一段这样的代码:

//传值返回
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
int count()
{
	int n = 0;
	n++;
	return n;
}
int main()
{
	int ret = count();
	cout << ret << endl;
	return 0;
}

大家思考一下,我们在调用函数之后,是不是把n返回到主函数之中

相信大家都知道肯定不是,因为这是传值返回,当函数调用结束后,n是不是就被销毁了,因为n是在函数中被定义的临时变量。

只要我们在这段代码稍加改动,就可以将其改成传引用返回

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
int& count()
{
	int n = 0;
	n++;
	return n;
}
int main()
{
	int ret = count();
	cout << ret << endl;
	return 0;
}

仔细区分这两段代码的区别,这段函数的返回值就是n的别名,也就是n的引用,大家想想,这个时候会发生什么事情,返回n的引用会引发什么问题?

前文我们提到,出了作用域,n就会被销毁,既然n已经销毁了,还要返回n的别名,是不是就成了类似于野指针的样子呀。

有些小伙伴会发出这样的疑问,空间都被销毁了,为了什么还能返回他的别名?

这是因为空间的销毁就像是退房一样,这个房间住着n这个人,现在这个房间n不用了,n退了这个房间,但是n还是存在的,而引用这个做法,就像是房间虽然退了,但是悄悄地把钥匙留下了。

这就造成了我们这段程序的结果不准确,有可能是1,也可能是随机值

只要我稍微一变招:

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
int& count()
{
	int n = 0;
	n++;
	return n;
}
int main()
{
	int& ret = count();
	cout << ret << endl;
	cout << ret << endl;

	return 0;
}

 打印的值就会变成这样:

第一次打印ret还是1,第二次就变成了随机值

这涉及到以后要学习的知识,先给大家卖个关子哈哈哈哈,大家记住这个小tip

 再来看这样一段代码:

int& Add(int a, int b)
{
    int c = a + b;
    return c;
}
int main()
{
    int& ret = Add(1, 2);
    Add(3, 4);
    cout << "Add(1, 2) is :"<< ret <<endl;
    return 0;
}

大家觉得这段代码的返回值是3还是7,把自己的答案写在评论区

答案:

为什么会是7呢,这是栈帧有关的知识,给大家看一张图,C语言学的稳定的小伙伴应该是可以看得懂,当然C语言学的不好的同学可以看看博主曾经C语言的博客,希望能给大家带来帮助

 

注意: 如果函数返回时,出了函数作用域,如果返回对象还在(还没还给系统),则可以使用
引用返回,如果已经还给系统了,则必须使用传值返回。

 

传值、传引用效率比较

以值作为参数或者返回值类型,在传参和返回期间,函数不会直接传递实参或者将变量本身直
接返回,而是传递实参或者返回变量的一份临时的拷贝,因此用值作为参数或者返回值类型,效
率是非常低下的,尤其是当参数或者返回值类型非常大时,效率就更低。
光说无用,我们来用代码验证一下
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include <time.h>
using namespace std;
#include <time.h>
struct A { int a[10000]; };

A a;
// 值返回
A TestFunc1() { return a; }

// 引用返回
A& TestFunc2() { return a; }

void TestReturnByRefOrValue()
{
	// 以值作为函数的返回值类型
	size_t begin1 = clock();
	for (size_t i = 0; i < 100000; ++i)
		TestFunc1();
	size_t end1 = clock();

	// 以引用作为函数的返回值类型
	size_t begin2 = clock();
	for (size_t i = 0; i < 100000; ++i)
		TestFunc2();
	size_t end2 = clock();

	// 计算两个函数运算完成之后的时间
	cout << "TestFunc1 time:" << end1 - begin1 << endl;
	cout << "TestFunc2 time:" << end2 - begin2 << endl;
}
int main()
{
	TestReturnByRefOrValue();
	return 0;
}

这段代码里我分别调用了传值返回和传引用返回,并且计算它们的运行时间供大家比较:

可以看出传值返回比传引用返回慢了很多很多,这仅仅只是传个参的小路就差了这么多!!!

所以衍生出一个问题:

什么时候该用传值返回,什么时候该用传引用返回?

1.返回的参数是一个全局变量或者静态对象,就不需要考虑生命周期的事,所以就用引用返回正正好好,提高很多的效率

2.在堆上动态申请的用引用返回,提高效率

引用和指针的区别

在语法概念上引用就是一个别名,没有独立空间,和其引用实体共用同一块空间。(同一地址)

 

int main()
{
int a = 10;
int& ra = a;
cout<<"&a = "<<&a<<endl;
cout<<"&ra = "<<&ra<<endl;
return 0;
}
在底层实现上实际是有空间的,因为引用是按照指针方式来实现的。
int main()
{
int a = 10;
int& ra = a;
ra = 20;
int* pa = &a;
*pa = 20;
return 0;
}

(仔细观察代码,自己运行一下)

我们来看下引用和指针的汇编代码对比:

 

引用和指针的不同点:
1. 引用概念上定义一个变量的别名,指针存储一个变量地址。
2. 引用在定义时必须初始化,指针没有要求
3. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何
一个同类型实体
4. 没有NULL引用,但有NULL指针
5. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32
位平台下占4个字节)
6. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
7. 有多级指针,但是没有多级引用
8. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
9. 引用比指针使用起来相对更安全
在某些角度上,引用和指针是一样的,引用使用的更方便

内联函数

内联函数也是c++来补C语言的一个坑。

宏在C语言的使用相信大家都很熟悉了

宏的优点 :可以批量置换数值

宏最大的缺陷在于宏函数,现给大家看一段实现两数之和的宏函数

define add(x,x) ((x)+(y))
不会写宏函数的自觉去学习C语言,看博主以往的作品就够了。
宏的根本含义就是替换,把前边的表达式替换成后边的,在替换的过程中是很容易出错的,语法错误太多。我们写宏函数的目的就是替换一般函数,宏函数一句话的事,图个方便,但是宏太容易出错,方便又成了麻烦,并且宏还不能调试
1.容易出错,语法坑多
2.不能调试
3.没有类型安全的检查
优点:
1.没有类型的严格限制
2.针对频繁调用的小函数不需要建立栈帧,提高效率
c++祖师爷用了很久很不耐烦,所以祖师爷发明了 inline修饰
我们只需要在函数定义前加上inline
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include <time.h>
using namespace std;
inline int Add(int x, int y)
{
	return x + y;
}
int main()
{
	Add(1, 2);
	return 0;
}

inline的作用就是和宏大差不差,但是没有宏的缺陷,宏函数没有调用的消耗,内联函数也没有,内联函数的特点是在release优化下把这个函数给展开,像编译器展开

博主通过汇编语言给大家看一下内联函数的展开

 

 

刚刚学到这块的时候,博主就在想,如果把所有的函数都搞成内联函数,那效率岂不是爽歪歪了 ,不知大家有没有这样的想法,哈哈哈哈这样的想法是很有想象力,现在博主也给大家解答一下这样是不可以的:

首先,我们是不可以把很大的函数建内联的,更不要说所有的函数,如果一个程序很长,变成内联会导致程序变大的,这叫做程序膨胀

内联的特性:

1. inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会
用函数体替换函数调用,缺陷:可能会使目标文件变大,优势:少了调用开销,提高程序运
行效率。
2. inline对于编译器而言只是一个建议,不同编译器关于inline实现机制可能不同,一般建
议:将函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)、不
是递归、且频繁调用的函数采用inline修饰,否则编译器会忽略inline特性。下图为
《C++prime》第五版关于inline的建议:
       《内联说明只是像编译器发出一个请求,编译器可以选择忽略这个要求》
所以你的函数可不可以内联是编译器说的算

使用内联的小tip:
inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址
了,链接就会找不到。

auto关键字(c++11)

随着程序越来越复杂,程序中用到的类型也越来越复杂,经常体现在:
1. 类型难于拼写
2. 含义不明确导致容易出错
所以祖师爷创立的auto!
上代码:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include <time.h>
using namespace std;
int main()
{
	int a = 0;
	int b = a;
	return 0;
}

我们先定义了一个a,一般我们要定义一个和a同类型的b的时候,是不是就要像代码那样操作,现在有了auto,我们完全可以换个方式:

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include <time.h>
using namespace std;
int main()
{
	int a = 0;
	auto b = a;
	return 0;
}

把b的类型定义为auto,且让b=a,auto就会推到出a的类型,进而影响b,当然大家要分清,这里的a=b不是数值的等于,而是针对于类型。

这种方法走在常规场景下毫无价值,但是随着学习的深入,我们的代码会越来越多,auto的重大作中就显露出来了:

以后我们会学习一个新的类型:

std::vertor<std::string>::iterator

这就是一个我们以后会学的类型名,很长吧,现在我们用它来定义一个变量it
std::vertor<std::string>::iterator it = v.begin();
光是定义一个类型变量代码就如此长,但是用上auto,效果就立马不一样:
auto it = v.begin();
直接缩短了一半的长度!!!
 

但是要【注意】:

使用auto定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推导auto
的实际类型。因此auto并非是一种类型的声明,而是一个类型声明时的占位符,编译器在编
译期会将auto替换为变量实际的类型

auto不能推导的场景

1. auto 不能作为函数的参数
// 此处代码编译失败, auto 不能作为形参类型,因为编译器无法对 a 的实际类型进行推导
void TestAuto ( auto a )
{}
2. auto 不能直接用来声明数组
void TestAuto ()
{
    int a [] = { 1 , 2 , 3 };
    auto b [] = { 4 5 6 };//错误
}
3. 为了避免与 C++98 中的 auto 发生混淆, C++11 只保留了 auto 作为类型指示符的用法
4. auto 在实际中最常见的优势用法就是跟以后会讲到的 C++11 提供的新式 for 循环,还有
lambda 表达式等进行配合使用。

基于范围的for循环(C++11)

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include <time.h>
using namespace std;
int main()
{
	int arr[] = { 1,2,3,4,5 };

	for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		arr[i] *= 2;
	}
	return 0;
}

以前我们对这个数组进行访问,要先求数组的大小,在进行for循环逐个遍历,现在我们来学用范围for访问数组

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include <time.h>
using namespace std;
int main()
{
	int arr[] = { 1,2,3,4,5 };

	for(auto e :arr)
	{
		cout << 2*e << " ";
	}
	return 0;
}

现在我们来访问数组,就可以用这个范围for,这是一个固定的框架,大家把他记下来,就像当初学习for循环的时候那样,会用就行!

指针空值nullptr(C++11)

c++11补了一个c+98的大坑:

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include <time.h>
using namespace std;
void f(int)
{
	cout << " f(int)" << endl;
}
void f(int*)
{
	cout << " f(int*)" << endl;
}
int main()
{
	f(0);
	f(NULL);
	return 0;
}

大家看这段代码,根据我们前边的学习,不难分辨这两个函数构成了函数重载,他们的类型不一样,按理说第一次传参传的是数字0,应该调用第一个函数,第二次传参应该调用第二个函数,因为传的是指针,但是实际却很坑:

 
我们发现两次调用都是第一个函数,这就是c++的一个大坑:
NULL实际是一个宏,在传统的C头文件(stddef.h)中,可以看到如下代码:

 

可以看到,NULL可能被定义为字面常量0,或者被定义为无类型指针(void*)的常量。不论采取何
种定义,在使用空值的指针时,都不可避免的会有错误。
所以为了补坑,c++11用nullper来表示空指针,就能解决到上边的问题
注意:
1. 在使用nullptr表示指针空值时,不需要包含头文件,因为nullptr是C++11作为新关键字引入
的。
2. 在C++11中,sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。
3. 为了提高代码的健壮性,在后续表示指针空值时建议最好使用nullptr。
本期我们把c++补的C语言的坑就基本讲解完了,接下来博主会给大家带来c++的第三课:类与对象。
如果觉得有用的话,不要吝啬你们的点赞收藏关注。

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

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

相关文章

基于 KubeKey 扩容 Kubernetes v1.24 Worker 节点实战

前言 知识点 定级&#xff1a;入门级KubeKey 扩容 Worker 节点openEuler 操作系统的基本配置Kubernets 基本命令 实战服务器配置(架构 1:1 复刻小规模生产环境&#xff0c;配置略有不同) 主机名IPCPU内存系统盘数据盘用途ks-master-0192.168.9.912450100KubeSphere/k8s-mas…

新能源汽车的发展

目录 1.什么是新能源 2.什么是新能源汽车 3.新能源汽车的优点 4.新能源汽车的危害 5.新能源汽车未来的发展 1.什么是新能源 新能源是指与传统能源&#xff08;如化石燃料&#xff09;相比&#xff0c;更具可再生性、清洁性和低碳排放的能源形式。它主要通过利用自然资源和可…

C语言---每天小练习,从大到小输出

题目&#xff1a;从大到小输出 写代码将三个整数数按从大到小输出。 例如&#xff1a; 输入&#xff1a;2 3 1 输出&#xff1a;3 2 1 int main() {// 初始化int a 0;int b 0;int c 0;int d 0;scanf("%d %d %d", &a, &b, &c);if (a < b) {…

Unity Shader - UI/Default shader 优化示例

文章目录 环境优化示例Texture Format : Alpha 8 和 shaderlab : _TextureSampleAddshaderlab : _UIMaskSoftnessX 和 _UIMaskSoftnessYshaderlab _Colorshader ARM Mobile Studio - Graphics Analyzer优化前优化后 环境 Unity : 2020.3.37f1 Pipeline : BRP 优化 做性能优化…

laravel10.x nginx服务推荐配置文件

laravel10.x 服务器配置 如果您正在将应用程序部署到运行Nginx的服务器&#xff0c;则可以使用以下配置文件作为配置web服务器的起点。很可能&#xff0c;此文件需要根据服务器的配置进行自定义。如果你想在管理服务器方面获得帮助&#xff0c;可以考虑使用第一方Laravel服务器…

3分钟搭建一个springboot项目并运行起来

第一步&#xff1a; 创建一个maven项目。 第二步&#xff1a; 导入maven依赖&#xff0c;代码如下&#xff1a; <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http:/…

执行 yum install gcc 报 【-bash: $‘yum\302\240install\302\240gcc‘: 未找到命令】

执行 yum install gcc 报错 找了一圈&#xff0c;执行&#xff1a;sudo apt-get install yum 执行&#xff1a;wget http://yum.baseurl.org/download/3.2/yum-3.2.28.tar.gz 在线下载yum完成 对其进行解压&#xff1a;tar zxvf yum-3.2.28.tar.gz 解压后如下&#xff1a; 执行…

vue3+h5实现虚拟列表

目录 vue3h5实现虚拟列表component / List.vuecomponent / itemComponent.vue使用组件效果 vue3h5实现虚拟列表 安装&#xff1a;npm i vue3-virtual-scroll-listpackage.json "dependencies": {"vue": "^3.2.36","vue3-virtual-scroll-li…

7.20 ARM-A7核心三盏LED灯

思维导图 汇编代码&#xff1a; .text .global _start _start: /**********LED1点灯**************/ RCC_INIT_E:ldr r0,0x50000A28ldr r1,[r0]orr r1,r1,#(0x1 << 4)str r1,[r0]RCC_INIT_F:ldr r0,0x50000A28ldr r1,[r0]orr r1,r1,#(0x1 << 5)str r1,[r0]LED_INI…

SAP 后台作业简单介绍 job

主要事务代码&#xff1a;SM36、SM37 作业设置 1. 一般数据&#xff1a;作业名称&#xff0c;优先级&#xff0c;目标服务器 优先级适用于资源受限情况&#xff0c;目标服务器适用于服务器环境依赖的程序&#xff0c;比如操作服务器文件等类似的处理 2. 开始条件&#xff0c;…

LRU页面置换算法(C语言实现)

1、实验目的 &#xff08;1&#xff09;熟悉虚拟存储器页面置换过程&#xff1b; &#xff08;2&#xff09;通过编写和调试页面置换算法的模拟程序以加深对页面置换算法的理解&#xff1b; &#xff08;3&#xff09;掌握LRU算法的原理&#xff1b; &#xff08;4&#xf…

Ubuntu中安装Vivado软件

文章目录 Ubuntu中安装Vivado加载License修改软件运行权限安装下载器驱动运行Vivado软件连接开发板测试驱动交叉编译器 Ubuntu中安装Vivado 跨系统文件复制的设置在文章Ubuntu 的安装及其设置中已经介绍过了。 在Ubuntu中找到一个需要存放Vivado软件安装包的文件夹&#xff0c…

二分图博弈学习笔记

前言&#xff1a;最近每场训练赛都有博弈题&#xff0c;而且我都被薄纱了。。。真烦 二分图博弈是少有的直接跟图论挂钩的一种博弈模型 一个博弈是二分图博弈应当满足一下条件&#xff1a; 博弈人数为两人&#xff0c;轮流操作 博弈状态转移可以表示成一张二分图 不可访问已…

MySQL 的 crash-safe浅谈

MySql执行流程 MySQL作为当下最流行的开源关系型数据库&#xff0c;有一个很关键和基本的能力&#xff0c;就是必须能够保证数据不会丢。那么在这个能力背后&#xff0c;MySQL是如何设计才能保证不管在什么时间崩溃&#xff0c;恢复后都能保证数据不会丢呢&#xff1f;有哪些…

在Win11的WSL子系统Ubuntu上安装Gnome桌面环境

目录 1. 使用 WSL 在 Win11 上安装 Linux 2. 安装Ubuntu 22.04默认Gnome桌面环境 2.1更新Ubuntu 22.04软件包 2.2 安装Ubuntu桌面环境 2.3 重启服务 2.4 重启Ubuntu 22.04系统 2.5 登录Gnome桌面环境 在Win11上安装ubuntu版linux系统并实现默认Gnome桌面环境&#xff08…

MySQL数据库(八)

目录 一、什么是索引 1.1索引的原理 1.2索引的优缺点 二、索引的使用 2.1查看索引 2.2手动创建索引 2.3删除索引 三、MySQL索引底层的数据结构 3.1 B树 3.2 B树 一、什么是索引 索引是一种特殊的文件&#xff0c;包含着对数据表里所有记录的引用指针。可以对表中的一列或多列创…

深度理解 Spring AOP

一、什么是AOP(面向切面编程)&#xff1f;&#x1f349; AOP 为 Aspect Oriented Programming 的缩写&#xff0c;意思为面向切面编程&#xff0c;是通过预编译方式 和运行期 动态代理 实现程序功能的统一维护的一种技术。 AOP &#xff08;面向切面编程&#xff09;是 OOP&a…

Jmeter 接口自动化和 Python 接口自动化,到底选哪个?

目录 前言&#xff1a; 背景 Jmeter 接口自动化 特点 Python 接口自动化 特点 谈项目 写在最后 前言&#xff1a; JMeter接口自动化和Python接口自动化都是常见的选择。 背景 很多刚接触接口自动化的朋友都会疑惑&#xff0c;市面上 Jmeter 接口自动化&#xff0c;Py…

题目2 文件上传(保姆级教程)

url&#xff1a;http://192.168.154.253:82/ #打开http://XXX:81/&#xff0c;XXX为靶机的ip地址 审题 1、打开题目看到有一个提示&#xff0c;此题目需要绕过WAF过滤规则&#xff0c;上传木马获取webshell&#xff0c;最后从根目录下key.php文件中获得flag 2、开始答题 第一步…

【数据结构】二叉树详解(3)

⭐️ 前言 ✨ 往期链接&#xff1a;【数据结构】二叉树详解(1) 在第一篇二叉树文章中&#xff0c;我们探讨了二叉树的链式结构定义与实现。二叉的遍历包含( 前序/中序/后序遍历 )及代码实现和递归流程图的详细讲解。还有一些二叉树的其他接口定义与实现&#xff0c;包含 Binar…