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】专栏 专栏简介:本专栏主要面向C初学者,解释C的一些基本概念和基础语言特性,涉及C标准库的用法,面向对象特性,泛型特性高级用法。通过使用标准库中定义的抽象设施,使你更加适应高级…...
Jupyter lab 无法导出格式 Save and Export Notebook As无法展开
本来尝试jypyter lab如何导出HTML带有侧边导航栏,一顿操作后发现还是没实现。 又突然发现导出其他格式地功能不能用了,浏览器里Save and Export Notebook As展开按钮为灰色打不开。 经典想实现的没实现还把原先的搞坏了。 看了jupyter lab的运行信息发…...

Mac之JDK安装
Mac之JDK安装 一.安装 jdk 打开终端输入命令:java -version 查看是否已安装 JDK Oracle 官方下载地址 根据自己Mac 系统安装 查看 Mac 系统,打开中断命令,输入: uname -a Compressed Archive 是压缩文档,下载的是一个 .tar.gz 压缩包 D…...

OpenEuler学习笔记(三十一):在OpenEuler上搭建仓颉语言开发环境
仓颉语言(Cangjie programming language)相对较为小众,截至2025年,并没有广泛的资料和成熟的通用搭建流程。不过下面为你提供一个较为通用的在OpenEuler上搭建开发环境的大致思路,你可以根据实际情况进行调整。 1. 安…...

2021年全国研究生数学建模竞赛华为杯E题信号干扰下的超宽带(UWB)精确定位问题求解全过程文档及程序
2021年全国研究生数学建模竞赛华为杯 E题 信号干扰下的超宽带(UWB)精确定位问题 原题再现: 一、背景 UWB(Ultra-Wideband)技术也被称之为“超宽带”,又称之为脉冲无线电技术。这是一种无需任何载波,通过发送纳秒…...

【电脑】u盘重装win7
u盘必须8GB以上 1. CPU型号 首先查看CPU的型号看看到底能不能装win7 2. 下载光盘映像文件 网址 看电脑是多少位的机器(32位下载x86 64位下载x64) 一共是这么多个版本按需下载对应的版本 电脑小白推荐无脑下载旗舰版 将链接复制到迅雷进行下载 3. 下载软碟通 网址 下…...

HCIA项目实践--RIP的拓展配置
9.4.7 RIP的拓展配置 (1)RIPV2的手工认证 RIPv2 的手工认证是增强网络安全性的手段。管理员手动配置密钥,路由器在收发 RIPv2 路由更新消息时,会对消息中的认证信息进行检查。发送方添加密钥,接收方用预设密钥验证。若…...

常用架构图:业务架构、产品架构、系统架构、数据架构、技术架构、应用架构、功能架构及信息架构
文章目录 引言常见的架构图I 业务架构图-案例模块功能说明1. 用户界面层 (UI)2. 应用服务层3. 数据管理层4. 基础设施层业务流程图示例技术实现II 功能架构图 -案例功能模块说明1. 船舶监控模块2. 报警管理模块3. 应急响应模块4. 通信管理模块5. 数据分析模块数据管理层基础设施…...

初阶c语言(练习题,猜随机数,关机程序)
目录 第一题,使用函数编写一个随机数,然后自己猜,猜随机数 第二道题(关机程序) 实现代码(关机程序) 实现代码(猜数字) 前言: 学习c语言,学习…...
三维重建(十二)——3D先验的使用
文章目录 零、最近感受和前言一、使用能够快速得到重建初始化的方法1.1 Colmap(多视角)1.2 深度估计(单视角)二、已知形状模板2.1 人脸2.2 人体2.3 动物三、刚性与非刚性约束(变形约束)3.1 刚性变形3.2 非刚性变形四、统计(深度学习)先验——从大量(3D)数据中提取信息…...

DDoS技术解析
这里是Themberfue 今天我们不聊别的,我们聊聊著名的网络攻击手段之一的 DDoS,看看其背后的技术细节。 DoS 了解 DDoS 前,先来讲讲 DoS 是什么,此 DoS 而不是 DOS 操作系统啊。1996年9月6日,世界第三古老的网络服务提供…...

总结:如何在SpringBoot中使用https协议以及自签证书?
总结:如何在SpringBoot中使用https协议以及自签证书? 前提一:什么是http协议?前提二:什么是https协议?一生成自签证书二 将证书转换为PKCS12格式三 配置SpringBoot(1)修改配置文件&a…...

Django开发入门 – 4.创建Django app
Django开发入门 – 4.创建Django app Create A Django App Under An Existing Project By JacksonML 1. 什么是Django app? Django项目面向Web应用程序,它会由一个或多个子模块组成,这些子模块称为apps。 Django apps负责执行完整Web应用程序中涉及…...

安装WPS后,导致python调用Excel.Application异常,解决办法
在使用xlwings编辑excel文件时,默认调用的是“Excel.Application”,如果安装过wps,会导致该注册表为WPS,会导致xlwings执行异常 因为安装过WPS,导致与Excel不兼容的问题,想必大家都听说过。有些问题及时删…...

语言大模型基础概念 一(先了解听说过的名词都是什么)
SFT(监督微调)和RLHF(基于人类反馈的强化学习)的区别 STF(Supervised Fine-Tuning)和RLHF(Reinforcement Learning from Human Feedback)是两种不同的模型训练方法,分别…...
理解 WebGPU 的入口: navigator.gpu
在现代 Web 开发中,WebGPU 已经成为实现高性能图形渲染和计算的强大工具。作为 WebGPU API 的入口点, navigator.gpu 是开发者与 GPU 交互的起点。本文将详细介绍 navigator.gpu 的属性和方法,以及如何通过它初始化 WebGPU 环境。 什…...
Django 创建第一个项目
Django 创建第一个项目 引言 Django 是一个高级的 Python Web 框架,它鼓励快速开发和干净、实用的设计。本指南将带您从头开始创建一个简单的 Django 项目,以便您能够熟悉 Django 的基本结构和概念。 准备工作 在开始之前,请确保您已经安装了 Python 和 Django。以下是安…...
ChatGPT vs DeepSeek详细对比
💡 AI模型发展背景 OpenAI的GPT系列需要数据参数算力,这些要素共同推动了模型的成长。但是,到了GPT-5时代,人类现有的知识精华几乎被学习殆尽,模型的提升空间变得有限。于是OpenAI团队另辟蹊径,尝试模拟人…...
日语学习-日语知识点小记-构建基础-JLPT-N4N5阶段(6):動詞ない形について句型
日语学习-日语知识点小记-构建基础-JLPT-N4&N5阶段(6):動詞ない形について句型 1、前言(1)情况说明(2)工程师的信仰2、知识点(1)~動詞な形 +なければなりません(2)~動詞な形 + なくてもいいです(3)に まで までに :区別3、单词(1)日语单词…...
我的docker随笔46:在x86平台构建龙芯镜像
本文介绍在x86服务器上构建龙芯平台的docker镜像。 前言 去年11月,在龙芯机器上安装了docker工具,并开始尝试研究如何构建龙芯的文件系统。断断续续搞了2个月后,有点结果出来了。前面有文章介绍了如何用debootstrap构建龙芯编译运行环境&…...
基于大模型的 UI 自动化系统
基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...

使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...

超短脉冲激光自聚焦效应
前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应,这是一种非线性光学现象,主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场,对材料产生非线性响应,可能…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...

MFC内存泄露
1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...

Cinnamon修改面板小工具图标
Cinnamon开始菜单-CSDN博客 设置模块都是做好的,比GNOME简单得多! 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序
一、开发环境准备 工具安装: 下载安装DevEco Studio 4.0(支持HarmonyOS 5)配置HarmonyOS SDK 5.0确保Node.js版本≥14 项目初始化: ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...

【Oracle】分区表
个人主页:Guiat 归属专栏:Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...

C# 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
【JavaSE】多线程基础学习笔记
多线程基础 -线程相关概念 程序(Program) 是为完成特定任务、用某种语言编写的一组指令的集合简单的说:就是我们写的代码 进程 进程是指运行中的程序,比如我们使用QQ,就启动了一个进程,操作系统就会为该进程分配内存…...