栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C++ 深入探讨,指针与引用的区别和相同

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C++ 深入探讨,指针与引用的区别和相同

相同和区别
  • 相同
  • 区别
  • 小结

本博客不是新手向的,而是复习时使用。新手并不适合本文,且不一定能看懂本文

相同

开门见山地说,指针和引用其实是同一个东西,引用是指针的包装 (引用几乎和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)也能这么写来改变指向,内联汇编不必遵守编译器的限制

规矩是编译器定的,内联汇编不必遵守编译器的规矩
但除了玩之外,别写出上面那样的代码。因为这样的代码打破了规则,让人很难读懂

这一切都建立在假设编译器是这么翻译的基础上(我关闭了所有优化),如果有编译器有其他翻译,那么这么说也并不准确。编译器总会有自己奇怪的优化方式,把这些东西优化得面目全非


区别

我们还是要准守编译器的规矩,因为规矩给我们的代码带来了便利

好了,我们再说回他俩的区别:

  1. 有空指针,但是没有空引用。引用可以直接当做别名使用,但指针需要解引用

  2. 当引用使用时,可以当做是指针的解引用。 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 其实是同一个东西
//两个函数的调用,和内部的代码转换成汇编后几乎一样。
  1. 一般情况下引用声明时必须初始化,类内的引用可以先声明,再用初始化列表初始化;指针可以只声明,不初始化,但是const指针也必须在声明时初始化。

  2. 引用其实是个const指针,一旦定义后就不能修改指向的目标了;指针可以,但const指针也必须初始化。

  3. 指针有const,引用没有。因为引用本身就是const,所以加了也没用

我认为 2 — 4 严格来说算不上区别,所以我划掉了

大家都喜欢说指针分配了内存,但是引用没有分配。
这句话不一定正确,因为是否分配内存还得是编译器翻译的时候说得算。如果某款编译器觉得没必要翻译成指针的封装,而是直接当做别名用,实现起来也不算太复杂,这算是一种优化,因为间接访问没有直接访问快。传引用参另说,也需要看编译器如何翻译。

小练习
引用访问对象是直接访问,指针访问对象是间接访问?

不一定。这也取决于编译器的翻译


小结

相同:引用几乎和const指针(xxx * const)一样。引用声明和初始化时是:xxx * const r ;使用时将 (*r)作为整体
区别:引用不能为空,指针可以为空
其他却别:指针和const指针的区别;指针 p 和解引用指针 *p 的区别

本文基于编译器如此翻译的基础上,如若遇到其他翻译方式,还请自行考量

为什么要这么包装一层,因为指针虽然灵活,但是很容易出错。引用不会乱指,也更不容易出错。

嘟囔两句: 本文并不是想告诉你,指针和引用的本质区别,而是想说所有学到的东西都要有自己的思考。 因为在不同编译器的翻译器,本文所写的东西也不一定正确,具体情况要具体分析,并不总能公式般地套用。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/948435.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号