引用類型的本質

来源:http://www.cnblogs.com/painterQ/archive/2017/06/16/7021759.html
-Advertisement-
Play Games

編譯器是怎麼實現引用類型的呢?本篇文章複習了const常量和指針,在此基礎上推測了引用類型的本質。旨在加深對語言的理解,希望對你有所幫助。 ...


編譯器是怎麼實現引用類型的呢?

預備知識1:使用const定義常量

  使用const可以採用類似定義變數的方法來定義常量,在定義的時候必須初始化以指明常量值。比如 const int a = 1; 。

  那麼編譯器會給const定義的常量分配記憶體空間嗎?如果分配了記憶體空間,那麼每次使用這個常量都要訪問這個地址,空間效率暫且不論,時間效率不也被大大浪費?在這裡可能一開始大家都會有這樣的疑問。特別是單片機編程出身的我,在那個一無所知的時候覺得浪費效率簡直渾身難受(單片機的計算能力非常有限)。

  先說是否給const常量分配空間,答案是會。可以對一個const常量取地址,因此無疑const常量在記憶體中是有一席之地的。

  再說時間效率,實際上編譯器都會採用常量摺疊技術來優化代碼。具體說就是像巨集替換一樣把常量替換成立即數。但與巨集替換不同的是,這個是在編譯階段完成的。這樣凡是用到const常量的時候,都不需要訪問記憶體去取出常量值,而是直接用立即數(這個數是直接寫在機器指令里的)。這樣的時間效率和巨集替換相當。儘可能多的使用const關鍵字吧,這樣可以大大減少bug數量。

//C++語句與對應的反彙編,可以看到給a分配了空間並初始化為1
//但是在之後用到a的地方使用了立即數1
//如果看不懂反彙編也沒關係,代碼中要表達的在上文中已經全陳述過了
const int a = 1; 00C516EE mov dword ptr [a],1 int b; b = fun(a); 00C516F5 push 1

預備知識2:指針

一般說的“指針”是指“指針變數”。但是指針有時候也被理解成地址的同義詞,所以指針變數不過是指“一個變數,裡面存儲的是一個指針/地址”。這裡想說的是常量指針和指針常量。

常量指針是說“一個指向常量的指針”(也不一樣指向一個常量,但*ptr被視為一個常量),本質上是一個指針變數。定義的方法是 const int * ptr ; 。

指針常量是說“一個保存了指針/地址的常量”,所以指針常量本質上是一個常量,定義的方法是 int * const ptr = &a;。就像一個const常量在定義時必須被初始化一樣,常量指針也必須在定義時初始化,也就是說明自己是哪一個地址。

上一小節說明瞭什麼叫常量摺疊,和const常量是如何被替換成立即數的。那麼指針常量當然也會被這樣替換成立即數,只不過這個立即數表示一個地址,比如等於1480091972什麼的。

預備知識3:變數的三個屬性

  變數的最基本的屬性基本上有三種:

        (1)名字(必須顯示說明)

        (2)類型 (必須顯示說明)

        (3)存儲類別 (預設方式或顯示說明(使用:auto、register、static、extern))

  作用域和生存期(作用域和生存期被認為是另兩種屬性,但這兩個屬性不像前三種基本)實際上由存儲類別和定義變數的位置決定,所以變數最基本的屬性就是上面三種。當看到一段C++源代碼的某一個變數的時候,合格的程式員應該能說明這個變數的三種屬性是什麼,比如一個全局變數是int類型,名字是a。

  可是這些屬性只對程式員和編譯器有意義,機器只懂機器碼不懂C++。也就是說編譯器在編譯源代碼的時候需要這些信息,這些信息也在源代碼中有體現,所以編譯器也可以得到這些信息。編譯器根據這些信息進行編譯,但是編譯之後這些信息就都丟失了,不保證再能從機器碼里得到這些信息。所以名字對編譯器是重要的信息,但編譯後一塊記憶體不再需要有名字,因為記憶體只需要編號就夠了。名字只對程式員和編譯器重要。

引用

經典的解釋什麼是引用的說法是:引用是變數的別名。確實是的,這句話完美到無懈可擊,如果說有什麼瑕疵那就是這句話不太好理解。什麼叫別名?那麼為什麼要給變數多起一個名字呢?特別是在形參類型是引用時,如何能把一個“名字”作為參數呢。前面說了名字只是變數的屬性之一,如果只是一個名字的話那連變數都不是啊,什麼叫引用呢?

我先接觸的是C語言,對於指針並沒有太多困惑,但是C++的引用著實讓我困惑了一陣。看看引用的使用場景:

1 int a = 1;
2 int &b = a;
3 b = 2;
4 cout << a << endl;    //輸出2

上面的代碼仿佛可以解釋什麼叫“引用是變數的別名”。b是a的別名,就是對b的操作和對a的操作一樣, b = 2; 就可以理解成 a = 2; 。仿佛沒什麼難以理解。但是引用的應用價值在於作為形參的引用類型和返回引用類型。

在C語言中參數傳遞是傳值的,想要實現對實參的修改需要藉助指針,一個典型的swap函數的實現如下:

1 void swap(int*a,int *b){
2   int temp = *a;
3   *a = *b;
4   *b = temp;  
5 }

交換兩個變數a和b,需要傳入a和b的地址 swap(&a,&b); 。想要交換兩個盒子里的蘋果,必須先找到這兩個盒子,所以變數的地址在這個演算法中是必不可少的。只懂C語言的我認為這自然而然:想要修改實參的值就必須傳入地址。可是到接觸了c++就發現實現同樣的功能可以藉助引用,而且要簡單得多。既然“變數的地址在這個演算法中是必不可少的”,那麼引用到底是怎麼實現傳入地址的?

實際上引用可以看成指針常量。對const常量有定義時必須初始化的要求,同樣對引用也有這樣的要求:定義引用時必須指明是誰的引用(作為形參的引用類型是在傳入實參的時候創建並初始化的)。把引用看成指針常量就可以解釋傳入引用為什麼和傳入指針一樣的作用了。

什麼是別名呢,就是地址或者叫指針常量


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1、問題:啟動Tomcat時報錯! 主要錯誤代碼如下 1 1 在這段代碼之前還有錯誤代碼: 1 1 2、出現錯誤的環境:spring MVC + MyBatis框架下,涉及有control層、service層、dao層。 3、問題分析:一開始以為是由於control層使用@Resource出現的異常 ...
  • 最近開發一個項目,需要調用第三方的介面,第三方提供的數據是xml,我直接使用Array2XML把php數組轉成XML格式。 XML格式如: 由於php數組無法指定多個重覆下標,後面的會覆蓋前面的值,最終只會展示一個值 上面php數組用Array2XML轉成XML,body裡面只會有一個item節點。 ...
  • 本文為公司製作API介面後臺的小結! 1.命名註意事項: 不要使用易混淆的名字,如index,index01... 我喜歡用拼音... 比如: 2.資料庫文件修改: 去database.php里把數據得首碼去掉; 3.獲取請求的值: 4.操作資料庫: (1)原生操作: (2)name查詢: 5.返回 ...
  • 前言: 這幾天剛剛開始學習python,然後就安裝了pycharm,但是那個中文亂碼的問題真是讓人心煩,在網上找了好久,都寫得好亂,今天終於讓我解決了,在這裡總結一下經驗,希望可以幫到你們 問題:如下圖,我的問題主要是在控制台輸入漢字的時候會出現以下亂碼 一般的解決方法 1. 首先如上圖所示,把fi ...
  • Java 數據類型 基本數據類型 數值:int、short、long 字元:char 布爾:boolean 引用數據類型 class(類) interface(介面) 數組[] 所占位元組數 ( ) int:4位元組 char: 規定2位元組。若使用UTF 8編碼,數字和英文等占1個位元組,中文3個位元組;若 ...
  • 目錄 自定義函數 內置函數 文件的操作 練習題 一. 自定義函數 1. 函數的創建 2. 函數的參數 (1)參數的定義 參數是使用通用變數來建立函數和變數之間關係的一個變數。我們都知道函數是用來被調用的,當我們需要給這個函數傳送值的時候,參數用來接收調用者傳遞過來的數據,並保存下來作為一個變數以便後 ...
  • re模塊 序言: re模塊用於對python的正則表達式的操作 標誌位即模式修正符,不改變正則表達式的情況下,通過模式修正符改變正則表達式的含義,從而實現一些匹配結果的調整等功能: 貪婪模式、懶惰模式: match: 從起始位置開始根據模型去字元串中匹配指定內容: 匹配ip地址: search: 根 ...
  • 1. 為重用以及更好的維護代碼,`Python`使用了模塊與包;一個`Python`文件就是一個模塊,包是組織模塊的特殊目錄(包含`__init__.py`文件)。 2. 模塊搜索路徑,`Python`解釋器在特定的目錄中搜索模塊,運行時`sys.path`即搜索路徑。 3. 使用`import`關... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...