C++值传递、引用传递、指针传递区别

在C/C++中经常会遇到传值问题,总结一下传参区别。

简介

值传递: 值传递仅传递了实参的拷贝,在传参时会在栈区开辟内存空间并将实参值拷贝其中。也就是说值传递的形参任何时候都只是实参的一个副本,仅改变了该函数栈内的局部变量值。

引用传递: 引用传递实际是为实参设置了别名,其在栈区开辟内存空间后并不进行拷贝,而是存放实参的地址,从而对形参的操作实际上是通过对实参的间接寻址来实现,操作的是实参。

指针传递: 指针传递其实是特殊的值传递,区别是其传递的值是地址而值传递是传递了实参的值。指针传递时会在栈区开辟内存空间并存放指针,而指针指向实参的地址。通过这一方法,操作指针也就达到了操作实参的效果。

试验

我们来尝试如下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 值传递
void change1(int num) {
cout << "(值传递) 形参地址: " << &num << endl;
cout << "(值传递) 改变前 , num = " << num << endl;
num++;
}
// 引用传递
void change2(int &num) {
cout << "(引用传递) 形参地址: " << &num << endl;
cout << "(引用传递) 改变前, num = " << num << endl;
num++;
}
// 指针传递
void change3(int *num) {
cout << "(指针传递) 形参地址: " << num << endl;
cout << "(指针传递) 改变前, num = " << *num << endl;
// 注意区分指针地址自增和指针值自增的区别
(*num)++;
}

int main(int argc, const char * argv[]) {

int num = 1;
cout << "实参的地址: " << &num << endl;

change1(num);
cout << "(值传递) 改变后, num = " << num << endl << endl;

change2(num);
cout << "(引用传递) 改变后, num = " << num << endl << endl;

change3(&num);
cout << "(指针传递) 改变后, num = " << num << endl << endl;

return 0;
}

运行后结果为:

1
2
3
4
5
6
7
8
9
10
11
12
实参的地址: 0x7ffeefbff5ac
(值传递) 形参地址: 0x7ffeefbff54c
(值传递) 改变前 , num = 1
(值传递) 改变后, num = 1

(引用传递) 形参地址: 0x7ffeefbff5ac
(引用传递) 改变前, num = 1
(引用传递) 改变后, num = 2

(指针传递) 形参地址: 0x7ffeefbff5ac
(指针传递) 改变前, num = 2
(指针传递) 改变后, num = 3

通过对比,我们不难发现,引用传递和指针传递时形参存放的地址是相同的,而值传递则是对实参进行了拷贝。那么引用和指针有什么区别呢?我们来进一步讨论。

引用传递和指针传递的区别

1. 是否可空

我们先看函数原型以及传入空地址的情况:

1
2
3
4
5
void change2(int &num);		// 引用传递
void change3(int *num) // 指针传递

change2(NULL); // 报错,不能引用空地址
change3(NULL); // 通过,可以传入空地址

可以看出,引用必须对应一个有效的内存地址,因为其本质上是实参的别名,而指针可以指向一个空地址,因为其本质上是可以指向某地址的指针。

2. 是否为实体

我们知道,指针实际上也是一种类型,其指向一个内存地址,其与目标变量之间并无关系。而引用实际上是作为变量别名来存在,并非一个独立的实体。

在使用时,引用不能改变其引用地址,只能改变目标变量的值,而指针可以改变地址和被指向变量的值,这一点显示了后者是一个独立的实体。

3. sizeof

我们知道sizeof用来计算变量大小,当我们对引用取sizeof时,得到的是其引用变量的大小,而对指针使用sizeof,得到的只是指针变量的大小,与被指向变量无关。

4. 编译时的区别

在编译时,引用和指针在符号表的处理上同样有区别,体现在符号表变量名和变量地址上。

引用的地址是目标变量的地址,也就是说引用并非以一个独立实体来存在。
而指针的地址是这个指针类型变量的地址,目标变量的地址只是存在于指针变量内部,并没有在该指针符号表中存在。

由于符号表不可变,所以不难看出,引用并不能改变其目标变量,而指针可以任意改变其指向的地址。