- 相同
- 区别
- 小结
本博客不是新手向的,而是复习时使用。新手并不适合本文,且不一定能看懂本文
相同开门见山地说,指针和引用其实是同一个东西,引用是指针的包装 (引用几乎和const xxx * 差不多)
什么,不信?
看代码
int test(){
int i = 0;
int* pi = &i;
int& ri = i;
int ri_addr;
*pi = 1;
ri = 2; //此时的ri和(*pi)其实是同一个东西
_asm {
push eax //保护现场
lea eax, [ri] //取出ri本来的地址,而不是使用 像是被重载过 的&
mov [ri_addr], eax // ri_addr = eax
pop eax //还原现场
}
cout << hex << &pi << endl; //打印&pi的地址
cout << hex << ri_addr << endl; //打印真正的&ri的地址
cout << pi << endl; //打印pi存的地址
cout << *(int*)ri_addr << endl; //将ri看作指针,打印ri存的地址,
return 0;
}
但是这里写mov eax, [ri],为什么不能写&ri呢?可以理解为编译器为引用写了一个类,重载了 <<,&,= 等运算符,所以写 &ri 得到的返回值才是 &i。但是编译器的规则管不到内联汇编,所以我们才能取到正确的引用(指针)真正的地址
我在汇编里暗箱操作,看不懂?
那这张图一定是铁证如山。汇编码都是一样的,是否无话可说?
即使是看不懂汇编,那么对于pi和ri的操作,是否是一模一样?一样,则证明他俩是同一个东西
如果你还不死心,那就跑跑下面这段代码,定能让你大开眼界。
//改变 ri = i 的指向,使得 ri = j,并使用 ri 改变 j 的值
int test(){
int i = 0;
int j = 123;
int& ri = i;
_asm { //这一段的大意是:int& ri = j
pushad//保护现场
lea eax, [ri]
lea ebx, [j] //ebx = &j
mov [eax], ebx
popad //还原现场
}
cout << j << endl; //123
ri = 321;
cout << j << endl; //321
return 0;
}//const指针(xxx *const)也能这么写来改变指向,内联汇编不必遵守编译器的限制
规矩是编译器定的,内联汇编不必遵守编译器的规矩
但除了玩之外,别写出上面那样的代码。因为这样的代码打破了规则,让人很难读懂
这一切都建立在假设编译器是这么翻译的基础上(我关闭了所有优化),如果有编译器有其他翻译,那么这么说也并不准确。编译器总会有自己奇怪的优化方式,把这些东西优化得面目全非
区别
我们还是要准守编译器的规矩,因为规矩给我们的代码带来了便利
好了,我们再说回他俩的区别:
-
有空指针,但是没有空引用。引用可以直接当做别名使用,但指针需要解引用
-
当引用使用时,可以当做是指针的解引用。 r 相当于 (*p)
(假设 int i = 1; const int* p = &i; int& r = i)
关于sizeof,sizeof(*p)和sizeof(p)这能一样吗?
关于++,(*p)++ 和 p++ 这能一样吗?
传参方面,引用r传入可以当做 *p 使用,指针传入得手动解引用,此时p相当于&r (&(*p))
void t1(const int* p) { *p = 1; }
void t2(int& r) { r = 2; }
//这里的 t2里的 r 和 t1里的 *p 其实是同一个东西
//两个函数的调用,和内部的代码转换成汇编后几乎一样。
-
一般情况下引用声明时必须初始化,类内的引用可以先声明,再用初始化列表初始化;指针可以只声明,不初始化,但是const指针也必须在声明时初始化。 -
引用其实是个const指针,一旦定义后就不能修改指向的目标了;指针可以,但const指针也必须初始化。 -
指针有const,引用没有。因为引用本身就是const,所以加了也没用
我认为 2 — 4 严格来说算不上区别,所以我划掉了
大家都喜欢说指针分配了内存,但是引用没有分配。
这句话不一定正确,因为是否分配内存还得是编译器翻译的时候说得算。如果某款编译器觉得没必要翻译成指针的封装,而是直接当做别名用,实现起来也不算太复杂,这算是一种优化,因为间接访问没有直接访问快。传引用参另说,也需要看编译器如何翻译。
小练习
引用访问对象是直接访问,指针访问对象是间接访问?
不一定。这也取决于编译器的翻译
小结
相同:引用几乎和const指针(xxx * const)一样。引用声明和初始化时是:xxx * const r ;使用时将 (*r)作为整体
区别:引用不能为空,指针可以为空
其他却别:指针和const指针的区别;指针 p 和解引用指针 *p 的区别
本文基于编译器如此翻译的基础上,如若遇到其他翻译方式,还请自行考量
为什么要这么包装一层,因为指针虽然灵活,但是很容易出错。引用不会乱指,也更不容易出错。
嘟囔两句: 本文并不是想告诉你,指针和引用的本质区别,而是想说所有学到的东西都要有自己的思考。 因为在不同编译器的翻译器,本文所写的东西也不一定正确,具体情况要具体分析,并不总能公式般地套用。



