zoukankan      html  css  js  c++  java
  • 由底层和逻辑说开去--c++之引用的深入剖析

         在学c++的时候 我遇到的第一个问题就是这个引用,引用是什么东西,我的c++启蒙教科书是c++ primer plus,这本书上说的是:引用是已定义变量的别名,可以使用这个引用来表示这个变量;每当看到这句话的时候 我就有一种淡淡的的忧伤感,其实还是不懂, 这句话说的意思是说引用是个名字吗,那么引用占多大内存呢,我把这章从头看到尾 可惜对此只字不提, 由此可见写书者的诚意(至少我看不到),   本文就准备解析一下这个引用到底是个什么东东。

         先说一下本文的结构,本文主要由三个问题1,引用占内存吗,多大  2.引用跟指针啥关系  3.为什么要发明引用呢?针对这三个问题本文希望从c逻辑和汇编底层层面进行剖析,如有疏漏,还请大家批评指教。


                   首先第一个问题是引用占内存吗:为了回答这个问题,我们可以用假设验证法;假设1,引用是个名字而已,不占内存,这个我觉得是有些书和老师要传达给我的想法(起码让我这样想了),2.引用占内存,内存大小和它所引用的变量的大小,一样大(这个听起来貌似也有点道理);假设3,引用跟指针一样大,在x86架构上是32位的,就像指针一样(这个是根据引用的作用跟指针相似推出的);   那么我们就写个程序来测一测哪个对喽:

    #include <iostream>
    using namespace std;
    class Data
    {
    public:
    	char a;
    	char & b;
    	Data():b(a)
    	{}
    	
    };
    int main()
    {
    	Data d;
    	cout<<sizeof (d)<<endl;
    	return 0;
    }

    看上面的代码,这个对象呢,包含两种类型的变量,一个是char 型的变量另一个是它的引用,那就蛋疼了,这个对象到底是多大的呢;

    如果假设1,成立那应该是1,如果假设2成立,那应该是2,如果假设三成立,应该是大于4的倍数也就是8(这里其实是内存对齐机制大家可以测试一个char 和一个int 组成的结构体是8,这个问题更有点意思以后再说) 嗯,那么经过我的测试,这个结果是8个字节,说明啊 这个char & b这个东西啊 占了四个字节 ,这里我们就庆幸没有int型做测试吧;嗯现在弄清了内存方面的事情,就开始思考这个引用变量是个什么东西了,

             接着,我们就来看看这个引用它究竟是什么东西,这里我们会对一段cpp代码的结果反编译成汇编来分析分析;

    先来一个测试代码

    int main()
    {
    	int a=10;
    	int & b=a;
    	b=20;
    	return 0;	
    }


    这么小,能干啥事啊,别担心,虽然这个玩意儿连头文件都没带,但是却能干大事啊 把这段代码编译链接成exe,然后我们学一会儿黑客(黑的是自己),分析一下这个程序的汇编成分;

    _main proc near
    
    var_8= dword ptr -8
    var_4= dword ptr -4
    argc= dword ptr  8
    argv= dword ptr  0Ch
    envp= dword ptr  10h
    
    push    ebp
    mov     ebp, esp
    sub     esp, 8
    mov     [ebp+var_4], 0Ah
    lea     eax, [ebp+var_4]
    mov     [ebp+var_8], eax
    mov     ecx, [ebp+var_8]
    mov     dword ptr [ecx], 14h
    xor     eax, eax
    mov     esp, ebp
    pop     ebp
    retn
    _main endp
    


    这是啥玩意啊,看不懂啊,看不懂没关系,我们可以把上面那个cpp代码换成指针版本的;如下

    int main()
    {
    	int a=10;
    	int * b=&a;
    	*b=20;
    	return 0;	
    }

    功能一样是吧,那我们也来反编译一下;


    _main proc near
    
    var_8= dword ptr -8
    var_4= dword ptr -4
    argc= dword ptr  8
    argv= dword ptr  0Ch
    envp= dword ptr  10h
    
    push    ebp
    mov     ebp, esp
    sub     esp, 8
    mov     [ebp+var_4], 0Ah
    lea     eax, [ebp+var_4]
    mov     [ebp+var_8], eax
    mov     ecx, [ebp+var_8]
    mov     dword ptr [ecx], 14h
    xor     eax, eax
    mov     esp, ebp
    pop     ebp
    retn
    _main endp
    


    有没有发现,两个汇编代码居然是一模一样啊,应该没有一点不一样的;虽然不明白它们是什么意思,但是好厉害的样子,起码能够证明引用和指针在底层实现上是等价的,这也就解释了为什么问题1里面引用的内存是4个字节了(X86 架构哦);对于这两段汇编代码(其实是一样的)  如果感兴趣的话,我们可以聊一聊    慢慢的有种渐入佳境的感觉了,下面我们就来从逻辑上分析一下,为什么要有这么一种引用类型呢;


             其实这个问题,不是我能回答的,因为c++又不是我发明的,我哪里知道,  但是呢我们可以从编程语言的历史上推测(这是一种科学的猜测方法) , c语言有一个强大的东西叫指针,它的功能强大,又有多种变种,。拿到一个指针,便能操作那块内存了,  但是呢 指针太强大了,容易出错啊,怎么办呢,c++就说咱们来封装一个这个指针吧,虽然不能灭了它,但咱惹不起总躲得起吧; 于是 就发明出引用这种东西,从使用角度上讲增加了许多易用性呢;比如说对int a;操作 指针要 int *pa=a; * pa=123;而引用就不用* 了,而且引用必须在定义的时候赋初值,否则报错,而指针就没错 甚至有些连警告都没有,想想看这多危险啊,  还有引用一经指定对象便不能更改 相当于一个const指针 ,所以我们可以认为引用是对const指针的一种封装 ,好了引用有了 那就灭掉指针吧,不行,因为c++有一种妥协性,怎么说呢,引用受限是为了防止指针使用出错,但是由于指针太强大了,在一些需要指针出现的场合(比如遍历)引用又不能胜任,所以就又保留了指针;  在对指针的封装方面,java做到了,它直接把指针封装成名字了,让人感觉不到指针的存在,完全抛弃了指针 ,  那就不能姓c了 于是就叫java吧;

          经过上面放分析,我们知道引用就是对const指针的封装,为什么要封装,当然是第一安全,第二快速;就像栈和队列是对链表的封装一样,链表那么强大的东西,封装出来两个小东西,c也一样, 其他语言就是在逻辑层面封装了c , 懂了c再搞这些东西,岂不是 小儿科 ,要是不懂c 不懂内存,那这些我感觉就得跟着感觉走了,理解起来就不是推测而是猜测了; 有人觉得我不懂这些东西也行啊 会用就行了,那这是建立在记忆而不是理解的基础上,等到一段时间不用,再回头来看,我觉得如果之前理解过还是要回忆的快; 所以每当听到有人说c简单 我就觉得莫名其妙 这些东西不都是c玩剩下 的么;



  • 相关阅读:
    windows的80端口被占用时的处理方法
    Ansible自动化运维工具安装与使用实例
    Tomcat的测试网页换成自己项目首页
    LeetCode 219. Contains Duplicate II
    LeetCode Contest 177
    LeetCode 217. Contains Duplicate
    LeetCode 216. Combination Sum III(DFS)
    LeetCode 215. Kth Largest Element in an Array(排序)
    Contest 176 LeetCode 1354. Construct Target Array With Multiple Sums(优先队列,递推)
    Contest 176
  • 原文地址:https://www.cnblogs.com/dragonfive/p/3265848.html
Copyright © 2011-2022 走看看