為什麼C C 等少數程式語言要區分左右值?

時間 2021-05-11 16:25:55

1樓:棧廬

簡單來說:

左值(lvalues)表示返回可分配值的物件的表示式,右值(rvalues)表示返回可分配給物件的值的表示式。

乙個右值(rvalues)是乙個在記憶體中沒有位址的表示式。臨時物件是在同一表示式中建立和銷毀的物件,因此通常是未命名的物件,或者是在隱式轉換完成後由編譯器建立的物件。

它們的不同之處在於,右值(rvalues)是值型別,表示式具有的屬性,而臨時物件是無法分配的物理物件。

補充:

術語lvalue、rvalue、xvalue、glvalue、prvalue描述了表示式,而不是物件。臨時物件不是右值(rvalues),因為物件不是表示式。

臨時物件是由以下六種情況之一而建立的任何物件:

將引用繫結到prvalue;

對類prvalue執行member access ;

brace-initializing 乙個std :: initializer_list;

下標或對陣列prvalue執行陣列到指標的轉換;

在prvalue上使用sizeof和typeid;

當prvalue是捨棄值表示式時;

並且在實現定義的情況下,在函式呼叫表示式中傳遞/返回trivially-copyable的物件時。

前兩者是大多數人關心的,另外四個則越來越模糊:

1const

int&n=

1;// temporary object of type int

2std

::cout

<

string

("hello"

).size

();// temporary of

type

std::

string

臨時物件的重要屬性是(除非繫結到const左值引用或右值引用)——它們在包含建立點的最大表示式的末尾被銷毀(以初始化的相反順序),這意味著它們可以用於實施raii。

RAII - cppreference.com

關於「為什麼C/C++等少數程式語言要區分左右值?「回答完畢

2樓:半仙

一些個人的看法吧,僅供參考。

左值右值這些主要是根據以下兩點來劃分的:

1. 有沒有分配位址;

2. 是不是即將消亡;

左值和右值都能表達乙個值,但是乙個值並不代表乙個完整的物件,給這個值分配了位址後,這個值才是乙個真正的物件。求出乙個值(初始化)和給這個值分配位址是兩個過程。

一般來說,乙個值在初始化前就已經分配了位址,所以我們一般不會去區分這兩個過程。但是,在一些特殊的情況下,會出現一些「臨時物件」,這些「臨時物件」的唯一用途就是用來初始化另乙個物件,初始化完成後,「臨時物件」就自動銷毀了。在這種情況下,如果先給「臨時物件」分配位址,再初始化另乙個物件,再把「臨時物件」銷毀,這幾步操作就過於臃腫了。

不如使用乙個更好的辦法:把「臨時物件」直接在分配給另乙個物件的位址上初始化。這麼操作可以省下一次初始化,一次位址分配以及一次物件銷毀,提高了效率。

這種優化手段也就是返回值優化。

但是,在有些情況下,雖然直接把「臨時物件」初始化到了另乙個物件上的位址上,但真正處理這個「臨時物件」的函式還在別的地方,另乙個物件只是暫時接收了這個值。這時,雖然給「臨時物件」分配了位址,但「臨時物件」還是乙個即將消亡的物件。這種情況也就是右值引用,右值引用就是左值,它們都具有分配位址的功能,但右值引用又和左值不同,右值引用繫結的是乙個即將消亡的物件,這個物件處理完成後會被直接銷毀。

3樓:蘇秦

粗糙理解

物件 = 指標+引用計數。

各種引用相當於就是指標然後自帶一些計數需求。引用一下引用就變成了 &&,到生命週期時先乾掉引用的引用,然後把引用的計數-1。

4樓:

可是實現上為了即便是右值表示式為了求出其值依然需要分配一塊記憶體來存放這個值,這形成了乙個臨時物件,即使之後沒有給這個物件取名,物件依然形成了,依然經歷了構造和析構的完整生命週期。

這是C++支援RAII之後的事情了。而且即便現在基本型別的右值表示式並不需要事先分配記憶體。

左右並非是C/C++才開始有的,而是當前計算機體系結構的客觀現實。本來,左值是記憶體上的變數(有位址),右值是只在暫存器當中存在的值(沒有位址)。

這樣就好理解了吧。

只不過有了結構體和物件概念之後,單個資料型別也可能大到無法完整存放在暫存器當中,需要在棧或者堆(反正都是記憶體)上開額外空間臨時存放。再加上RAII的要求,所以才把事情搞那麼繞,那麼複雜。

其實若把這個臨時空間(scratch memory)看作暫存器的擴充套件,不屬於常規記憶體,那麼就好理解了。

事實上C++從語法規定上禁止對右值取位址,即便它在記憶體上有位置。這就是告訴你,這片記憶體並不是通常的記憶體,邏輯上它只是超大暫存器。

順便提一下,左右之分(計算和儲存分離)很可能就是電腦和人腦在工作方式上最本質的區別之一。現在也有研究in place或者叫on memory計算的,感興趣的可以了解了解。

5樓:暮無井見鈴

我個人認為的區分左值和右值的原因:

賦值的意義(基本上)是把右邊的值放進左邊的位置,替換掉左邊位置原有的值。從而賦值對左邊期望的是位置與其中的值的結合(相當於 C/C++ 的物件),對右邊期望的是單純的值。在這種語義差別下就有了左右值的區別。

以下是一些補充內容。首先我說下 C++17 起的情況。

prvalue (純右值)不是物件,它是潛在生成物件的,沒有對應位置的「純」值。只有在要求有物件的語境中,它才會實質化變成物件。對於非類、非陣列型別的物件型別純右值,有些語境會使它不需要變成物件(譬如 1 + 2; 這條語句中, 1 、 2 和整個表示式都是純右值,都不會變成物件)。

C++17 起乙個類型別純右值用以初始化同型別變數或返回值時,它沒有(也不可能)被移動,而是直接實質化變成了該變數,或者通過 return 維持純右值的性質傳遞。沒有臨時物件。除非在函式呼叫中型別滿足一定條件。

這是為了允許通過暫存器傳遞返回。

有些時候純右值會需要變成臨時物件,比如繫結到引用、類或陣列型別的純右值表示式出現在棄值表示式中、要求對純右值訪問子物件等等。 C++17 起認為實質化的結果是乙個 xvalue (亡值),而引用不會繫結到純右值。

然後是 C++11/14 的劃分。這種劃分在我看來不如 C++17 的純粹。

C++11/14 中純右值一併指代實質化後產生的完整臨時物件(不實質化的情況才沒有物件),而引用會繫結到純右值。具體行為上和 C++17 區別是類型別的純右值會在該表示式出現的場所直接實質化變成臨時物件,這導致更多地方需要可呼叫的建構函式。另外 C++11/14 標準術語上沒有純右值變亡值的轉換這一步。

C++98/03 沒有右值引用型別,從而無法把左值標記成亡值(但由於 const 左值引用的性質,反過來可以)。這樣一來 C++98 中亡值指代的都是完整臨時物件的子物件,而純右值與亡值統稱右值也稍微純粹一些:要麼不指代物件,要麼指代臨時物件。

(注意下標表示式的值類別存在問題 CWG1213 。多數編譯器把陣列右值取下標為亡值視作 C++11 的改動,而 C++98 中得到左值。但最新的 gcc 把這個缺陷報告應用到 C++98 ,從而在 C++98 模式也得到右值。

)另外 C++98 起就允許更改語義的複製消除,允許一些同型別物件「直接成為結果」而不呼叫建構函式。注意即使編譯器進行複製消除,也要求相應的建構函式被定義且可呼叫。

C++11 起的移動構造是一種(可能)修改原有物件以建立新物件的操作。在發生移動構造時,必須已經存在了乙個源物件,而且指代它的表示式是右值(可以是臨時物件,也可以是手動標記成亡值的非臨時物件)。注意通常來說允許外部修改物件的操作需要維持物件在合法狀態,移動構造(或移動賦值)也只是其中一種而已。

6樓:AtomicGu

就是一種抽象罷了

提醒一下,不是所有程式語言都有所謂左右值概念,低階而又不想那麼低的語言(比如C++),就會編造出一堆奇奇怪怪的概念。

7樓:hhpy

左值和右值一般只有c和c++才有這種說法。

值的範圍比變數要大,值可以是變數,表示式

其它程式語言的賦值表示式左邊只能是變數而不能說是左值。

舉個例子:c語言

*&*a=8;

*&*a是表示式而不是變數。

為什麼要區分中醫西醫?

一路狂奔 中醫與西醫都能治病救人,中醫可治西醫無法治之病,西醫也可治中醫無法治之病,兩者的背後都有一套完善的理論模型支撐,理論模型都不一樣,你說要不要區分? 已登出 古代各種科學條件太落後,但是為了更好的把醫學經驗傳承下去,就需要一套理論,於是古人苦思冥想,在經驗用藥基礎上杜撰出了幾套相對來說能讓人...

為什麼C C 程式語言經常會提到對齊?對齊到底是什麼,為什麼要對齊,對齊有什麼好處?

哲學家 除了其他答案提到的32位或64位對齊以外,還有一種叫cache line 對齊。快取的讀取是一塊塊的,比如每64位元組是一塊。對一些訪問頻繁的資料結構,快取對齊可以保證整個資料結構都在一塊裡,cache miss 的情況下只需要讀一塊。話說回來,這些對齊都是很底層的東西了,不是對效能有極高要...

為什麼要區分我們和你們?

為什麼分你我?生物學意義上,因為人的大腦只 部分 控制自己的身體和思想,無法控制別人的。我 就出現了。社會學意義上,勞動分工的出現,資源進行交換,就有交換的雙方,這就是最早的你我之分。我 是用於資源交換的載體,例如我種的糧食換你的刀具。除了分工,還有協作,所以出現你們和我們。人和人之間的互相理解 人...