一、简介
参数传递的三种方式:值传递、引用传递、指针传递
二、举例如下
#if 1 值传递 引用传递 指针传递的区别
void value_input(int a)
{
cout << "值传递------函数" <<&a <<endl;
a = 100;
}
void Pointer_input(int * n)
{
cout << "指针传递------函数" << n << endl;
*n = 101;
}
// 引用传递
void Othername_Input(int &n)
{
cout << "引用传递------函数" <<&n << endl;
n = 1001;
}
int main()
{
int a = 10;
cout << "实参的地址------" << &a << endl;
cout << "=====================------" << endl;
value_input(a);
cout << "值传递------" << a <<" 地址是: "<<&a << endl; // a 的值不会改变
cout << "=====================------" << endl;
Pointer_input(&a);
cout << "指针传递------" << a << " 地址是: " << &a << endl; // a 的值会改变
cout << "=====================------" << endl;
Othername_Input(a);
cout << "引用传递------" << a << " 地址是: " << &a << endl; // a 的值会改变
return 0;
}
如上测试:我们可以看出
1、值传递并不会改变实参的地址,而是在形参中有一个新的地址,因此在函数中改变这个值,main 函数中的实参是不会发生改变的。
2、引用和指针传递都是在函数中拿到了main 函数中的实参的地址,然后改变实参中的值
C++中引用传递与指针传递区别(进一步整理)-pudn.com
三、参数传递的本质
1、值传递的本质是将实参的值复制给形参,地址是不同的。
2、引用传递 被调用的形参虽然也作为局部变量在栈中开辟了空间,但是这个时候存放的是有住函数放进来的实参变量的地址,当被调用的时候直都处理成间接寻址(通过栈中存放的地址来访问主函数的实参变量)
3、与引用传递不同的是,虽然都是在被调用函数栈空间上开辟了空间,但是引用采用的是间接寻址法找到主函数中的实参,而指针传递,如果改变被调函数中的指针地址,将不影响主掉函数的相关变量,如果想通过指针参数来改变主调函数中的相关变量,那就要用二级指针或者指针引用。
四、指针传递和引用传递的区别与优劣
引用:
创建的时候必须初始化
不能用NULL做引用
一旦这个引用被初始化就不能改变引用的关系:程序在编译时分别将指针和引用添加到符号表上,符号表上记录的是变量名及变量所对应地址。指针变量在符号表上对应的地址值为指针变量的地址值,而引用在符号表上对应的地址值为引用对象的地址值。符号表生成后就不会再改,因此指针可以改变其指向的对象(指针变量中的值可以改),而引用对象则不能修改。
// 有两个对象 pt1和p Point pt1(10,10); Point p(11, 11); Point& pt2 = pt1; // 也就是说 pt1 这快空间的别名就是 pt2, pt1和pt2 指的是同一个对象 可以理解为messi 有一个小名--》梅老板 Point& pt3 = pt1; // 也就是说 pt1 这快空间的别名就是 pt2, pt1和pt2 指的是同一个对象 messi 还有另一个外号-》小跳蚤 cout << "实参------" << &pt1 << endl; cout << "引用2------" << &pt2 << endl; //&pt2 = p; // 这是不能允许的:通俗理解就是, 梅老板同时指向的是两个不同的对象,这个计算在调用的时候就不知道调用的是那个也就是我们常说的二义性 pt1.x = 12; // 梅西7夺金球奖 pt1.y = 15; cout << "x------" << pt2.x << endl; // 12 梅老板 也7夺金球奖 因为是messi对象 cout << "y------" << pt2.y << endl; //15 小跳蚤 也7夺金球奖 因为是messi对象
指针传递和引用传递一般适用于:
函数内部修改参数并且希望改动影响调用者。对比指针/引用传递可以将改变由形参“传给”实参(实际上就是直接在实参的内存上修改,
不像值传递将实参的值拷贝到另外的内存地址中才修改)。
另外一种用法是:当一个函数实际需要返回多个值,而只能显式返回一个值时,可以将另外需要返回的变量以指针/引用传递
给函数,这样在函数内部修改并且返回后,调用者可以拿到被修改过后的变量,也相当于一个隐式的返回值传递吧。
指针和引用的异同点:
相同点:
都是地址的概念:指针指向的是一块内存,其内存的内容是的地址
引用则是这块内存的小名
不同点:
1、指针是一个实体,而引用就是一个别名
2、引用在定义的时候就要初始化,切不可变(就像一个对象都不确定你起别名没有意义),而指针可以不初始化,而且可以随时改变。
3、sizeof 引用是对象的大小,指针的话则是这个指针类型的大小
4、引用是安全的(寻址法)不能改变,指针是可以改变的
5、引用不能为NULL,指针可以为NULL
6、引用没有const,指针有const,const的指针不可变;(具体指没有int& const a这种形式,而const int& a是有 的, 前者指引用本身即别名不可以改变,这是当然的,所以不需要这种形式,后者指引用所指的值不可以改变)
五、引用的使用:
1 作为参数
传统传参:
void swap(int * a ,int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
然后我们使用引用:
void swap3(int &a,int &b)
{
int temp = a;
a = b;
b = temp;
}
给函数传递大型的对象
当大型对象被传递给函数的时候,使用引用参数可以参数的传递效率提高,主要是由于引用并不会对这个对象进行拷贝
2 作为返回值
引用作为返回值,须遵守以下规则:
- 不能返回局部变量的引用。主要原因是局部变量会在函数返回后被销毁,因此被返回的引用就成为了”无所指”的引用,类似于野指针,程序会进入未知状态。
- 不能返回函数内部new分配的内存的引用。虽然不存在局部变量的被动销毁问题,可对于这种情况又面临其它尴尬局面。例如,被函数返回的引用只是作为一 个临时变量出现,而没有被赋予一个实际的变量,那么这个引用所指向的空间(由new分配)就无法释放,造成memory leak。
- 可以返回类成员的引用,但最好是 const,这样可以避免在无意的情况下破坏该类的成员。
using namespace std;
float temp; //返回值不能是一个局部的变量
float func1(float r)
{
temp = r * r * 3.14;
return temp;
}
float& func2(float r) { //&说明返回的是temp的引用,换句话说就是返回temp本身
temp = r * r * 3.14;
return temp;
}
int main()
{
float a = func1(5.0); //case 1:返回值
//float &b = func1(5.0); //case 2:用函数的返回值作为引用的初始化值 [Error] invalid initialization of non-const reference of type 'float&' from an rvalue of type 'float'
//(有些编译器可以成功编译该语句,但会给出一个warning)
float c = func2(5.0); //case 3:使用返回的指向temp的引用给变量c赋值
float& d = func2(5.0); //case 4:用函数返回的引用作为新引用的初始化值
cout << a << endl;//78.5
//cout<<b<<endl;//78.5
cout << c << endl;//78.5
cout << d << endl;//78.5
return 0;
}