传值、传址与传引用知识概述
1. 概念
值传递:形参是实参的拷贝,改变形参的值并不会改变实参的值,从被调函数角度来看,值传递是单向的(实参->形参)。 在被调函数结束之后,形参自动销毁,并不会传到主调函数中。
传址:形参是指向实参的指针,当对形参进行改动时,就相当于对实参进行改动。将实参地址传给形参,注意,这时形参指针和实参指针指向同一块地址。因此,改变形参就会修改实参。
传引用:形参是实参的“别名”,对形参的改动也就是对实参的改动。这时候,形参与实参使用相同的内存空间,二者的地址完全一样。
注意:这一切都取决于你是否想要改变主调函数中的实参。
2. 简单例子
代码如下:
#include
using namespace std;
void swapValue(int a, int b){
int temp = a;
a = b;
b = temp;
}
void swapRef(int &a, int &b){
int temp = a;
a = b;
b = temp;
}
void swapPtr(int *a, int *b){
int temp = *a;
*a = *b;
*b = temp;
}
int main(){
int a = 1;
int b = 4;
cout << "Original Status: " << endl;
cout << "a = " << a << "\t" << "b = " << b << "\t" << "&a = " << &a <
swapValue(a, b);
cout << "After passing by value :" << endl;
cout << "a = " << a << "\t" << "b = " << b << "\t" << "&a = " << &a <
swapRef(a, b);
cout << "After passing by ref: " << endl;
cout << "a = " << a << "\t" << "b = " << b << "\t" << "&a = " << &a <
swapPtr(&a, &b); //这里会恢复原值,因为上面引用已经交换过了。
cout << "After passing by Ptr: " <
cout << "a = " << a << "\t" << "b = " << b << "\t" << "&a = " << &a <
return 0;
}
输出结果如下:
Original Status:
After passing by value :
a = 1 b = 4 &a = 0x61fe1c &b = 0x61fe18
After passing by ref:
a = 4 b = 1 &a = 0x61fe1c &b = 0x61fe18
After passing by Ptr: //注意这里貌似是没有通过指针交换,实际上是因为上面的引用已经换过了,传指针又将其恢复了。
a = 1 b = 4 &a = 0x61fe1c &b = 0x61fe18
3. 三种方式比较
功能上:传值不可以在主调函数中改变实参;传指针与传引用则可以在主调函数中改变实参的值。而且要注意:指针是最灵活的传参方式。
传递效率上:指主调函数中实参传递到被调函数中的形参中的效率。对于32位的机器,小于4字节的参数,可以看出是值传递效率更高,要是大于4字节参数或者自定义的数据类型(class、struct),最好就是使用传址或者传引用。
执行效率上:指在被调函数体内执行的效率。传值的话就是直接寻址,指针与大部分情况下的引用则是通过间接寻址的方式执行的。所以传值执行效率稍高些。对于传过来的变量的频繁操作且总次数很多的情况下,传址和传引用有较多的效率损失。
综合2和3来看,传引用效率始终是不低于传指针的。所以C++优先传引用而非指针。😙
“Reference when possible, Pointers when needed!”
4. 一个可能忽视的点
考虑如下代码:
int add(int a, int b){
return a + b;
}
int main(){
int m = 0, n = 1;
int ret = add(m, n);
return 0;
}
实参m、n值传递给被调函数add,进行相应计算。但是我们不需要修改实参,我们想要的就只是一个返回值,所以在6行,程序开始运行add子程序,执行完之后,还是在第6行继续进行,将add函数返回值赋值给ret。
这个例子是不让自己混淆 上面讨论的修改实参问题 与 直接拷贝返回值。