左值就是在賦值中可以放在賦值操作符兩邊的值,比如:
int a = 1;
double b = 2.0
a = b;
b = a;
這裡a和b都是左值,一切變數都是左值,但const變數是例外。
*p是乙個左值,和變數一樣,只要在*p的右邊加上賦值運算子,就可改變*p的值。
如果p是乙個指向常量的指標,*p就是乙個不能修改的左值,即它不能被放到賦值運算子的左邊。
i和 -i 都是表示式
但乙個是左值(i),乙個是右值(-i)。
++,--這兩種操作符要求作用於左值,所以i++合法,(-i)++不合法。
不嚴謹的講,左值右值的區分在於位於等號的那一側,左側的是左值,通常是乙個變數,右側的是右值,可以是乙個變數,或者是乙個表示式。
先看什麼是表示式:
表示式由乙個或多個運算元通過操作符組合而成。最簡單的表示式僅包含乙個字面值常量或變數。較複雜的表示式則由操作符以及乙個或多個運算元構成。
再看什麼是左值:
c++ 中存在兩種表示式:左值可以出現在賦值語句的左邊或右邊。右值只能出現在賦值的右邊,不能出現在賦值語句的左邊。
另外說明一下,i不僅是乙個表示式,它還是乙個變數,但是-i卻不是乙個變數,這是他們乙個可以自增乙個不能自增的根本原因
對於i++來說,i是乙個變數,所以是乙個左值,執行i=i+1
但是對於(-i)++來說,-i是乙個表示式,而不是乙個變數,乙個表示式是不可以作為左值的,因為沒有辦法執行這條語句:-i=-i+1(-i+1的值不能附給-i,因為沒有-i這個變數儲存空間)
乙個賦值表示式:
x = y;
在這個表示式裡,符號x的含義是x所代表的位址,這被稱為左值,左值在編譯時可知,左值表示儲存結果的地方;
在這個表示式裡,符號y的含義是y所代表的位址的內容,這被稱為右值,右值在執行時才可知,如無特別說明,右值表示「y的內容」。 在《c專家程式設計(中文版)》中第4章,對左值和右值的基本描述。
而右值則是只可以放在賦值操作符右邊的值,比如:
int a = 0;
char *b = "hello";
3 = a; // error
"howdy" = b // error
這裡3和"howdy"都是右值,所以不能放在賦值操作符左邊,一切常數、字元和字串都是右值。
l-value 與 r-value 區別
左值是引用某個物件的表示式,就是可以放在賦值左邊的東西,如:*(p+1)=7, 沒有名字的變數(*(p+1)表示式一定是乙個型別的物件)被賦值了,但左值並不一定能被賦值,因為左值可以引用某個常量。 所有的引用都是左值。
右值是表示式的值(不是引用),可以放在賦值右面。
所有的左值都可以是右值,反之不成立
int i, j, *p;
i = 7; // correct. a variable name, i, is an l-value.
7 = i; // error. a constant, 7, is an r-value.
j * 4 = 7; // error. the expression j * 4 yields an r-value.
*p = i; // a dereferenced pointer is an l-value.
const int ci = 7; // declare a const variable.
ci = 9; // error. ci is a nonmodifiable l-value
普通引用和const引用的初始化
當引用的初始式是乙個左值(是乙個物件,你可以取得他的位址)時,其初始化就是非常簡單的事情。普通t&的初始式必須是乙個t型別的。而cosnt t&則不必是乙個左值,甚至可以不是t型別的。在這樣的情況下,經過以下幾個步驟。
(1)首先,如果需要的話,將應用到型別t的隱式型別轉換。
(2)而後,將結果存入乙個型別t的臨時變數。
(3)最後,將此臨時變數用作初始化的值。
例如double& d=1; //錯誤,d是左值,不能用右值1 來初始化
const double& cd=1; //ok
對後乙個初始化的解釋是:
double temp=double(1); //首先建立乙個具有正確資料型別的臨時變數
const double& cd=temp; //而後用這個臨時變數作為cd的初始式,temp是個右值
由於左值的一大特點是可以對其賦值,而const正好將這個特點給閹割了,使該表示式成為了乙個右值,所以可以用右值來初始化。
===7.3.1引用引數===
函式的引用引數
把引數宣告成引用,實際上改變了預設的按值傳遞引數的傳遞機制。在按值傳遞時,函式操縱的是實參的本地拷貝。當引數是引用時,函式接收的是實參的左值而不是值的拷貝。
這意味著函式知道實參在記憶體中的位置,因而能夠改變它的值或取它的位址。
int obj;
void frd( double & );
int main()
對frd()的呼叫是錯誤的.實參型別是int 必須被轉換成double 以匹配引用引數的型別,
該轉換的結果是個臨時值,因為這種引用不是const 型的,所以臨時值(不是左值)不能被用來初始化該引用
什麼時候將乙個引數指定為引用比較合:
1、允許改變實參的值
2、向主調函式返回額外的結果
3、向函式傳遞大型類物件
使用指標還是引用作為引數的乙個重要區分:
引用必須被初始化為指向乙個物件,一旦初始化了,它就不能再指向其他物件,而指標可以指向一系列不同的物件也可以什麼都不指向
所以,如果乙個引數可能在函式中指向不同的物件,或者這個引數可能不指向任何物件,則必須使用指標引數
===7.3.3 陣列引數===
在c++中陣列永遠不會按值傳遞它是傳遞第乙個元素(準確地說是第0 個)的指標
如下宣告
void putvalues( int[ 10 ] ); //當編譯器對實參型別進行引數型別檢查時,並不檢查陣列的長度,編澤器忽略10
被編譯器視為
void putvalues( int* );
陣列的長度與引數宣告無關,因此下列三個宣告是等價的
// 三個等價的putvalues()宣告
void putvalues( int* );
void putvalues( int );
void putvalues( int[ 10 ] );
傳遞陣列長度的三種機制
1、提供乙個含有陣列長度的額外引數:void putvalues( int, int size );
2、將引數宣告為陣列的引用:void putvalues( int (&arr)[10] );
當引數是乙個陣列型別的引用時,陣列長度成為引數和實參型別的一部分,編譯器檢查陣列實參的長度與在函式引數型別中指定的長度是否匹配
// 引數為10 個int 的陣列
// parameter is a reference to an array of 10 ints
void putvalues( int (&arr)[10] );//只接受10 個int的陣列
int main()
3、使用抽象容器
void putvalues(const vector&vec );
C語言中的左值右值
一般說來出現在 左邊的是左值,出現在 右邊的是右值,左值是可改變的值,出現在賦值號左邊的符號所代表的位址上的內容一定是可以被修改的,唯讀變數是不能做左值的。i 就可以作為左值,而 i 卻不能作為左值。這也是為什麼 i 合法,而 i 卻不合法。變數 i i為左值時 編譯器認為 i 代表的是 i 的位址...
c 左值 右值 右值引用 左值引用
c 裡一切值必須屬於左值 右值兩者之一。左值 一切變數 包括用const修飾的變數 物件 包括引用都屬於左值 右值 一切字面值 可以是巨集 臨時無名物件 函式返回值 表示式 如a n 說明一下 函式返回值,返回的是某乙個型別的值,並不是返回變數。左值並不是說能放在 左邊的值就是左值 雖然用const...
c 左值 右值 左值引用 右值引用
在c語言中,左值認為是賦值語句的左側,右值認為是賦值語句的右側。在c 中,意義稍有不同。c 中,每乙個表示式會產生乙個左值或者右值,相應的,該表示式也就被稱作 左值表示式 右值表示式 乙個左值表示式的求值結果是乙個物件或者是乙個函式。左值可以當右值使用,而右值不能當左值使用。c prime 中這麼簡...