1. 程式人生 > >C筆記-左值與右值

C筆記-左值與右值

[toc] # 前言:工欲善其事,必先利其器 ## 兩種資料 學習程式語言, 有兩類資料可以讓人"高潮". ​ 一類是針對初學者而設計的入門類書籍, 這種書總是適時地結合生動的生活例項, 來讓啥都不懂的萌新理解一些基本的和關鍵的東西, 達到撥雲見日的效果. 為將來的進一步學習培養出良好的興趣和打下堅實的基礎. 最具代表性的就是 headfirst 系列叢書. ​ 而另一類資料, 便是標準文獻了. 它就像博學的導師或者修仙小說裡的隨身老爺爺, 能夠完美地解答你的任何疑惑(就算有解答不了的問題, 那也是暫時的, 因為標準文獻本身也是不斷改進和迭代的). ​ 這邊作者假設讀者都有一定的C基礎,不是啥都不懂的萌新, 但是對於左值和右值的概念仍存有疑惑的朋友, 另外作者水平有限, 如有錯誤和瑕疵, 歡迎各位朋友指正. --- ## 參考資料及其使用說明 **參考資料** ​ 本文的參考資料是C11標準文獻草案(N1570), 是免費且幾乎等同於C11標準文獻的版本. * 外網版C11標準文獻資料(需FQ) [html版](https://port70.net/~nsz/c/c11/n1570.html) [pdf版](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf) * 筆者提供的國內版(筆者自建站) [html版](http://peterzhang.cool:3000/pdfs/c11.html) * 筆者所提供的本地下載(7z壓縮包, 內含pdf與html版) [本地下載](http://peterzhang.cool:3000/pdfs/c11.7z) **本文的連結及資料使用說明** * 本文連結說明 本文的連結部分,均是國內html版的連結 * 本地下載的資料說明 * c11標準文獻不僅每一個章節都有編號, 且每一個自然段都有編號,方便定位 * c11標準的html版: **可以用錨點直接定位到對應章節, 自然段 以及 註解** * 錨點: 形如 `#6.3.1.2p3` 的東西, 出現在網址欄的最後, 用於定位到網頁中的位置(滾輪會自動滾到對應內容處) * c11標準html版的錨點構成說明: 示例1: `#6.3.1.2p3` * 6.3.1.2是具體的章節編號: 第6章第3部分1小節第2節 * p3是對應的自然段編號: p3代表第3自然段 示例2: `#note99` * note99是對應的註解編號: note99代表第99個註解 * 應用說明: * 檢視國內版c11標準的第3章第2部分7小節第4自然段,可以直接輸入以下網址: peterzhang.cool:3000/pdfs/c11.html#3.2.7p4,然後回車 * 檢視本地下載的c11的html版本也可以開啟c11.html之後,在網址後面加上#3.2.7p4,然後按回車即可 --- # [官方對於左值和右值的定義](http://peterzhang.cool:3000/pdfs/c11.html#note64) ​ 可見, 左值右值的概念來自賦值表示式, `=`號左邊的為左值(可修改的左值), 它代表(定位)了一個可用於存放資料的儲存空間; 而右值通常被理解為 "表示式的值"(value of an expression). ## 實際使用時的疑問 ​ 那麼到底哪些是左值, 哪些又屬於右值? 什麼情況下屬於左值, 什麼情況下屬於右值呢? --- # 左值的涵蓋範圍 * 變數名 * 指標變數 * 一些運算子的運算結果: * \* -- 取內容運算子 * [] -- 陣列下標運算子 * (type-name){initialize-list} -- 複合字面量 * **.** (只有左運算元為左值時,結果才為左值) * ->(無論左運算元為左值還是右值,結果均為左值) 舉例說明: * a是陣列名,絕大部分情況下屬於指標值(見後續部分),是右值 * a[1]屬於運算子[]的結果, 屬於左值, 可以放在等號左邊進行賦值操作. --- # 重要概念: [左值轉化(lvalue conversion)](http://peterzhang.cool:3000/pdfs/c11.html#6.3.2.1p2) > \#6.3.2.1p2: 滿足以下條件的左值會被轉化成對應的儲存空間(資料物件)中所儲存的值,並且不再是一個左值, 這一過程被稱為 ==**左值轉化**== > > * 不是 sizeof, _Alignof, &, ++, -- 運算子的運算元 > > * 不是 **.** 或 賦值運算子的左運算元 > > * 該左值不是陣列型別(陣列型別的左值按[其他規定](http://peterzhang.cool:3000/pdfs/c11.html#6.3.2.1p3)進行轉化) > > * 一維陣列: 不是陣列名,但可以是陣列元素 > > * 多維陣列: 不是任意N維(N>1)的陣列名或陣列元素,但可以是一維的陣列元素 > > (也就是說: 二維陣列arr\[\]\[\]中, arr[1]仍舊代表一個數組, 等同於一個數組名,不滿足左值不是陣列型別的條件) --- # 左值與指標 ## 概念上的區別 * 左值: 可以放在賦值號的左邊, 與一個儲存單元(資料物件)對應, 代表了可直接獲取和設定該單元內容的途徑. (左值就像是一個已經撥通且未結束通話的電話) * 指標值: 某一資料的儲存位置的資訊. (指標值就像是一個電話號碼) 通過左值, 你可以通過它直接獲取和設定儲存單元(資料物件)中的內容, 就像你可以直接問已撥通電話的另一頭問題或告訴另一頭一些資訊; 而指標值, 就像一個電話號碼, 想要像左值那樣獲取或設定內容, 必須先要 "按照號碼撥打電話", 這一步驟通常由取內容運算子 **\*** 完成. 如果我們用另一個變數儲存這個 "電話號碼", 這個變數就成了 "指標變數". 注意: 指標變數是一個變數, 它是左值, 而指標值並不是左值. 舉例: (我們把其他人當作是一個儲存空間,而你扮演主程式) 你正在跟小張通電話 -- 左值 <\==> `int a;` 你手裡有小張的電話號碼 -- 指標值 <\==> `&a;` 你通過給小劉打電話,獲取了小張的電話號碼,然後再給小張打電話告訴他一些事 -- 利用指標變數 <\==> `int *p = &a; *(p) = 314;` ## 左值與指標值的互相轉化 我們宣告的變數名是一類天然的左值, 它就像是我們和朋友直接面對面說話(或者一通已打通的電話); 而有時候,我們需要交談的物件並不在我們身邊, 這時候就需要我們自己去撥打電話. * 將指標值轉化為對應的左值: **取內容運算子\*** * 獲取某一左值的指標值: **取地址運算子&** ## 指標值的構成 ### 補充知識:儲存單元的地址編排 * 地址編號是基於位元組的: 一個位元組對應一個地址編號, 地址值(指標值)只能指向單個位元組 * 除了char外,C中的資料型別是多位元組 * 讀取多位元組資料的策略: * 地址值(指標值)指向儲存單元的第一個位元組 * 定義一個取值範圍, 說明取得資料的長度 ![](https://img2020.cnblogs.com/blog/1514893/202007/1514893-20200731192418083-711880219.png) ### 指標值的構成 * 指標值/地址值: 指向儲存空間的起始位元組 * 指標值的型別是無符號的多位元組數值 * 指標的型別並不影響指標值的sizeof大小 * **指標指向的型別**: 規定利用指標一次讀取/設定位元組的範圍 * 一次讀取或設定: 同時操作包含起始位元組在內的N個位元組(N由**指標指向的型別**確定) * **指標變數**增加或減少1: 地址值/指標指增加或減少N 圖示: ![](https://img2020.cnblogs.com/blog/1514893/202007/1514893-20200731192456604-1875640042.png) 測試程式碼: test.c ```c #