- 序
最近在逛StackOverflow,有个人问了一个很好的问题“What are the differences between a pointer variable and a reference variable in C++“,因为在C++中,引用和指针都发挥着类似的功能,虽然我们都有着天然的直觉,在某个地方就只能用指针而不能用引用,就像以前做英语单选一样,总觉得这个空的答案是XXX,却说不出个所以然来。这就是我要写这篇博客的目的,尽可能地总结出这两者之间的区别,囿于水平,错误以及不足之处欢迎指正。
调试机器:VS2012
环境:Win8.1 32位系统
Stack Overflow
指针可以被多次绑定,而引用不能
引用跟一个对象绑定后,直到那个对象生命周期结束,都不能绑定到其他对象。
看这么一个例子:
int i= 2;
int j = 4;
int &ri = i;
ri = j;
j = 8;
此时i=ri=4,说明ri并没有绑定到j上,而刚才ri=j只是简单的赋值操作,虽然在vs下能顺利编译通过。
看一下下面的例子
int *const p = &i;
p = &j;//error
p在赋值j时出现错误,那是因为常数指针在代码中是不能进行赋值或是绑定操作的,所以在常数指针的声明时,必须要给他赋值,否则会报错。
int *const p;//error
这一点跟引用变量具有不能重复绑定的性质很相像,所以引用变量也像常数指针一样在声明时,不能声明一个空的引用。 但是也不完全的一样,例如声明对象是NULL时,两者就会出现差异。
int *const pi = NULL;//ok
int &ri = NULL;//error
引用不一定都是安全的
可能有人会觉得既然引用变量不能为空或是NULL,是不是引用就比指针就安全得多呢。大多数情况下是的,但也没想象中的那么安全,因为我们还是有很多种办法可以使它空引用。看下面的一个例子
int *pi;
int &ri=*pi;
声明了一个空指针pi,引用变量ri此时恰好引用pi指针,这种情况在vs2012下是可以编译通过的,但是很明显在调用时会发生错误。再看另一个例子:
int &f(){
int i ;
return i;
}
函数f的返回值类型为int &,但是局部变量i的栈空间在函数结束后也一并摧毁,此时它指向了一个未知空间或是未知数。
并没有绝对安全的技术,良好的编程习惯可以帮助我们克服很多这一类的问题。
引用变量&到底是什么
引用一开始学习C++时很经典的一个例子
void swap_reference(int &v1, int &v2){
int temp = v1;
v1 = v2;
v2 = temp;
}
void swap_pointer(int *v1, int *v2){
int temp = *v1;
*v1 = *v2;
*v2 = temp;
}
void swap(int v1, int v2){
nt temp = v1;
v1 = v2;
v2 = temp;
}
我们知道swap(v1,v2)并不能做到两个值的交换,这是因为在执行swap函数时,互换的是函数空间内的两个值,可认为是v1,v2的两个拷贝互换了。swap_pointer(v1, v2)由于是对地址的操作,可以达到互换的目的。我们也知道swap_reference(v1,v2)也可以执行这样的目的,不同于以上的两种方式,通过引用方式传递的是真真切切的对象,既不是对象的拷贝也不是对象的地址。一个很有趣的例子
char i = 'a';
char b='9';
char *pi = &i;
char &ri = b;
cout<<sizeof(ri)<<endl;
cout<<sizeof(*pi)<<endl;
cout<<sizeof(&ri)<<endl;
cout<<sizeof(pi)<<endl;
cout<<(void*)&ri<<"\n"<<(void*)&b;
在栈里创建了一个char型的指针和引用,结果用sizeof计算大小时输出1144,而且ri和b的地址一样,ri和b的值也一样,看起来引用是原对象的一个nick name。但问题来了,我在声明了一个引用变量ri,这个变量的地址确是b地址,系统没有给ri分配栈空间,这听起来真的很不可思议。
还有一个很有趣的事情,如果我们稍微改下输出,发现这两个输出值会不一样。
char b[2]={'9'};
cout<<(void*)(&b+1)<<"\n"<<(void*)(b+1);