http://gideshi.blog.163.com/blog/static/8991803420081118115951620/
在ddj上看到一篇文章,針對初學者,不過我覺得還是有一定的難度的。本來想翻譯過來,想想工作量會非常大,乾脆結合自己的知識,總結幾句。有興趣的話可以參考原文“Computer Programming and Precise Terminology”。文中闡述了定義和聲明的區別,寫得很精彩,讓大家瞭解了一些internals。由於我寫過一篇相關的文章,因此這裡不再囉嗦,想看的話可以參考“[原]C/C++:如何理解複雜的聲明”。
lvalue和rvalue
在電腦的遠古時代,變數的lvalue和rvalue是指:
lvalue:變數在記憶體中的位置。通過它能夠找到記憶體中存放的變數(location value);
rvalue:存放在lvalue對應的記憶體中的東西(register value);
變數的lvalue的存在表明有儲存空間,然而其rvalue並沒有對應儲存空間,它只是那片儲存空間對應的值。如果不考慮類型的話,那個就是一個1和0的序列。
好了,我們來看看:
// Allocate 4 bytes on stack and set content to 10. int i = 10;
|
例1
i的lvalue和rvalue如下(假定i在記憶體中的地址為0x1000):
lvalue rvalue
-------- -------
|0x1000| | 10 |
-------- -------
-〉|字長 |<- ->|int |<-
圖1 i的lvalue和rvalue
對於:
// Allocate a machine word on stack and set content to NULL. int *p = NULL;
// Allocate 4 bytes on heap and set content to 10, then set p's rvalue to the start address of these bytes. p = new int(10);
|
例2
p的lvalue、rvalue以及真正的值如下(假定p的地址為0x1008):
lvalue rvalue
-------- -------- -------
|0x1008| |0x5000| | 10 |
-------- -------- -------
-〉|字長 |<- ->| 字長 |<- ->|int |<-
圖2 p的lvalue和rvalue lvalue rvalue
-------- -------
|0x5000| | 10 |
-------- -------
->| 字長 |<- ->|int |<-圖3 *p的lvalue和rvalue
傳值和傳引用
一個引用變數只能有取兩種值的rvalue:NULL和記憶體位址。在傳值時傳遞的是rvalue,傳引用則傳遞lvalue。也就是說:
int foo(int i);
int main() { int i = 10; return foo(i); }
|
這段代碼中,foo()採用傳值方式來傳遞參數。也就是說i的lvalue並沒有被傳遞到foo()中。相反,在foo()的棧上會產生一個隱藏變數,並將i的rvalue,10,拷貝到其中——這是因為,如果沒有該變數的lvalue,那麼i的rvalue就沒有存放的地方。這樣,無論在foo()中對這個變數做任何修改都不會影響到i。如果要將foo()中i的修改反映到main()中,那就要採用傳引用方式:
int foo(int *i);
int main() { int i = 10; return foo(&i); }
|
此時,&i表示的是傳遞i的lvalue。因此就不需要建立一個變數來存放該i的rvalue,相當於foo()直接存取main()中的i變數。這樣就能夠通過foo()來修改i的rvalue(參考圖1)。
那麼,如果我要修改例2中p的rvalue——使指標指向另一塊記憶體——也是同樣的道理:
int foo(int & *i); // Or: int foo(int **i); // But in c++ we prefer the first one.
int main() { int *p = NULL; p = new int(10); return foo(p);
delete p; }
|
此時根據foo()的signature,p的引用被傳遞。也就是說,p的lvalue被傳遞進去,那麼修改p的rvalue時就能使p指向另一塊記憶體(參考圖2)。
不光有些變數同時具有lvalue和rvalue,運算式也可能同時具有lvalue和rvalue。在傳遞參數時需要顯式指定使用lvalue或rvalue,除此之外,要判斷使用的是運算式的lvalue還是rvalue,需要根據context來判定。參考“數群組類型、函數類型到左值和右值的轉換”,在這裡有些爭論,不過現在完全同意這篇文章關於l/rvalue的觀點了。
木桶
對於運算式的lvalue來說,它的size是固定的——機器字長。而對於其rvalue來說就不是了。因此不能把兩個不同類型的變數相互賦值。一定要這樣做的話就需要使用類型轉換。這就好比有很多木桶,每個木桶的編號長度都是固定的,但大小不一樣。如果將小木桶裡的水倒入大木桶中自然沒有問題,但要反過來就會有問題,可能會導致資訊丟失。
Copyleft (C) 2007, 2008 raof01. 本文可以用於除商業外的所有用途。此處“用途”包括(但不限於)拷貝/翻譯(部分或全部),不包括根據本文描述來產生代碼及思想。若用於非商業,請保留此權利聲明,並標明文章原始地址和作者資訊;若要用於商業,請與作者聯絡(raof01@gmail.com),否則作者將使用法律來保證權利。