在C++中,有三种传递参数的方法
-
值传递:将实参的值复制一份传递给形参,函数内对形参的修改不会影响实参的值。
-
地址传递:将实参的地址传递给形参,函数内对形参指向的地址的修改会影响实参的值。
-
引用传递:用实参的别名(即引用)作为函数形参,函数内对形参的修改会直接影响实参的值。
本篇文章将会带领大家通过代码的方式,来了解值传递,引用传递,指针传递,返回值,返回引用的区别。
指针
要想学会指针传递,你首先要明白指针是什么,就好比你要跑步,那你肯定得先学会走路啊,要不然很容易摔个脚朝天。
由于程序在运行过程中,会给你所创建的变量分配一块内存,而指针变量是用来存放这个内存的地址的,通过对指针的解引用(*),就可以获得和修改地址中存放的值。
#include<iostream>
using namespace std;
int main()
{
int a = 3; //声明变量a=3;
cout << "a=" << a << endl; //在控制台显示a的值
cout << "&a=" << &a << endl; //在控制台显示a的地址
int* pa = &a; //声明指针变量pa,并让指针pa指向a
cout << "pa=" << pa << endl; //打印出pa的值
cout << "*pa=" << *pa << endl; //打印出对pa解引用后的值
*pa = 5; //修改pa地址中存放的值
cout << "a=" << a << endl;
cout << "*pa=" << *pa << endl; //在控制台显示a的值和pa的解引用
return 0;
}
这段代码运行之后,我们可以发现,指针变量pa打印出来就是变量a的地址,对pa解引用以后的值,就等于a的值。
在对*pa的值进行修改以后,a的值也发生变化,这是通过对指针解引用,修改内存中存放的值来修改a的值,当然,我们也可以认为*pa就是a的别名,pa是&a的别名。
引用
在C++中,引用的就是一个变量的别名,对应一段内存,这也就意味着我们可以通过引用来修改引用变量的值,废话不多说,直接上代码。
#include<iostream>
using namespace std;
int main()
{
int a = 3;int b; //声明变量a=3和变量b;
int& ra = a; //声明引用ra,此时ra是a的别名
cout << "a=" << a << ",ra=" << ra << endl; //打印出a和ra的值
cout << "&a=" << &a << ",&ra=" << &ra << endl; //打印出a和ra的地址
ra = 5; //修改ra的值
cout << "a=" << a << ",ra=" << ra << endl; //打印出a和ra的值
cout << "&a=" << &a << ",&ra=" << &ra << endl; //打印出a和ra的地址
//&ra=b //让ab成为b的别名(此行报错)
return 0;
}
运行以后,我们发现在对ra进行修改以后,a的值也发生了改变,但是a的地址始终等于ra的地址,在指针中,我们可以知道,指针变量pa指向变量a,pa的值始终等于a的地址,那么我们接着来探索下引用和指针的关系。
既然引用和指针都是修改内存中存放的值来修该变量,那么我们可以认为"引用的本质就是指针"在程序末尾,我们重新让ra作为b的别名,也就是让ra的地址等于b的地址,这时程序报错,所以声明引用初始化以后就无法修改,所以,我们可以认为,引用的本质就是一个指针常量( int* const p)
值传递
值传递是指将实参的值复制给形参,函数内对形参的修改不会影响到实参。这里给大家用代码演示一下。
#include<iostream>
using namespace std;
void func(int num)
{
num=100;
}
int main()
{
int a = 3; //声明变量a=3
cout << "a=" << a << endl; //在控制台显示a的值
func(a); //调用函数func()
cout << "a=" << a << endl; //在控制台显示a的值
return 0;
}
func函数中,对num的值进行了修改,可是在函数调用以后,传入的实参a并未被修改,接下来,我把函数的执行过程移植到main函数中帮助大家理解。
#include<iostream>
using namespace std;
void func(int num)
{
num = 100;
}
int main()
{
int a = 3; //声明变量a=3
cout << "a=" << a << endl; //在控制台显示a的值
//func(a); //调用函数func()
{
int num = a;
num = 100;
}
//运行结束 局部变量num被销毁
}
如此,我们便可以发现,在函数func内部,修改的是局部变量num的值,并不是a的值,这就会导致程序无法达到预期的效果。
引用传递
引用传递是指将实参的引用传递给函数的形参,通过使用引用,函数可以直接对实参进行修改,代码如下。
#include<iostream>
using namespace std;
void func(int& num)
{
num = 100;
}
int main()
{
int a = 3; //声明变量a=3
cout << "a=" << a << endl; //在控制台显示a的值
func(a); //调用函数func()
cout << "a=" << a << endl; //在控制台显示a的值
return 0;
}
运行以后,我们发现,不同于值传递,使用引用传递的方式,使得实参a发生了改变。我们用代码看一下该函数的执行过程。
#include<iostream>
using namespace std;
void func(int& num)
{
num = 100;
}
int main()
{
int a = 3; //声明变量a=3
cout << "a=" << a << endl; //在控制台显示a的值
//func(a); //调用函数func()
{
int& num = a; //创建引用,num是a的别名
num = 100; //修改num的值,a的值也改变
}
//运行结束 局部变量num被销毁
}
通过执行过程,我们发现又回到了引用的知识了,如果引用的知识点不了解,那么引用传递肯定也是一团糟。
指针传递
C++中的指针传递是一种传递指针作为函数参数的方式。通过将地址作为参数传递给函数,可以在函数内部对指针所指向的数据进行修改,代码如下。
#include<iostream>
using namespace std;
void func(int* num)
{
*num = 100;
}
int main()
{
int a = 3; //声明变量a=3
cout << "a=" << a << endl; //在控制台显示a的值
func(&a); //调用函数func()
cout << "a=" << a << endl; //在控制台显示a的值
return 0;
}
运行结果和引用传递的运行结果一样,实参a均被修改,要注意的是,指针传递传入的是实参的地址而不是实参,接下来我把执行过程放到main中帮助大家理解。
#include<iostream>
using namespace std;
void func(int* num)
{
*num = 100;
}
int main()
{
int a = 3; //声明变量a=3
cout << "a=" << a << endl; //在控制台显示a的值
//func(&a); //调用函数func()
{
int* num = &a; //声明指针变量num,指向实参a的地址
*num = 100; //修改num解引用的值
}
//函数运行结束,局部指针变量num被销毁
}
指针引用在执行的过程中,通过修改了内存中存放的值,也就是修改指针解引用的值来修改实参。指针传递和引用传递有着异曲同工之妙,印证了前面所说的"引用的实质就是指针常量"这句话。
返回值
C++中的函数可以返回值或返回引用。返回值是指在函数体中定义一个变量,然后将其作为函数返回值返回。在返回值的情况下,会发生变量拷贝,因为在返回之后,函数内部定义的局部变量将被销毁,而返回值已经复制到一个新的变量中,该变量位于调用函数的位置。需要注意的是,返回值不可以作为赋值运算符的左值。
#include<iostream>
using namespace std;
int func(int sum)
{
cout << "func()函数内的局部变量sum=" << sum << endl;
return sum;
}
int main()
{
int a = 3; int b; //声明变量a=3和变量b
//func(a) = 5; //将返回值作为赋值运算符的左值(此行报错)
b=func(a); //用b来接收函数func(a)的返回值,必须要与fun()函数的返回值类型一样
cout << "b=" << b << endl; //在控制台显示b的值
func(func(a)); //套娃操作,将func(a)当作一个数字来使用
return 0;
}
上述代码中,func函数的功能是先显示局部变量sum的值,也就是传入实参的值,然后将局部变量的值作为返回值,在main函数中,func(a)不能作为赋值运算符的左值,然后由b来接收func(a)的返回值并在控制台中显示b的值。需要注意的是,用来接收返回值的变量必须与返回值的类型保持一致,否则程序会出现一些意想不到的bug。
在这个函数中,我们就可以把func(a)的返回值当作一个数字来看就行。所以给大家表演一个套娃操作func(func(a))来帮助大家理解。
返回引用
返回引用是指将函数返回值的引用作为返回值返回。在返回引用的情况下,不会发生变量拷贝,因为返回的是指向函数内部定义变量的引用,该变量仍在函数执行后保留。与返回值不同的是,返回引用可以作为赋值运算符的左值并且返回引用不能返回局部变量
#include<iostream>
using namespace std;
int& func(int& sum)
{
return sum;
}
int main()
{
int a = 3; //定义变量a=3
int& ra = func(a); //定义引用ra,ra是func(a)返回值的别名
cout << "func(a)=" << func(a) << endl;
cout << "ra=" << ra << endl; //在控制台中显示func(a)和ra的值
func(a) = 5; //修改func(a)的返回值
cout << "func(a)=" << func(a) << endl;
cout << "ra=" << ra << endl; //在控制台中显示func(a)和ra的值
return 0;
}
细心的朋友可能已经发现了,返回引用的函数,在传参的时候使用了引用传递,如果不使用引用传递,程序会不会正常执行下去呢?我们用代码测试一下。
#include<iostream>
using namespace std;
int& func(int/*&*/ sum)
{
return sum;
}
int main()
{
int a = 3; //定义变量a=3
int& ra = func(a); //定义引用ra,ra是func(a)返回值的别名
cout << "func(a)=" << func(a) << endl;
cout << "ra=" << ra << endl; //在控制台中显示func(a)和ra的值
func(a) = 5; //修改func(a)的返回值
cout << "func(a)=" << func(a) << endl;
cout << "ra=" << ra << endl; //在控制台中显示func(a)和ra的值
return 0;
}
运行以后,程序出现了bug,那么为什么会出现bug呢?
在之前值传递的时候,我把值传递的执行过程放到main函数中帮助大家理解,在值传递执行过程里面,会先在func()函数里面声明一个局部变量,这个局部变量的值就等于实参的值,然后返回的是局部变量而不是实参,局部变量在函数调用完成后会被销毁,销毁之后,局部变量都不存在了,你还引用个毛线。
本篇文章是个人在学习过程中,产生的一些理解,如有错误,还请各位轻点喷...