由 *& 引发的对C++中值传递、指针传递、引用传递的探讨
由 &* 引发的对C++中值传递、指针传递、引用传递的探讨
问题出于今天在写Leetcode时,在Discuss中发现的 &* 写法,于是找了一些相关资料,并且整理如下。
首先,说一下C++ 中*a 和 *&a作为函数的参数时的区别
在之前写代码中,我们当遇到形如
void foo(int *ptr);
这样的函数声明时,函数的形参是一个指针,它实际上是值传递, 如果不清楚请继续往下看,比如,我们这样调用
int a = 3;
int *pValue = &a;
foo(pValue);
这种情况下,pValue的值是不会被foo函数改变的,也就是说pValue肯定是指向a的。例如我们在foo()函数中进行下面的操作
void foo(int *ptr)
{
ptr = nullptr;
}
在foo(pValue)调用后,pValue仍然为a的地址, *pValue仍然为3。
如果我们更改一下声明
void foo(int*& ptr)
{
ptr = nullptr;
}
在foo(pValue)调用后,pValue成为了一个空指针。
为什么呢?
要从值传递、指针传递、引用传递这几个方面开始看。
值传递
形参为指向实参的拷贝,改变形参的值不会影响外部实参的值。从被调用函数的角度来说,值传递是单向的(实参->形参), 参数的值只能传入,不能传出。当函数内部需要修改参数,并且不希望这个改变影响调用者时,采用值传递。
指针传递
指针,实际上是地址的一个拷贝,其实也是值传递。你不能改变原来变量的值,但你能改变这个地址指向的内容。
沿着上面的foo(int *ptr)
来说,你可以通过*ptr = 5
来改变pValue指向(即a)的值,但是不能通过函数内ptr=nullptr
让pValue
为空指针。
引用传递
形参相当于实参的“别名”,对形参的操作其实就是对实参的操作,在引用传递过程中,被调用函数的形参虽然也作为局部变量在栈中开辟内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量。
为了进一步加深对指针和引用的区别,下面从编译器的角度来阐释它们之间的区别。
程序在编译时分别将指针和引用添加到符号表上,符号表上记录的是变量名和变量所对应的地址。指针变量在符号表上对应的地址值为指针变量的地址值;引用在符号表上对应的地址值为引用对象的地址值。符号表生成后就不会再改,因此指针可以改变其指向的对象(指针变量中值可以改),而引用对象则不能更改。
最后,总结一下指针和引用的相同点和不同点
- 相同点
- 指针指向一块内存,它的内容是所指内存的地址;而引用则是某块内存的别名
- 不同点
- 指针是一个实体,而引用是一个别名
- 引用只能在定义是被初始化一次,之后不可变;指针可变。引用“从一而终”, 指针可以“见异思迁”
- 引用没有const, 指针有const,具体内容见这里
- 引用不能为空,指针可以为空
- sizeof(引用)得到的是所指向的对象的大小, 而sizeof(指针)得到的是指针本身的大小
- 指针和引用的自增运算含义不同
- 引用是类型安全的,而指针不是。引用比指针多了类型的检查。