• <tfoot id="ukgsw"><input id="ukgsw"></input></tfoot>
    
    • 久久精品精选,精品九九视频,www久久只有这里有精品,亚洲熟女乱色综合一区
      分享

      阿里云二面:引用和指針有什么區別?

       伊伊爸 2022-05-24 發布于湖北

      大家好。

      上周有個同學說面阿里云存儲被問到了引用和指針的區別,并且給我發了一些百度上搜到的標準回答。

      所以在這分享一下剛學 C++ 時寫的一篇博客,是關于指針和引用的區別的,基本沒有做修改,講解可能會比較粗略,需要有一點點匯編的知識。

      引用和指針有什么區別呢?

      相信大多數學過 C++ 的都能回答上幾點:

      • 指針是所指內存的地址
      • 引用是別名
      • 引用必須初始化,并且初始化后不能重新引用其它變量

      但是引用是別名這是 C++ 語法規定的語義。

      那么到底引用在匯編層面和指針有什么區別呢?

      沒區別。

      是的,這是我當時自己反匯編觀察后得出的結論,引用會被 C++ 編譯器當做 const 指針來進行操作。

      我們來看個例子:

      匯編揭開引用面紗

      先分別用指針和引用來寫個非常熟悉的函數swap

      // 指針版
      void swap(int *a, int *b) {
          int temp = *a;
          *a = *b;
          *b = temp;
      }
      // 引用版
      void swap(int &a, int &b) {
          int temp = a;
          a = b;
          b = temp;
      }

      直接 gcc -S 輸出匯編:

      引用版匯編

      __Z4swapRiS_:                           ## @_Z4swapRiS_
      .cfi_startproc
      ## %bb.0:
      pushq %rbp
      .cfi_def_cfa_offset 16
      .cfi_offset %rbp, -16
      movq %rsp, %rbp
      .cfi_def_cfa_register %rbp
      movq %rdi, -8(%rbp) # 傳入的第一個參數存放到%rbp-8 (應該是采用的寄存器傳參,而不是常見的壓棧)
      movq %rsi, -16(%rbp) # 第二個參數 存放到 %rbp-16
      movq -8(%rbp), %rsi # 第一個參數賦給 rsi
      movl (%rsi), %eax # 以第一個參數為地址取出值賦給eax,取出*a暫存寄存器
      movl %eax, -20(%rbp) # temp = a
      movq -16(%rbp), %rsi # 將第二個參數重復上面的
      movl (%rsi), %eax
      movq -8(%rbp), %rsi
      movl %eax, (%rsi) # a = b
      movl -20(%rbp), %eax # eax = temp
      movq -16(%rbp), %rsi
      movl %eax, (%rsi) # b = temp
      popq %rbp
      retq
      .cfi_endproc
      ## -- End function

      在來一個函數調用引用版本 swap

      void call() {
          int a = 10;
          int b = 3;
          int &ra = a;
          int &rb = b;
          swap(ra, rb);
      }

      對應匯編:

      __Z4callv:                              ## @_Z4callv
      .cfi_startproc
      ## %bb.0:
      pushq %rbp
      .cfi_def_cfa_offset 16
      .cfi_offset %rbp, -16
      movq %rsp, %rbp
      .cfi_def_cfa_register %rbp
      subq $32, %rsp
      leaq -8(%rbp), %rax # rax中是b的地址
      leaq -4(%rbp), %rcx # rcx中是a的地址
      movl $10, -4(%rbp)
      movl $3, -8(%rbp) # 分別初始化a、b
      movq %rcx, -16(%rbp) # 賦給ra引用
      movq %rax, -24(%rbp) # 賦給rc引用
      movq -16(%rbp), %rdi # 寄存器傳參, -16(%rbp)就是rcx中的值也就是a的地址
      movq -24(%rbp), %rsi # 略
      callq __Z4swapRiS_
      addq $32, %rsp
      popq %rbp
      retq

      從上面我們可以看到給引用賦初值,也就是把所引用對象的地址賦給引用所在內存,和指針是一樣的。

      再來看看指針的匯編吧

      指針版匯編

      __Z4swapPiS_: ## @_Z4swapPiS_
      .cfi_startproc
      ## %bb.0:
      pushq %rbp
      .cfi_def_cfa_offset 16
      .cfi_offset %rbp, -16
      movq %rsp, %rbp
      .cfi_def_cfa_register %rbp
      movq %rdi, -8(%rbp)
      movq %rsi, -16(%rbp)
      movq -8(%rbp), %rsi
      movl (%rsi), %eax
      movl %eax, -20(%rbp)
      movq -16(%rbp), %rsi
      movl (%rsi), %eax
      movq -8(%rbp), %rsi
      movl %eax, (%rsi)
      movl -20(%rbp), %eax
      movq -16(%rbp), %rsi
      movl %eax, (%rsi)
      popq %rbp
      retq
      .cfi_endproc
      ## -- End function

      匯編我就不注釋了,真的是完全一樣!并不是我直接復制的引用匯編而是真的在編譯器實現上都是相同的方式。

      指針版調用

      void pointer_call() {
          int a = 10;
          int b = 3;
          int *pa = &a;
          int *pb = &b;
          swap(pa, pb);
      }

      這次我特意改了下函數名,對應匯編:

      __Z12pointer_callv: ## @_Z12pointer_callv
      .cfi_startproc
      ## %bb.0:
      pushq %rbp
      .cfi_def_cfa_offset 16
      .cfi_offset %rbp, -16
      movq %rsp, %rbp
      .cfi_def_cfa_register %rbp
      subq $32, %rsp
      leaq -8(%rbp), %rax
      leaq -4(%rbp), %rcx
      movl $10, -4(%rbp)
      movl $3, -8(%rbp)
      movq %rcx, -16(%rbp)
      movq %rax, -24(%rbp)
      movq -16(%rbp), %rdi
      movq -24(%rbp), %rsi
      callq __Z4swapPiS_
      addq $32, %rsp
      popq %rbp
      retq

      還是幾乎完全一樣.......也沒再注釋

      簡單總結

      1. 引用只是c++語法糖,可以看作編譯器自動完成取地址、解引用的常量指針
      2. 引用區別于指針的特性都是編譯器約束完成的,一旦編譯成匯編就喝指針一樣
      3. 由于引用只是指針包裝了下,所以也存在風險,比如如下代碼:
        int *a = new int;
        int &b = *a;
        delete a;
        b = 12;    // 對已經釋放的內存解引用
      4. 引用由編譯器保證初始化,使用起來較為方便(如不用檢查空指針等)
      5. 盡量用引用代替指針
      6. 引用沒有頂層 const (引用本身不可變) 即int & const,因為引用本身就不可變,所以在加頂層 const 也沒有意義;但是可以有底層 const ()即 const int&,這表示引用所引用的對象本身是常量
      7. 指針既有頂層const(int * const--指針本身不可變),也有底層const(const int *--指針所指向的對象不可變)
      8. 有指針引用--是引用,綁定到指針, 但是沒有引用指針--這很顯然,因為很多時候指針存在的意義就是間接改變對象的值。但是引用本身的值我們上面說過了是所引用對象的地址,但是引用不能更改所引用的對象,也就當然不能有引用指針了。
      9. 指針和引用的自增(++)和自減含義不同,指針是指針運算, 而引用是代表所指向的對象對象執行++或--


        本站是提供個人知識管理的網絡存儲空間,所有內容均由用戶發布,不代表本站觀點。請注意甄別內容中的聯系方式、誘導購買等信息,謹防詐騙。如發現有害或侵權內容,請點擊一鍵舉報。
        轉藏 分享 獻花(0

        0條評論

        發表

        請遵守用戶 評論公約

        類似文章 更多

        主站蜘蛛池模板: 国产精品人妻久久无码不卡| 亚洲理论电影在线观看| 最新AV中文字幕无码专区| 久久精品亚洲乱码伦伦中文| 日本高清中文字幕免费一区二区 | 欧美日韩一区二区综合| 97人妻人人揉人人躁人人| 亚洲成人av综合一区| 国产AV无码专区亚洲AWWW| 国产精品午夜福利资源| 成人午夜福利视频镇东影视| 国产999久久高清免费观看| 无码A级毛片免费视频内谢| 国偷自产一区二区三区在线视频| 免费无码专区毛片高潮喷水| 亚洲av成人一区在线| 欧美XXXX色视频在线观看| 亚洲精品色无码AV试看| 99久久精品国产一区二区蜜芽| 又粗又紧又湿又爽的视频| 日产国产一区二区不卡| 国产AV人人夜夜澡人人爽| 玩弄漂亮少妇高潮白浆| 天天爽夜夜爱| 国产精品无码久久综合网| 99在线精品国自产拍中文字幕| 少妇xxxxx性开放| 久久精品国产亚洲不AV麻豆| 色爱综合激情五月激情| 亚洲AV无码专区国产乱码电影| 亚洲制服无码一区二区三区| 日韩在线观看精品亚洲| 国产丝袜视频一区二区三区| 精品免费看国产一区二区| jizz视频在线观看| 亚洲综合无码精品一区二区三区| 日本大片免A费观看视频三区| 国产亚洲精品第一综合另类无码无遮挡又大又爽又黄的视频 | 无码一区二区三区中文字幕| 午夜DY888国产精品影院| 亚洲成av人片在www鸭子|