当前位置: 首页 > news >正文

C++ Primer 参数传递

欢迎阅读我的 【C++Primer】专栏

专栏简介:本专栏主要面向C++初学者,解释C++的一些基本概念和基础语言特性,涉及C++标准库的用法,面向对象特性,泛型特性高级用法。通过使用标准库中定义的抽象设施,使你更加适应高级程序设计技术。希望对读者有帮助!

在这里插入图片描述
在这里插入图片描述

目录

  • 6.2 参数传递
    • 传值参数
    • 指针形参
    • 传引用参数
    • 使用引用避免拷贝
    • 使用引用形参返回额外信息
    • const形参和实参
    • 指针或引用形参与const
    • 尽量使用常量引用
    • 数组形参
    • 使用标记指定数组长度
    • 使用标准库规范
    • 显式传递一个表示数组大小的形参
    • 数组形参和const
    • 数组引用形参
    • 传递多维数组
    • main:处理命令行选项
    • 含有可变形参的函数
      • initializer_list形参
    • 省略符形参如

6.2 参数传递

每次调用函数时都会重新创建它的形参,并用传入的实参对形参进行初始化。

形参初始化的机理与变量初始化一样。

和其他变量一样,形参的类型决定了形参和实参交互的方式。如果形参是引用类型,它将绑定到对应的实参上;否则,将实参的值拷贝后赋给形参。

当形参是引用类型时,我们说它对应的实参被引用传递(passed by reference)或者函数被传引用调用(called by reference)。和其他引用一样,引用形参也是它绑定的对象的别名;也就是说,引用形参是它对应的实参的别名。

当实参的值被拷贝给形参时,形参和实参是两个相互独立的对象。我们说这样的实参被值传递(passed by value)或者函数被传值调用(called by value)。

传值参数

当初始化一个非引用类型的变量时,初始值被拷贝给变量。此时,对变量的改动不会影响初始值:

int n = 0;// int类型的初始变量
int i = n;//工是n的值的副本
i = 42; //i的值改变;n的值不变

传值参数的机理完全一样,函数对形参做的所有操作都不会影响实参。例如,在fact函数内对变量val执行递减操作:

ret* = val--; // 将val的值域减1

尽管fact函数改变了val的值,但是这个改动不会影响传入fact的实参.调用fact(i)不会改变i的值。

指针形参

指针的行为和其他非引用类型一样。当执行指针拷贝操作时,拷贝的是指针的值。拷贝之后,两个指针是不同的指针。因为指针使我们可以间接地访问它所指的对象,所以通过指针可以修改它所指对象的值:

int n = 0,i = 42;
int *p = &n,*q = &i; //p指向n; q指向i
*p = 42; //n的值改变;P不变
p=q; //p现在指向了i;但是i和n的值都不变

指针形参的行为与之类似:

//该函数接受一个指针,然后将指针所指的值置为0
void reset(int*ip)
{*ip=03//改变指针ip所指对象的值ip=0//只改变了ip的局部拷贝,实参未被改变
}

调用reset函数之后,实参所指的对象被置为0,但是实参本身并没有改变:

int i = 42;
reset(&i);                  //改变i的值而非i的地址
cout<<"i = "<< i << endl;   //输出i=0

熟悉C的程序员常常使用指针类型的形参访问函数外部的对象,在C++语言中,建议使用引用类型的形参替代指针。

传引用参数

回忆过去所学的知识,我们知道对于引用的操作实际上是作用在引用所引的对象上

int n = 0,i=42;
int&r = n; //i绑定了n(即i是n的另一个名字)
i=423;//现在n的值是42
r = i; //现在n的值和i相同
i = r; //i的值和n相同

引用形参的行为与之类似。通过使用引用形参,允许函数改变一个或多个实参的值。举个例子,我们可以改写上一小节的reset程序,使其接受的参数是引用类型而非指针:

//该函数接取一个int对象的引用,然后将对象的值置为0
void reset(int i)//i是传给reset函数的对象的另一个名称
{i=0; //改变了i所引对象的值
}

和其他引用一样,引用形参绑定初始化它的对象。当调用这一版本的reset函数时,i绑定我们传给函数的int对象,此时改变i也就是改变i所引对象的值。此例中,被改变的对象是传入reset的实参。

调用这一版本的reset函数时,我们直接传入对象而无须传递对象的地址:

int j=42;
reset(j);//了采用传引用方式,它的值被改变
cout<< "j=" << j <<endl;//输出于=0

在上述调用过程中,形参i仅仅是j的又一个名字。在reset内部对i的使用即是对j的使用。

使用引用避免拷贝

拷贝大的类类型对象或者容器对象比较低效,甚至有的类类型(包括IO类型在内)根本就不支持拷贝操作。当某种类型不支持拷贝操作时,函数只能通过引用形参访问该类型的对象。举个例子,我们准备编写一个函数比较两个string对象的长度。因为string对象可能会非常长,所以应该尽量避免直接拷贝它们,这时使用引用形参是比较明智的选择。又因为比较长度无须改变string对象的内容,所以把形参定义成对常量的引用

//比较两个string对象的长度
bool isShorter(const string&s1,const string&s2)
{return s1.size()<s2.size();
}

当函数无须修改引用形参的值时最好使用常量引用。

加果函就无须改变引用形参的值,最好将其声明为常量引用。

使用引用形参返回额外信息

一个函数只能返回一个值,然而有时函数需要同时返回多个值,引用形参为我们一次返回多个结果提供了有效的途径。举个例子,我们定义一个名为find_char的函数,它返回在string对象中某个指定字符第一次出现的位置。同时,我们也希望函数能返回该字符出现的总次数。

该如何定义函数使得它能够既返回位置也返回出现次数呢?一种方法是定义一个新的数据类型,让它包含位置和数量两个成员。还有另一种更简单的方法,我们可以给函数传入一个额外的引用实参,令其保存守符出现的次数:

//返回s中c第一次出现的位置家引
//引用形参 occeurs 负责统计c出现的总次数
string::size_type find_char(const string&s,char c, string::size_type&occurs){
auto ret=s.size();//第一次出现的位置(如果有的话)
oceurs=0;//设置表示出现次数的形参的值
for(decltype(ret)i=0;i!=s.size();++i)1if(s[i]=c){if(ret==s.size())ret=i;//记录c第一次出现的位置++occur;//将出现的次数加1
}
return ret;//出现次数通过ccours隐式地返回
}

当我们调用find_char函数时,必须传入三个实参:作为查找范围的一个string对象、要找的字符以及一个用于保存字符出现次数的size_type对象。假设s是一个string对象,ctr是一个size_type对象,则我们通过如下形式调用find_char函数:

auto index=find_char(s,'o',ctr);

调用完成后,如果string对象中确实存在o,那么ctr的值就是o出现的次数,index指向o第一次出现的位置;否则如果string对象中没有o,index等于s.size()而ctr等于0。

const形参和实参

当形参是const时,顶层const作用于对象本身:

const int ci=42;//不能改变c,const是顶层的
int i = ci;    //正确;当拷贝ci时,忽略了它的顶层const
int*const p = &i;//const是顶层的,不能给p赋值
*p = 0;//正确:通过p改变贞象的肉容是允许的,现在i变成了0

和其他初始化过程一样,当用实参初始化形参时会忽略掉顶层const。换句话说,形参的顶层const被忽略掉了。当形参有顶层const时,传给它常量对象或者非常量对象都是可以的:

voi fcn(const int){/*fcn能够读取,但是不能向i写值*/}

调用fcn函数时,既可以传入const int也可以传入int。忽略掉形参的顶层const可能产生意想不到的结果:

void fcn(const int){/*fcn能够读取,但是不能向i写值*/}
void fcn(int i){/*…*///错误:重复定义了fcn(int)

在C++语言中,允许我们定义若干具有相同名字的函数,不过前提是不同函数的形参列表应该有明显的区别。因为顶层const被忽略掉了,所以在上面的代码中传入两个fcn函数的参数可以完全一样。因此第二个fcn是错误的,尽管形式上有差异,但实际上它的形参和第一个fcn的形参没什么不同。

指针或引用形参与const

形参的初始化方式和变量的初始化方式是一样的,所以回顾通用的初始化规则有助于理解本节知识。我们可以使用非常量初始化一个底层const对象,但是反过来不行;同时一个普通的引用必须用同类型的对象初始化。

int i=42;
const int*cp=&i;//正确:但是cp不能改变iconst int &r=i;//正确:但是r不能改变i
const int &r2=42;//正确int*p=cp;//错误:p的类型和cp的类型不匹配
int&r3 = r;//错误:r3的类型和的类型不匹配
int&r4= 42; //错误:不能用字面值初始化一个非常量引用

将同样的初始化规则应用到参数传递上可得如下形式:

int i=0;
const int ci=i;
string::size_type ctr=0;
reset(&i);//调用形参类型是int*的reset函数
reset(&ci)}//错误:不能用指向const int对象的指针初始化int*
reset(i);//调用形参类型是intg的reset函数
reset(ci);//错误:不能把普通引用绑定到const对象ci上
reset(42);//错误:不能把普通应用绑定到字面值上
reset(ctr);//错误:类型不匹配,ct是无符号类型
//正确:find_chaz的第一个形参是对常量的引用
find_char(“Hello World!,'o',ctr);

要想调用引用版本的reset,只能使用int类型的对象,而不能使用字面值、求值结果为int的表达式、需要转换的对象或者const int类型的对象。类似的,要想调用指针版本的reset只能使用int*。

另一方面,我们能传递一个字符串字面值作为find_char的第一个实参,这是因为该函数的引用形参是常量引用,而C++允许我们用字面值初始化常量引用。

尽量使用常量引用

把函数不会改变的形参定义成(普通的)引用是一种比较常见的错误,这么做带给函数的调用者一种误导,即函数可以修改它的实参的值。此外,使用引用而非常量引用也会
极大地限制函数所能接受的实参类型。就像刚刚看到的,我们不能把const对象、字面值或者霁要类型转换的对象传递给普通的引用形参。

这种错误绝不像看起来那么简单,它可能造成出人意料的后果。的find_char函数为例,那个函数(正确地)将它的string类型的形参定义成常量引用。假如我们把它定义成普通的string:

//不良设计:第一个形参的类型应该是conststringg
string::size_type find_char(string&s,charc,string::size_type &occurs);

则只能将find_char函数作用于string对象。类似下面这样的调用

find_char("Hello World",'o',ctr);

将在编译时发生错误。

还有一个更难察觉的问题,假如其他函数(正确地)将它们的形参定义成常量引用,那么第二个版本的find_char无法在此类函数中正常使用。举个例子,我们希望在一个判断string对象是否是句子的函数中使用find_char:

bool is_sentence(const string&s)
{
//如果在s的末尾有且只有一个句号,则s是一个句子
strtng::size_type ctr=0;
return find_char(8,'.',ctr)==s.size()-1 &&ctr==1;
}

如果find_char的第一个形参类型是string&,那么上面这条调用find_char的语句将在编译时发生错误。原因在于s是常量引用,但find_char被(不正确地)定义成只能接受普通引用。

解决该问题的一种思路是修改is_sentence的形参类型,但是这么做只不过转移了错误而已,结果是is_sentence函数的调用者只能接受非常量string对象了。

正确的修改思路是改正find_char函数的形参。如果实在不能修改find_char,就在is_sentence内部定义一个string类型的变量,令其为s的副本,然后把这个string对象传递给find_char。

数组形参

数组的两个特殊性质对我们定义和使用作用在数组上的函数有影响,这两个性质分别是:不允许拷贝数组以及使用数组时(通常)会将其转换成指针。因为不能拷贝数组,所以我们无法以值传递的方式使用数组参数。因为数组会被转换成指针,所以当我们为函数传递一个数组时,实际上传递的是指向数组首元素的指针。

尽管不能以值传递的方式传递数组,但是我们可以把形参写成类似数组的形式:

//尽管形式不同,但这三个print函数是等价的
//每个函数都有一个const int*类型的形参
void print(const int*);
void print(const int[]);//可以看出来,函数的意图是作用于一个数组
void print(const int[10]);//这里的维度表示我们期望数组含有多少元素,实际不一定

尽管表现形式不同,但上面的三个函数是等价的:每个函数的唯一形参都是const int* 类型的。当编译器处理对print函数的调用时,只检查传入的参数是否是const int* 类型:

int i=0,j[2]={0};
print(&i);//正确:&i的类型是int*
print(j);//正确:了转换成int*并指向j[0]

如果我们传给print函数的是一个数组,则实参自动地转换成指向数组首元素的指针,数组的大小对函数的调用没有影响。

和其他使用数组的代码一样,以数组作为形参的函数必须确保使用数组时不会越界。

因为数组是以指针的形式传递给函数的,所以一开始函数并不知道数组的确切尺寸,调用者应该为此提供一些额外的信息。管理指针形参有三种常用的技术。

使用标记指定数组长度

管理数组实参的第一种方法是要求数组本身包含一个结束标记,使用这种方法的典型示例是C风格字符串,C风格字符串存储在字符数组中,并且在最后一个字符后面跟着一个宏字符。函数在处理C风格字符串时遇到空字符停止:

void print(const char* cp)
{if(cp)//若cp不是一个空指针while(*cp)//只要指针所指的字符不是空字符cout<<*cp++;//输出当前字符并将指针向前移动一个位置
}

这种方法适用于那些有明显结束标记且该标记不会与普通数据混淆的情况,但是对于像int这样所有取值都是合法值的数据就不太有效了。

使用标准库规范

管理数组实参的第二种技术是传递指向数组首元素和尾后元素的指针,这种方法受到了标准库技术的启发,关于其细节将在第I部分详细介绍。使用该方法,我们可以按照如下形式输出元素内容:

void print(const int*beg,const int*end)
{//输出beg到end之间(不含end)的所有元素while(beg=end)cout<<*beg++<<endl;//输出当前元素并将指针向前移动一个位置
}while循环使用解引用运算符和后置递减运算符输出当前元素并在数组内将beg向前移动一个元素,当beg和end相等时结束循环。为了调用这个函数,我们需要传入两个指针:一个指向要输出的首元素,另一个指向尾元素的下一位置:```cpp
int j[2] ={0,1};
//j转换成指向它首元素的指针
//第二个实参是指向j的尾后元素的指针
print(begin(j),end(j))//begin和end函数

只要调用者能正确地计算指针所指的位置,那么上述代码就是安全的。在这里,我们使用标准库begin和end函数提供所需的指针。

显式传递一个表示数组大小的形参

第三种管理数组实参的方法是专门定义一个表示数组大小的形参,在C程序和过去的C++程序中常常使用这种方法。使用该方法,可以将print函数重写成如下形式:

//const int ia[]等价于const int *ia
//size表示数组的大小,将它显式地传给函数用于控制对ia元素的访问
void print(const int ia[],size_t size)
{
for(size_t i=0;i!=size;++i){cout<<ia[i]<<endl;
}这个版本的程序通过形参size的值确定要输出多少个元素,调用print函数时必须传入这个表示数组大小的值:```cpp
int j[]={0,1};//大小为2的整型数组
print(j,end(j)-begin(j))

只要传递给函数的size值不超过数组实际的大小,函数就是安全的。

数组形参和const

我们的三个print函数都把数组形参定义成了指向const的指针。当函数不需要对数组元素执行写操作的时候,数组形参应该是指向const的指针。只有当函数确实要改变元素值的时候,才把形参定义成指向非常量的指针。

数组引用形参

C++语言允许将变量定义成数组的引用,基于同样的道理,形参也可以是数组的引用。此时,引用形参绑定到对应的实参上,也就是绑定到数组上:

//正确:形参是数组的引用,维度是类型的一部分
void print(int(&arr)[10])
{
for(auto elem:arr)cout << elem << endl;
}

因为数组的大小是构成数组类型的一部分,所以只要不超过维度,在函数体内就可以放心地使用数组。但是,这一用法也无形中限制了print函数的可用性,我们只能将函数作用于大小为10的数组:

int i= 0,j[2]={0,1};
int k[10]={0,1,2,4,5,6,7,8,9};
print(&i);//错误:实参不是含有10个整数的数组
print(j);//错误:实参不是含有10个整数的数组
print(k);//正确:实参是含有10个整数的数组

传递多维数组

我们曾经介绍过,在C++语言中实际上没有真正的多维数组所谓多维数组其实是数组的数组。

和所有数组一样,当将多维数组传递给函数时,真正传递的是指向数组首元素的指针。因为我们处理的是数组的数组,所以首元素本身就是一个数组,

指针就是一个指向数组的指针。数组第二维(以及后面所有维度)的大小都是数组类型的一部分,不能省略:

//matrix指向数组的首元素,该数组的元素是由10个整数构成的数组
void print(int(*matrix)[10],int rowSize){}//

上述语句将matrix声明成指向含有10个整数的数组的指针。

再一次强调,*matrix两端的括号必不可少:

int* matrix[10]; // 10个指针构成的教组
int(*matrix)[10];//指向吴有10个整敬的数组的指针

我们也可以使用数组的语法定义函数,此时编译器会一如既往地忽略掉第一个维度,所以最好不要把它包括在形参列表内:

//等价定义
void print(int matrix[][10],int rowSize){/*...*/}

matrix的声明看起来是一个二维数组,实际上形参是指向含有10个整数的数组的指针。

main:处理命令行选项

main函数是演示C++程序如何向函数传递数组的好例子。到目前为止,我们定义的main函数都只有空形参列表:

int main()

然而,有时我们确实需要给main传递实参,一种常见的情况是用户通过设置一组选项来确定函数所要执行的操作。例如,假定main函数位于可执行文件prog之内,我们可以向程序传递下面的选项:

prog -d -o ofile data0

这些命令行选项通过两个(可选的)形参传递给main函数:

int main(int argc char*argv[]){...}

第二个形参argv是一个数组,它的元素是指向C风格字符串的指针;第一个形参argc表示数组中字符串的数量。因为第二个形参是数组,所以main函数也可以定义成:

int main(int argc char *argv){}

其中argv指向char*。

当实参传给main函数之后,argv的第一个元素指向程序的名字或者一个空字符串,接下来的元素依次传递命令行提供的实参。最后一个指针之后的元素值保证为0。

以上面提供的和命令行为例,argc应该等于5,argv应该包含如下的C风格字符串:

argv[0]="prog"; //或者argv[0]也可以指向一个空字符串
argv[1]="-d";
argv[2]="-o";
argv[3]="ofile";
argv[4]="data0";
argv[5]=0;

当使用argv中的实参时,一定要记得可选的实参从argv[1]开始;argv[0]保存程序的名字,而非用户输入。

含有可变形参的函数

有时我们无法提前预知应该向函数传递几个实参。例如,我们想要编写代码输出程序产生的错误信息,此时最好用同一个函数实现该项功能,以便对所有错误的处理能够整齐划一。然而,错误信息的种类不同,所以调用错误输出函数时传递的实参也各不相同。

为了编写能处理不同数量实参的函数,C++11新标准提供了两种主要的方法:如果所有的实参类型相同,可以传递一个名为initializer_1ist的标准库类型;如果实参的类型不园,我们可以编写一种特殊的函数,也就是所谓的可变参数模板。

C++还有一种特殊的形参类型(即省略符),可以用它传递可变数量的实参。本节将简要介绍省略符形参,不过需要注意的是,这种功能一般只用于与C函数交互的接口程序。

initializer_list形参

如果函数的实参数量未知但是全部实参的类型都相同,我们可以使用initializer_list类型的形参。initializer_list是一种标准库类型,用于表示棠种特定类型的值的数组。initialtzer_list类型定义在同名的头文件中。

表6.1:inttializer_list提供的操作

initializer_list lst默认初始化;0类型元素的守列表
initializer_lists 1st{a,b,c…}lst的元素数量和初始值一样多;lst的元素是对应初始值的副本;列表中的元素是const
lst2 lst2=lst拷贝或赋值一个initializer_1ist对象不会拷贝列表中的元素;拷贝后,原始列表和副本共享元素
lst.size()列表中的元素数量
lst.begin()返回指向lst中首元素的指针
lst.end()返回指向lst中尾元素下一位置的指针

和vector一样,initializer 1ist也是一种模板类型。定义initializer_1ist对象时,必须说明列表中所含元素的类型:

initializer_list<string>1s; //initializer_litst的元素类型是string
initializer_list<int> li;   //initializer_list的元素类型是int

和vector不一样的是,initializer_list对象中的元素永远是常量值,我们无法改变initializer_list对象中元素的值。

我们使用如下的形式编写输出错误信息的函数,使其可以作用于可变数量的实参:

void error_msg(initializer_list<string>i1)
{for(auto beg=i1.begin();beg!=i1.end();++beg)cout<<*beg<<" ";cout<<endl;
}

作用于initialtzer_list对象的begin和end操作类似于vector对应的成员。begin()成员提供一个指向列表首元素的指针,end()成员提供一个指向列表尾后元素的指针。我们的函数首先初始化beg令其表示首元素,然后依次遍历列表中的每个元素。在循环体中,解引用beg以访问当前元素并输出它的值。

如果想向initializer_list形参中传递一个值的序列,则必须把序列放在一对花括号内:

//expected和actual是string对象
if(expected!=actual)error_msg({"functionX",expected,actual});
elseerror_msg({"functionX","okay"});

在上面的代码中我们调用了同一个函数error_msg,但是两次调用传递的参数数量不同:第一次调用传入了三个值,第二次调用只传入了两个。含有initializer_list形参的函数也可以同时拥有其他形参。例如,调试系统可能有个名为ErrCode的类用来表示不同类型的错误,因此我们可以改写之前的程序,使其包含一个initializer_list形参和一个ErrCode形参:

void error_msg(ErrCode,initializer_list<string>i1)
{cout<K<e.msg()<< ":";for(const auto&elem:11)cout<<elem<<" ";cout<<endl;
}
因为initializer_list包含begin和end成员,所以我们可以使用范围for循环处理其中的元素。和之前的版本类似,这段程序遍历传给11形参的列表值,每次迪代时访问一个元素。为了调用这个版本的erro_rmsg函数,需要额外传递一个ErrCode实参:```cpp
if(expected!=actual)
error_msg(ErrCode(42),{"functitonX",expected,actual});
else
error_msg(ErzCode(0),{"EuncttonX","okay"});

省略符形参如

省略符形参是为了便于C++程序访问树些特殊的C代码而设置的,这些代码使用了名为varargs的C标准库功能。通常,省略符形参不应用于其他目的。你的C编详器文档会描述如何使用varargs。

省略符形参应该仅仅用于C和C++通用的类型.特别应该注意的是,大多数类型的对象在传递给省略符形参时都无法正确拷贝。

省略符形参只能出现在形参列表的最后一个位置,它的形式无外乎以下两种:

void foo(parm_list,...);
void foo(...);

第一种形式指定了foo函数的部分形参的类型,对应于这些形参的实参将会执行正常的类型检查。省略符形参所对应的实参无须类型检查。在第一种形式中,形参声明后面的逗号是可选的。

相关文章:

C++ Primer 参数传递

欢迎阅读我的 【CPrimer】专栏 专栏简介&#xff1a;本专栏主要面向C初学者&#xff0c;解释C的一些基本概念和基础语言特性&#xff0c;涉及C标准库的用法&#xff0c;面向对象特性&#xff0c;泛型特性高级用法。通过使用标准库中定义的抽象设施&#xff0c;使你更加适应高级…...

2.7 静态方法/构造函数Mock

静态方法/构造函数Mock 在单元测试中&#xff0c;静态方法和构造函数的Mock是相对复杂的需求&#xff0c;因为Mockito的核心设计基于对象实例的模拟。然而&#xff0c;通过扩展工具或特定技巧&#xff0c;可以实现对这些场景的处理。本章详解两种主流方案&#xff1a;PowerMoc…...

注册Gmail如何跳过手机验证环节?

很多小伙伴在注册Gmail的时候都会遇到一个难题&#xff1a;手机号码验证&#xff0c;有可能包括了“手机号无法验证” “国内手机号验证失败” “收不到验证码”等等问题&#xff0c;但 根据真实案例&#xff0c;还有部分人则是“幸运地”没有手机号验证环节&#xff0c;那么今…...

【算法专场】哈希表

目录 前言 哈希表 1. 两数之和 - 力扣&#xff08;LeetCode&#xff09; 算法分析 算法代码 面试题 01.02. 判定是否互为字符重排 ​编辑算法分析 算法代码 217. 存在重复元素 算法分析 算法代码 219. 存在重复元素 II 算法分析 算法代码 解法二 算法代码 算法…...

5、pod 详解 (kubernetes)

pod 详解 &#xff08;kubernetes&#xff09; Pod 的基础概念pause 容器Pod 的分类与创建自主式 Pod控制器管理的 Pod静态 Pod Pod容器的分类基础容器&#xff08;infrastructure container&#xff09;初始化容器&#xff08;initcontainers&#xff09;应用容器&#xff08;…...

二叉树详解:Java实现与应用

在计算机科学中&#xff0c;数据结构是构建高效算法的基石&#xff0c;而二叉树作为一种基础且重要的树形结构&#xff0c;在诸多领域都有着广泛应用&#xff0c;如数据库索引、文件系统、编译器设计等。本文将从基础概念入手&#xff0c;带你逐步深入理解二叉树&#xff0c;并…...

GPT和BERT

笔记来源&#xff1a; Transformer、GPT、BERT&#xff0c;预训练语言模型的前世今生&#xff08;目录&#xff09; - B站-水论文的程序猿 - 博客园 ShusenWang的个人空间-ShusenWang个人主页-哔哩哔哩视频&#xff08;RNN模型与NLP应用&#xff09; 一、GPT 1.1 GPT 模型的…...

【工业安全】-CVE-2024-30891- Tenda AC18路由器 命令注入漏洞

1.漏洞描述 2.漏洞复现 2.1 qemu-user 模拟&#xff1a; 2.2 qemu-system模拟&#xff1a; 3.漏洞分析 4.poc代码&#xff1a; 1.漏洞描述 漏洞编号&#xff1a;CVE-2024-30891 漏洞名称&#xff1a;Tenda AC18 命令注入 威胁等级&#xff1a;高危 漏洞详情&#xff1a;Ten…...

如何从0开始将vscode源码编译、运行、打包桌面APP

** 网上关于此的内容很少&#xff0c;今天第二次的完整运行了&#xff0c;按照下文的顺序走不会出什么问题。最重要的就是环境的安装&#xff0c;否则极其容易报错&#xff0c;请参考我的依赖版本以及文末附上的vscode官方指南 ** 第一步&#xff1a;克隆 VSCode 源码 首先…...

登录弹窗效果

1&#xff0c;要求 点击登录按钮&#xff0c;弹出登录窗口 提示1&#xff1a;登录窗口 display:none 隐藏状态&#xff1b; 提示2&#xff1a;登录按钮点击后&#xff0c;触发事件&#xff0c;修改 display:block 显示状态 提示3&#xff1a;登录窗口中点击关闭按钮&#xff0…...

wps或office的word接入豆包API(VBA版本)

直接上代码&#xff0c;由于时间匆忙&#xff0c;以后写个详细的教程 #If VBA7 ThenPrivate Declare PtrSafe Function URLDownloadToFile Lib "urlmon" Alias "URLDownloadToFileA" (ByVal pCaller As Long, ByVal szURL As String, ByVal szFileName As…...

深入浅出 Python Logging:从基础到进阶日志管理

在 Python 开发过程中&#xff0c;日志&#xff08;Logging&#xff09;是不可或缺的调试和监控工具。合理的日志管理不仅能帮助开发者快速定位问题&#xff0c;还能提供丰富的数据支持&#xff0c;让应用更具可观测性。本文将带你全面了解 Python logging 模块&#xff0c;涵盖…...

系统巡检脚本分享:守护服务器的“健康卫士”

在日常的运维工作中&#xff0c;系统巡检是一项至关重要的任务。它可以帮助我们及时发现服务器的潜在问题&#xff0c;确保系统的稳定运行。今天&#xff0c;我想和大家分享一个实用的系统巡检脚本&#xff0c;它能够帮助我们快速、全面地检查服务器的健康状况。 一、为什么需…...

【Elasticsearch】运行时字段(Runtime Fields)索引时定义运行时字段

在 Elasticsearch 中&#xff0c;运行时字段&#xff08;Runtime Fields&#xff09;是一种在查询时动态计算的字段&#xff0c;而不是在索引时预先存储的字段。运行时字段为数据处理提供了极大的灵活性&#xff0c;尤其是在处理结构不固定的日志数据或需要动态生成字段值的场景…...

C++从入门到实战(四)C++引用与inline,nullptr

C从入门到实战&#xff08;四&#xff09;C引用与inline&#xff0c;nullptr 前言一、C 引用&#xff08;一&#xff09;什么是引用&#xff08;二&#xff09;引用的特点&#xff08;三&#xff09;引用作为函数参数&#xff08;四&#xff09;引用作为函数返回值&#xff08;…...

DeepSeek 助力 Vue 开发:打造丝滑的卡片(Card)

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 Deep…...

Azure Synapse Dedicated SQL Pool统计指定表中各字段的空值、空字符串或零值比例

-- 创建临时表存储结果 CREATE TABLE #Results (DatabaseName NVARCHAR(128),TableName NVARCHAR(128),ColumnName NVARCHAR(128),DataType NVARCHAR(128),NullOrEmptyCount INT,TotalRows INT,Percentage DECIMAL(10,2) );DECLARE db_name SYSNAME DB_NAME(); -- 获取当前数…...

【深度学习】计算机视觉(CV)-目标检测-SSD(Single Shot MultiBox Detector)—— 单次检测多框检测器

&#x1f539; SSD&#xff08;Single Shot MultiBox Detector&#xff09;—— 单次检测多框检测器 1️⃣ 什么是 SSD&#xff1f; SSD (Single Shot MultiBox Detector) 是一种用于 目标检测&#xff08;Object Detection&#xff09; 的 深度学习模型&#xff0c;由 Wei L…...

力扣100. 相同的树(利用分解思想解决)

Problem: 100. 相同的树 文章目录 题目描述思路Code 题目描述 思路 题目要求判断两个二叉树是否完全相同&#xff0c;而此要求可以利用问题分解的思想解决&#xff0c;即判断当前节点的左右子树是否完全相同&#xff0c;而在二叉树问题分解的一般题目中均会带有返回值&#xff…...

在SpringBoot服务器端采购上,如何选择操作系统、Cpu、内存和带宽、流量套餐

在Spring Boot服务器端采购时&#xff0c;选择操作系统、CPU、内存、带宽和流量套餐需根据应用需求、预算和性能要求综合考虑。以下是具体建议&#xff1a; 1. 操作系统 Linux发行版&#xff08;如Ubuntu、CentOS&#xff09;&#xff1a;适合大多数Spring Boot应用&#xff…...

我的新书《青少年Python趣学编程(微课视频版)》出版了!

&#x1f389; 激动人心的时刻来临啦&#xff01; &#x1f389; 小伙伴们久等了&#xff0c;我的第一本新书 《青少年Python趣学编程&#xff08;微课视频版&#xff09;》 正式出版啦&#xff01; &#x1f4da;✨ 在这个AI时代&#xff0c;市面上的Python书籍常常过于枯燥&…...

elementUI rules 判断 el-cascader控件修改值未生效

今天修改一个前端项目&#xff0c;增加一个多选字段&#xff0c;使用的是el-cascader控件&#xff0c;因页面是通过引用子页面组件形式使用&#xff0c;出现一个点选后再勾选原有值&#xff0c;输入框内不展示或取消后的也未正常隐藏&#xff0c;如果勾选的值是全新的则其他已选…...

深度学习与人工智能:解锁未来的无限可能

在当今这个科技飞速发展的时代&#xff0c;深度学习和人工智能已不再只是科幻小说中的概念&#xff0c;它们正以惊人的速度渗透到我们生活的方方面面&#xff0c;从智能手机上的语音助手到医疗领域的疾病诊断&#xff0c;从自动驾驶汽车到金融市场的风险预测&#xff0c;其影响…...

pwa应用进阶2-动态加载manifest.json文件

接pwa应用进阶-区分AB面-添加安装按钮而且区分不同的系统和浏览器的各种情况继续优化&#xff0c;主要是让manifest.json文件动态加载。 pwa应用进阶2-动态加载manifest.json文件 主要用途如下&#xff1a; 动态切换PWA的清单文件&#xff0c;例如根据不同的语言或者主题加载不…...

UI用例调试_元素能定位到且不在frame内_无法点击/录入文本

关于单据新增&#xff0c;编辑子集信息遇到的2个阻塞点&#xff0c;做记录已供后续参考 1、新增按钮元素能定位&#xff0c;就是无法点击 实现效果&#xff1a; 单据新增时&#xff0c;前面单据数据编辑完之后&#xff0c;开始新增证件信息&#xff0c;需要先点击新增按钮。…...

Python的web框架Flask适合哪些具体的应用开发?

Flask 适用的具体应用及实现案例代码 Flask 是一个轻量级的 Web 应用框架,以其简洁性和灵活性而广受欢迎。以下是 Flask 适合的具体应用场景及相关的实现案例代码: 1. 小型网站或博客 由于 Flask 的简洁性和易于使用的特性,它非常适合用来搭建个人博客或者小型的企业网站…...

oracle使用动态sql将多层级组织展平

ERP或者其他企业管理软件中都会有一张组织机构表&#xff0c;可以写固定sql的方式将其展平获取组织表中的字段信息&#xff0c;如负责人、上级组织负责人、分管领导、成立时间等。但是这种方式有个缺陷&#xff0c;就是如果只写到处理4个层级&#xff0c;那么后期层级增多就无法…...

vue学习笔记10

ChatGPT & Copilot AI 的认知 两个工具 1、ChatGPT 3.5 2、Github Copilot ChatGPT 的基本使用 - Prompt 优化 AI 互动的过程中&#xff0c;容易出现的问题&#xff1a; 1、 AI未能理解问题的核心要点 2、 AI的回答过于宽泛 或 过于具体 3、 AI提供了错误的信息或…...

网络安全常识

随着互联网和移动互联网的持续火热&#xff0c;人们的生活也越来越离不开网络&#xff0c;网络安全&#xff0c;在这个信息化时代显得尤为重要&#xff0c;那么网络攻击和安全&#xff0c;这一攻守之间&#xff0c;主要涵盖哪些要点呢&#xff0c;下面我们就来对此进行抽丝剥茧…...

如何在 Visual Studio Code 中使用 DeepSeek R1 和 Cline?

让我们面对现实吧&#xff1a;像 GitHub Copilot 这样的 AI 编码助手非常棒&#xff0c;但它们的订阅费用可能会在你的钱包里烧一个洞。进入 DeepSeek R1 — 一个免费的开源语言模型&#xff0c;在推理和编码任务方面可与 GPT-4 和 Claude 3.5 相媲美。将它与 Cline 配对&#…...

从Sora到有言:3D视频生成技术的突破与应用

近年来&#xff0c;AIGC领域飞速发展&#xff0c;这个词也越来越高频地出现在了大家的生活中。AIGC 能完成的任务也越来越多&#xff0c;大模型的能力飞速增长 —— 从Deepseek生成文字&#xff0c;到StableDiffusion生成图像&#xff0c;再到Sora可以生成视频。 而现在&#x…...

算法18(力扣136)只出现一次的数字

1、问题 给你一个 非空 整数数组 nums&#xff0c;除了某个元素只出现一次以外&#xff0c;其余每个元素均出现两次。找出那个只出现了一次的元素。 你必须设计并实现线性时间复杂度的算法来解决此问题&#xff0c;且该算法只使用常量额外空间。 2、示例 &#xff08;1&…...

基于HTML5 Canvas 和 JavaScript 实现的烟花动画效果

以下是一个使用 HTML5 Canvas 和 JavaScript 实现的烟花动画效果代码盒子: <!DOCTYPE html> <html> <head><title>烟花效果...

网络变压器的主要电性参数与测试方法(1)

Hqst盈盛&#xff08;华强盛&#xff09;电子导读&#xff1a;网络变压器的主要电性参数与测试方法&#xff08;1&#xff09;.. 今天我们就一起先来看看网络变压器的2个主要电性参数与它的测试方法&#xff1a; 1. 开路电感&#xff08;OCL or Lx----Open Circuit Ind…...

Python + WhisperX:解锁语音识别的高效新姿势

大家好&#xff0c;我是烤鸭&#xff1a; 最近在尝试做视频的质量分析&#xff0c;打算利用asr针对声音判断是否有人声&#xff0c;以及识别出来的文本进行进一步操作。asr看了几个开源的&#xff0c;最终选择了openai的whisper&#xff0c;后来发现性能不行&#xff0c;又换了…...

Qt的isVisible ()函数介绍和判断窗口是否在当前界面显示

1、现象&#xff1a;当Qt的窗口最小化时&#xff0c;isVisible值一定是true&#xff0c;这是正常的。 解释&#xff1a;在Qt中&#xff0c;当你点击窗口的最小化按钮时&#xff0c;Qt内部不会自动调用 hide() 方或 setVisible(false) 来隐藏窗口。相反&#xff0c;它会改变窗口…...

Github 2025-02-12 C开源项目日报 Top7

根据Github Trendings的统计,今日(2025-02-12统计)共有7个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量C项目7Python项目2OpenSSL - 强大的开源加密工具包 创建周期:4012 天开发语言:C协议类型:Apache License 2.0Star数量:23449 个Fork数量:10…...

PostgreSQL 数据类型

PostgreSQL 数据类型 PostgreSQL 是一款功能强大的开源关系型数据库管理系统,它以其出色的性能、灵活的数据类型和强大的扩展性而闻名。在 PostgreSQL 中,数据类型是构建数据库表和执行各种操作的基础。本文将详细介绍 PostgreSQL 中常用的数据类型,并探讨它们的使用场景。…...

synchronized关键字

文章目录 synchronized 关键字介绍synchronized 的内存语义 synchronized 关键字介绍 synchronized 块是 Java 提供的一种原子性 内 置锁&#xff0c; Java 中的每个对象都可以把它当作一个 同步锁来使用 &#xff0c; 这些 Java 内置的使用者看不到的锁被称为内部锁 &#xf…...

MATLAB计算反映热需求和能源消耗的度数日指标(HDD+CDD)(全代码)

目录 度数日(Degree Days, DD)概述计算公式MATLAB计算代码调用函数1:计算单站点的 CDD参考度数日(Degree Days, DD)概述 度数日(Degree Days, DD)是用于衡量建筑、城市和地区的热需求和能源消耗模式的指标。它分为两部分: 加热度日(Heating Degree Days, HDD):当室…...

在Linux中Redis不支持lua脚本的处理方法

redis安装在IP为x.x.x.x的服务器上 redis安装 第一步&#xff0c;安装前&#xff0c;检测系统是否安装了redis。若安装了redis&#xff0c;则需要删除redis&#xff1b;若没有安装redis&#xff0c;则需要安装2.6版本以上的redis。 # 确保Redis版本支持Lua脚本。从Redis 2.6…...

第39周:猫狗识别 2(Tensorflow实战第九周)

目录 前言 一、前期工作 1.1 设置GPU 1.2 导入数据 输出 二、数据预处理 2.1 加载数据 2.2 再次检查数据 2.3 配置数据集 2.4 可视化数据 三、构建VGG-16网络 3.1 VGG-16网络介绍 3.2 搭建VGG-16模型 四、编译 五、训练模型 5.1 上次程序的主要Bug 5.2 修改版…...

SpringBoot自定义starter

首先创建Maven项目 引入依赖 <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-autoconfigure</artifactId><version>3.4.2</version></dependency> </dependencies…...

JVM学习

JVM 1、JVM是一个跨语言的平台&#xff0c;与语言无关 2、java虚拟机规范&#xff1a;一流企业做标准&#xff0c;二流企业做品牌&#xff0c;三流企业做产品 JVM种类 Hotspot&#xff1a;Oracle 公司&#xff0c;有商业版和免费版 open jdk 内部包含免费版本hotspot虚拟机 Jr…...

RAG入门: RetroMAE、BGE、M3、MemoRAG

RAG实际上第一步都是先做Retrieval&#xff0c;关于Retrieval的思路有很多&#xff0c;持续更新&#xff1a; RetroMAE &#xff08;论文RetroMAE: Pre-Training Retrieval-oriented Language Models Via Masked Auto-Encoder&#xff09; RetraoMAE包括两个模块&#xff0c;…...

ruby 的安装

在51cto搜索的资料 ruby on rails的安装 http://developer.51cto.com/art/200906/129669.htm http://developer.51cto.com/art/200912/169391.htm http://developer.51cto.com/art/200908/147276.htm 史上最完整的ruby&#xff0c;rails环境架设配置&#xff08;Apachefast…...

MySQL的备份与还原

备份数据库 使用mysqldump工具是备份MySQL数据库的一种常用方法。mysqldump可以导出数据库的结构和数据到一个SQL文件中&#xff0c;这个文件稍后可以被用来重新创建数据库或恢复数据。以下是mysqldump命令的详细扩写&#xff1a; mysqldump -u <username> -p<passw…...

文心快码|AI重构开发新范式,从工具到人机协同

本系列视频来自百度前端架构师张立理&#xff0c;他在以“应用来了”为主题的2024百度世界大会上&#xff0c;进行了文心快码3.0能力演示&#xff0c;端到端能力展示。 以下视频是关于文心快码全栈编程智能体-AI重构开发新范式 文心快码AI重构开发新范式 百度前端架构师张立理认…...

Windows11+PyCharm利用MMSegmentation训练自己的数据集保姆级教程

系统版本&#xff1a;Windows 11 依赖环境&#xff1a;Anaconda3 运行软件&#xff1a;PyCharm 一.环境配置 通过Anaconda Prompt(anaconda)打开终端创建一个虚拟环境 conda create --name mmseg python3.93.激活虚拟环境 conda activate mmseg 4.安装pytorch和cuda tor…...

方法(构造方法、方法重载、可变参数)

方法&#xff08;Method&#xff09; 方法是组织好的、可以重复使用的代码块&#xff0c;用于实现单一或相关联的功能。方法有助于提高代码的模块化和可读性&#xff0c;并且通过减少代码冗余来促进代码的重用。 一个方法通常包含5中部分组成&#xff1a; 访问修饰符&#xf…...