理解C++ lvalue與rvalue

一個衆所周知的危險錯誤是,函數返回了一個局部變量的指針或引用。一旦函數棧被銷燬,這個指針就成爲了野指針,致使未定義行爲。而左值(lvalue)和右值(rvalue)的概念,本質上,是理解「程序員能夠放心使用的變量」。html

 

空泛的討論先到這裏,先看一段會報錯的代碼:ios

複製代碼
#include <iostream>

using std::cout;
using std::endl;

int foo(int &a) {
    return a;
}

int main() {
    int a = 1;
    cout << &a << endl;
    int *p = &foo(a);
}
複製代碼

這裏,對foo(a)取地址會引發錯誤: "lvalue required as left operand of assignment".字面理解是,&取地址運算符只能獲取左值的地址。程序員

毫無疑問的是,foo(a)的值是存在的,是數值1。之因此報錯,是由於編譯器認爲它不是左值,不容許程序員獲取它存放的地址。函數

 

我對這點標準的理解是:ui

獲取一個臨時空間的地址一般意味着要對這塊內存賦值。在C++程序中,臨時空間的銷燬時機是不肯定(undefined)的,它隨時被用於其餘臨時空間的存儲。因而程序員不容許使用這塊空間。聯繫以前總結的堆棧概念,能夠對C++中的變量存儲有更深的理解。spa

https://www.cnblogs.com/kinsang/p/6855579.html.net

 

堆棧概念一文,我小結了C++程序中數據能夠存在三個地方:unix

1. 函數棧,在函數體內的定義的變量指針

2. 堆,特別指使用new,malloc獲取的內存空間code

3. 靜態數據區,即.data 和.bss

 

對於lvalue的通俗描述,是「具備肯定地址的非臨時對象」,而不知足lvalue定義的值均被認爲是rvalue。換句話說,C++程序裏面出現的值,非左即右。下面咱們分析一下這三個存放數據的區域裏面能夠被使用的值的狀況:

堆的空間上的變量徹底由程序員申請和管理的,因此它們都有明確的地址,是能夠放心使用的左值。

靜態數據區

對於靜態數據區,儘管存放的位置是固定的,但裏面的數據並不能認爲都是左值。主要是由於裏面有「字面值」,包括const所實現的常量,即靜態存儲而不能被修改的值。

函數棧

當函數調用發生的時候,系統會建立函數棧,保留上下文,函數調用結束的時候,函數棧內的變量會被銷燬。函數體裏面定義的變量是左值,而臨時變量是右值。

 

拓展

C++ 11標準,爲了更好地利用臨時變量,提出Rvalue Reference,對應的的實現是move semantics (轉移語意)和Perfect Forwarding(完美轉發)。對這些新特性還不瞭解,暫時不寫。

 

參考:

http://www.cnblogs.com/dejavu/archive/2012/09/02/2667640.html

http://stackoverflow.com/questions/230584/where-are-variables-in-c-stored

http://eli.thegreenplace.net/2011/12/15/understanding-lvalues-and-rvalues-in-c-and-c

lvalue和rvalue

在計算機的遠古時代,變量的lvalue和rvalue是指:
lvalue:變量在內存中的位置。經過它可以找到內存中存放的變量(location value);
rvalue:存放在lvalue對應的內存中的東西(register value);
C++中的每一個表達式要麼是lvalue要麼是rvalue。lvalue表示一個內存位置,而rvalue表示計算表達式的結果。
rvalue引用是對有名稱變量的引用,並容許變量表示的內存經過lvalue引用來訪問。
rvalue引用是對包含表達式結果的內存位置的引用。

lvalue引用:

使用lvalue引用形參,能夠編寫直接訪問調用者實參的函數,避免了按值傳遞中的隱式複製。若不修改實參,只須要給lvalue引用類型使用const修飾符,以免意外修改參數。不管是按值傳遞、按址傳遞參數或引用都是編譯器的規則,咱們須要熟悉參數在不一樣狀況下的傳遞,好的理解方式就是輸出地址來觀察。

lvalue引用:

使用lvalue引用形參,能夠編寫直接訪問調用者實參的函數,避免了按值傳遞中的隱式複製。若不修改實參,只須要給lvalue引用類型使用const修飾符,以免意外修改參數。不管是按值傳遞、按址傳遞參數或引用都是編譯器的規則,咱們須要熟悉參數在不一樣狀況下的傳遞,好的理解方式就是輸出地址來觀察。

 

 

[cpp]  view plain  copy
 
  1. #include <iostream>      
  2. using namespace std;  
  3.   
  4. void add_1(int & num)  
  5. {  
  6.     num += 1;  
  7. }  
  8.   
  9. int main()  
  10. {  
  11.     int v = 6;  
  12.     add_1(v);  
  13.     cout <<"v="<< v << endl;  
  14.     return 0;  
  15. }  

輸出結果:v=7.

rvalue引用:

首先,舉一個報錯的例子:
[cpp]  view plain  copy
 
  1. #include <iostream>      
  2. using namespace std;  
  3.   
  4. void add_1(int && num)  
  5. {  
  6.     num += 1;  
  7. }  
  8. int main()  
  9. {  
  10.     int v = 6;  
  11.     add_1(v);  
  12.     cout << "v="<<v << endl;  
  13.     return 0;  
  14. }  
編譯會報錯: 沒法將左值綁定到右值引用。
由於:lvalue不能經過rvalue引用,有rvalue引用形參的函數只能經過rvalue實參來調用,後面列舉正確編譯的例子:
[cpp]  view plain  copy
 
  1. #include <iostream>      
  2. using namespace std;  
  3.   
  4. void add_1(int && num)  
  5. {  
  6.     num += 1;  
  7.     cout << "num=" << num << endl;  
  8. }  
  9. int main()  
  10. {  
  11.     int v = 6;  
  12.     int s = 4;  
  13.     add_1(v+s);  
  14.     cout << "v="<<v << endl;  
  15.     return 0;  
  16. }  
運行結果:num=11,v=6.

參考:

 

 

http://blog.chinaunix.net/uid-7471615-id-83794.html

http://blog.csdn.net/rogerhe/article/details/6410993 

http://www.cnblogs.com/yunqie/p/5892252.html 

相關文章
相關標籤/搜索