青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

隨筆-59  評論-36  文章-0  trackbacks-0

C and C++ enforce subtle differences on the expressions to the left and right of the assignment operator

If you've been programming in either C or C++ for a while, it's likely that you've heard the terms lvalue (pronounced "ELL-value") and rvalue (pronounced "AR-value"), if only because they occasionally appear in compiler error messages. There's also a good chance that you have only a vague understanding of what they are. If so, it's not your fault.

Most books on C or C++ do not explain lvalues and rvalues very well. (I looked in a dozen books and couldn't find one explanation I liked.) This may be due to of the lack of a consistent definition even among the language standards. The 1999 C Standard defines lvalue differently from the 1989 C Standard, and each of those definitions is different from the one in the C++ Standard. And none of the standards is clear.

Given the disparity in the definitions for lvalue and rvalue among the language standards, I'm not prepared to offer precise definitions. However, I can explain the underlying concepts common to the standards.

As is often the case with discussions of esoteric language concepts, it's reasonable for you to ask why you should care. Admittedly, if you program only in C, you can get by without understanding what lvalues and rvalues really are. Many programmers do. But understanding lvalues and rvalues provides valuable insights into the behavior of built-in operators and the code compilers generate to execute those operators. If you program in C++, understanding the built-in operators is essential background for writing well-behaved overloaded operators.


Basic concepts

--------------------------------------------------------------------------------

Kernighan and Ritchie coined the term lvalue to distinguish certain expressions from others. In The C Programming Language (Prentice-Hall, 1988), they wrote "An object is a manipulatable region of storage; an lvalue is an expression referring to an object....The name 'lvalue' comes from the assignment expression E1 = E2 in which the left operand E1 must be an lvalue expression."

In other words, the left and right operands of an assignment expression are themselves expressions. For the assignment to be valid, the left operand must refer to an object-it must be an lvalue. The right operand can be any expression. It need not be an lvalue. For example:

int n;

declares n as an object of type int. When you use n in an assignment expression such as:

n = 3;

n is an expression (a subexpression of the assignment expression) referring to an int object. The expression n is an lvalue.

Suppose you switch the left and right operands around:

3 = n;

Unless you're a former Fortran programmer, this is obviously a silly thing to do. The assignment is trying to change the value of an integer constant. Fortunately, C and C++ compilers reject it as an error. The basis for the rejection is that, although the assignment's left operand 3 is an expression, it's not an lvalue. It's an rvalue. It doesn't refer to an object; it just represents a value.

I don't know where the term rvalue comes from. Neither edition of the C Standard uses it, other than in a footnote stating "What is sometimes called 'rvalue' is in this standard described as the 'value of an expression.'"

The C++ Standard does use the term rvalue, defining it indirectly with this sentence: "Every expression is either an lvalue or an rvalue." So an rvalue is any expression that is not an lvalue.

Numeric literals, such as 3 and 3.14159, are rvalues. So are character literals, such as 'a'. An identifier that refers to an object is an lvalue, but an identifier that names an enumeration constant is an rvalue. For example:

enum color { red, green, blue };
color c;
...
c = green;    // ok
blue = green;    // error

The second assignment is an error because blue is an rvalue.

Although you can't use an rvalue as an lvalue, you can use an lvalue as an rvalue. For example, given:

int m, n;

you can assign the value in n to the object designated by m using:

m = n;

This assignment uses the lvalue expression n as an rvalue. Strictly speaking, a compiler performs what the C++ Standard calls an lvalue-to-rvalue conversion to obtain the value stored in the object to which n refers.


Lvalues in other expressions
-------------------------------------------------------------------------------

Although lvalues and rvalues got their names from their roles in assignment expressions, the concepts apply in all expressions, even those involving other built-in operators.

For example, both operands of the built-in binary operator + must be expressions. Obviously, those expressions must have suitable types. After conversions, both expressions must have the same arithmetic type, or one expression must have a pointer type and the other must have an integer type. But either operand can be either an lvalue or an rvalue. Thus, both x + 2 and 2 + x are valid expressions.

Although the operands of a binary + operator may be lvalues, the result is always an rvalue. For example, given integer objects m and n:

m + 1 = n;

is an error. The + operator has higher precedence than the = operator. Thus, the assignment expression is equivalent to:

(m + 1) = n;    // error

which is an error because m + 1 is an rvalue.

As another example, the unary & (address-of) operator requires an lvalue as its operand. That is, &n is a valid expression only if n is an lvalue. Thus, an expression such as &3 is an error. Again, 3 does not refer to an object, so it's not addressable.

Although the unary & requires an lvalue as its operand, it's result is an rvalue. For example:

int n, *p;
...
p = &n;    // ok
&n = p;    // error: &n is an rvalue

In contrast to unary &, unary * produces an lvalue as its result. A non-null pointer p always points to an object, so *p is an lvalue. For example:

int a[N];
int *p = a;
...
*p = 3;     // ok

Although the result is an lvalue, the operand can be an rvalue, as in:

*(p + 1) = 4;    // ok


Data storage for rvalues
--------------------------------------------------------------------------------

Conceptually, an rvalue is just a value; it doesn't refer to an object. In practice, it's not that an rvalue can't refer to an object. It's just that an rvalue doesn't necessarily refer to an object. Therefore, both C and C++ insist that you program as if rvalues don't refer to objects.

The assumption that rvalues do not refer to objects gives C and C++ compilers considerable freedom in generating code for rvalue expressions. Consider an assignment such as:

n = 1;

where n is an int. A compiler might generate named data storage initialized with the value 1, as if 1 were an lvalue. It would then generate code to copy from that initialized storage to the storage allocated for n. In assembly language, this might look like:

one: .word 1
...
mov (one), n

Many machines provide instructions with immediate operand addressing, in which the source operand can be part of the instruction rather than separate data. In assembly, this might look like:

mov #1, n

In this case, the rvalue 1 never appears as an object in the data space. Rather, it appears as part of an instruction in the code space.

On some machines, the fastest way to put the value 1 into an object is to clear it and then increment it, as in:

clr n
inc n

Clearing the object sets it to zero. Incrementing adds one. Yet data representing the values 0 and 1 appear nowhere in the object code.


More to come
--------------------------------------------------------------------------------

Although it's true that rvalues in C do not refer to objects, it's not so in C++. In C++, rvalues of a class type do refer to objects, but they still aren't lvalues. Thus, everything I've said thus far about rvalues is true as long as we're not dealing with rvalues of a class type.

Although lvalues do designate objects, not all lvalues can appear as the left operand of an assignment. I'll pick up with this in my next column.

如下:

 

--------------------------------------------------------------------------------
Non-modifiable Lvalues

--------------------------------------------------------------------------------


Lvalues actually come in a variety of flavors. If you really want to understand how compilers evaluate expressions, you'd better develop a taste.

const 限定符的含義: 比如 int const m;
它并不是說m的值不能被修改, 而是指 m 不能修改它引用的對象!
e.g:
int m;
int const *p = &m;
m += 1;  //right
*p += 1; //wrong
 

An expression is a sequence of operators and operands that specifies a computation. That computation might produce a resulting value and it might generate side effects. An assignment expression has the form:

e1 = e2

where e1 and e2 are themselves expressions. The right operand e2 can be any expression, but the left operand e1 must be an lvalue expression. That is, it must be an expression that refers to an object. As I explained last month ("Lvalues and Rvalues," June 2001, p. 70), the "l" in lvalue stands for "left," as in "the left side of an assignment expression." For example:

int n;

declares n as an object of type int. When you use n in an assignment expression such as:

n = 3;

the n is an expression (a subexpression of the assignment expression) referring to an int object. The expression n is an lvalue. On the other hand:

3 = n;

causes a compilation error, and well it should, because it's trying to change the value of an integer constant. Although the assignment's left operand 3 is an expression, it's not an lvalue. It's an rvalue. An rvalue is simply any expression that is not an lvalue. It doesn't refer to an object; it just represents a value.

Although lvalue gets its name from the kind of expression that must appear to the left of an assignment operator, that's not really how Kernighan and Ritchie defined it. In the first edition of The C Programming Language (Prentice-Hall, 1978), they defined an lvalue as "an expression referring to an object." At that time, the set of expressions referring to objects was exactly the same as the set of expressions eligible to appear to the left of an assignment operator. But that was before the const qualifier became part of C and C++.

The const qualifier renders the basic notion of lvalues inadequate to describe the semantics of expressions. We need to be able to distinguish between different kinds of lvalues. And that's what I'm about to show you how to do. But first, let me recap.


A few key points
--------------------------------------------------------------------------------

The assignment operator is not the only operator that requires an lvalue as an operand. The unary & (address-of) operator requires an lvalue as its sole operand. That is, &n is a valid expression only if n is an lvalue. Thus, an expression such as &3 is an error. The literal 3 does not refer to an object, so it's not addressable.

Not only is every operand either an lvalue or an rvalue, but every operator yields either an lvalue or an rvalue as its result. For example, the binary + operator yields an rvalue. Given integer objects m and n:

m + 1 = n;

is an error. The + operator has higher precedence than the = operator. Thus, the assignment expression is equivalent to:

(m + 1) = n; // error

which is an error because m + 1 is an rvalue.

An operator may require an lvalue operand, yet yield an rvalue result. The unary & is one such operator. For example:

int n, *p;
...
p = &n; // ok
&n = p; // error: &n is an rvalue


On the other hand, an operator may accept an rvalue operand, yet yield an lvalue result, as is the case with the unary * operator. A valid, non-null pointer p always points to an object, so *p is an lvalue. For example:

int a[N];
int *p = a;
...
*p = 3; // ok

Although the result is an lvalue, the operand can be an rvalue, as in:

*(p + 1) = 4; // ok

With this in mind, let's look at how the const qualifier complicates the notion of lvalues.


Lvalues and the const qualifier
--------------------------------------------------------------------------------

A const qualifier appearing in a declaration modifies the type in that declaration, or some portion thereof. For example: int const n = 127;

declares n as object of type "const int." The expression n refers to an object, almost as if const weren't there, except that n refers to an object the program can't modify. For example, an assignment such as:

n = 0; // error, can't modify n

produces a compile-time error, as does:

++n; // error, can't modify n

(I covered the const qualifier in depth in several of my earlier columns. See "Placing const in Declarations," June 1998, p. 19 or "const T vs. T const," February 1999, p. 13, among others.) How is an expression referring to a const object such as n any different from an rvalue? After all, if you rewrite each of the previous two expressions with an integer literal in place of n, as in:

7 = 0; // error, can't modify literal ++7; // error, can't modify literal

they're both still errors. You can't modify n any more than you can an rvalue, so why not just say n is an rvalue, too? The difference is that you can take the address of a const object, but you can't take the address of an integer literal. For example:

int const *p;
...
p = &n; // ok
p = &7; // error

Notice that p declared just above must be a "pointer to const int." If you omitted const from the pointer type, as in:

int *p;

then the assignment:

p = &n; // error, invalid conversion

would be an error. When you take the address of a const int object, you get a value of type "pointer to const int," which you cannot convert to "pointer to int" unless you use a cast, as in:

p = (int *)&n; // (barely) ok

Although the cast makes the compiler stop complaining about the conversion, it's still a hazardous thing to do. (See "What const Really Means," August 1998, p. 11.)

Thus, an expression that refers to a const object is indeed an lvalue, not an rvalue. However, it's a special kind of lvalue called a non-modifiable lvalue-an lvalue that you can't use to modify the object to which it refers. This is in contrast to a modifiable lvalue, which you can use to modify the object to which it refers.

Once you factor in the const qualifier, it's no longer accurate to say that the left operand of an assignment must be an lvalue. Rather, it must be a non-modifiable lvalue. In fact, every arithmetic assignment operator, such as += and *=, requires a modifiable lvalue as its left operand. For all scalar types:

x += y; // arithmetic assignment

is equivalent to:

x = x + y; // assignment

except that it evaluates x only once. Since the x in this assignment must be a modifiable lvalue, it must also be a modifiable lvalue in the arithmetic assignment. Not every operator that requires an lvalue operand requires a modifiable lvalue. The unary & operator accepts either a modifiable or a non-modifiable lvalue as its operand. For example, given:

int m;
int const n = 10;

&m is a valid expression returning a result of type "pointer to int," and &n is a valid expression returning a result of type "pointer to const int."


What it is that's really non-modifiable
--------------------------------------------------------------------------------
Earlier, I said a non-modifiable lvalue is an lvalue that you can't use to modify an object. Notice that I did not say a non-modifiable lvalue refers to an object that you can't modify-I said you can't use the lvalue to modify the object. The distinction is subtle but nonetheless important, as shown in the following example. Consider:

int n = 0;
int const *p;
...
p = &n;

At this point, p points to n, so *p and n are two different expressions referring to the same object. However, *p and n have different types. As I explained in an earlier column ("What const Really Means"), this assignment uses a qualification conversion to convert a value of type "pointer to int" into a value of type "pointer to const int." Expression n has type "(non-const) int." It is a modifiable lvalue. Thus, you can use n to modify the object it designates, as in:

n += 2;

On the other hand, p has type "pointer to const int," so *p has type "const int." Expression *p is a non-modifiable lvalue. You cannot use *p to modify the object n, as in:

*p += 2;

even though you can use expression n to do it. Such are the semantics of const in C and C++.


In summary
--------------------------------------------------------------------------------
Every expression in C and C++ is either an lvalue or an rvalue. An lvalue is an expression that designates (refers to) an object. Every lvalue is, in turn, either modifiable or non-modifiable. An rvalue is any expression that isn't an lvalue. Operationally, the difference among these kinds of expressions is this:

  • A modifiable lvalue is addressable (can be the operand of unary &) and assignable (can be the left operand of =).
  • A non-modifiable lvalue is addressable, but not assignable.
  • An rvalue is neither addressable nor assignable. 
  • Again, as I cautioned last month, all this applies only to rvalues of a non-class type. Classes in C++ mess up these concepts even further. 

Dan Saks is a high school track coach and the president of Saks & Associates, a C/C++ training and consulting company. You can write to him at dsaks@wittenberg.edu.

 

本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/SeeSeaBee/archive/2007/09/08/1777120.aspx

posted on 2010-02-06 22:41 zhaoyg 閱讀(851) 評論(0)  編輯 收藏 引用 所屬分類: C/C++學習筆記
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            欧美日韩二区三区| 在线视频中文亚洲| 久久黄色小说| 亚洲欧美在线x视频| 99视频精品全部免费在线| 暖暖成人免费视频| 麻豆成人91精品二区三区| 欧美福利一区| 欧美激情一区二区三区全黄| 久久午夜电影| 欧美成人一区在线| 亚洲人成在线播放网站岛国| 久久激情五月激情| 久久综合综合久久综合| 免费久久99精品国产自| 欧美国产日韩在线观看| 亚洲第一中文字幕在线观看| 欧美黄色日本| 亚洲精品在线观| 中文精品视频一区二区在线观看| 亚洲欧美一区二区三区极速播放| 久久激情五月婷婷| 欧美激情综合五月色丁香| 欧美国产日韩一二三区| 国产精品任我爽爆在线播放| 国产九九精品| 亚洲精品美女免费| 欧美一区二区三区四区夜夜大片| 免费在线亚洲| 日韩一级欧洲| 久久夜色精品国产| 国产精品日韩| 亚洲免费av网站| 久久久精品一区| 中文一区字幕| 欧美精品精品一区| 又紧又大又爽精品一区二区| 日韩午夜电影av| 久久国产毛片| 亚洲视频中文| 欧美三区美女| 99国产麻豆精品| 牛牛精品成人免费视频| 亚洲美女视频在线免费观看| 久久影视精品| 国产亚洲电影| 亚洲综合视频网| 亚洲欧洲精品一区二区三区不卡 | 欧美制服第一页| 国产精品www色诱视频| 亚洲三级免费观看| 欧美电影免费观看高清完整版| 亚洲小少妇裸体bbw| 欧美日韩福利视频| 99国产一区| 亚洲精品少妇30p| 欧美二区在线| 亚洲三级国产| 日韩午夜在线视频| 欧美高清在线一区| 亚洲一区二区在线视频| 欧美亚洲系列| 国产伦精品一区二区三区视频孕妇 | 国产精品美女www爽爽爽视频| 亚洲人精品午夜| 美女日韩在线中文字幕| 欧美一级精品大片| 国产情人节一区| 久久爱www| 欧美一区视频| 在线看欧美视频| 欧美激情亚洲综合一区| 欧美激情久久久久| 亚洲精品久久久久久久久| 欧美成人黑人xx视频免费观看| 亚洲综合日本| 在线观看福利一区| 亚洲国产精品高清久久久| 欧美高清在线视频| 亚洲午夜在线视频| 欧美一区观看| 亚洲精品一区二区三区蜜桃久| 亚洲人成在线播放网站岛国| 欧美视频在线视频| 久久裸体视频| 欧美伦理a级免费电影| 亚洲欧美一级二级三级| 久久超碰97人人做人人爱| 亚洲国产专区| 亚洲性夜色噜噜噜7777| 影音欧美亚洲| 99精品视频一区| 国产亚洲精品bt天堂精选| 亚洲电影免费观看高清完整版在线观看 | 亚洲人体一区| 国产欧美一区二区三区视频| 欧美ed2k| 国产精品免费小视频| 麻豆成人91精品二区三区| 欧美久久电影| 玖玖玖国产精品| 欧美日韩一区在线| 老司机久久99久久精品播放免费| 欧美精品日韩综合在线| 欧美在线亚洲一区| 欧美日韩成人在线播放| 蜜桃av综合| 国产精品日韩精品欧美精品| 国产一区二区精品丝袜| 亚洲国产成人在线视频| 国产精品久久久久久av下载红粉| 久久久久久**毛片大全| 欧美三区不卡| 亚洲黄网站黄| ●精品国产综合乱码久久久久| 一区二区三区三区在线| 亚洲国产高清一区二区三区| 亚洲专区国产精品| 在线亚洲观看| 欧美粗暴jizz性欧美20| 久久久噜久噜久久综合| 国产精品视频久久久| 99精品国产99久久久久久福利| 亚洲国产欧美久久| 久久九九精品| 久久久av网站| 国产日韩欧美另类| 亚洲一区美女视频在线观看免费| 一本久久综合| 欧美精品在欧美一区二区少妇| 欧美不卡福利| 亚洲高清一二三区| 久久一区二区三区超碰国产精品| 久久精品人人做人人爽电影蜜月| 国产精品第十页| 亚洲小视频在线| 欧美一区网站| 国产又爽又黄的激情精品视频| 亚洲欧美日韩一区在线| 午夜精品久久久久影视| 国产精品久久久久久影视| 一区二区三区欧美在线| 欧美一区2区视频在线观看| 国产欧美va欧美不卡在线| 亚洲淫性视频| 久久久水蜜桃| 91久久精品视频| 欧美久久电影| 亚洲在线观看免费| 久久视频在线免费观看| 亚洲成色777777女色窝| 欧美~级网站不卡| 亚洲日本理论电影| 亚洲欧美电影院| 国产综合香蕉五月婷在线| 久久综合九色综合网站 | 亚洲欧美日韩爽爽影院| 久久视频在线看| 亚洲毛片在线观看.| 欧美日韩四区| 欧美一区二区三区免费大片| 久久亚洲精选| 亚洲最新在线视频| 国产精品区一区| 久久夜色撩人精品| 一本色道久久88综合亚洲精品ⅰ | 欧美二区在线播放| 国产精品99久久久久久久vr| 久久精品一区四区| 亚洲美女黄网| 国产欧美不卡| 欧美日韩不卡在线| 欧美一区二区福利在线| 亚洲丰满在线| 国产一区二区三区直播精品电影| 久久久久久久精| 亚洲毛片在线观看.| 久久狠狠亚洲综合| 99国产欧美久久久精品| 国产亚洲二区| 欧美日韩中文字幕| 久久久久久日产精品| 99国内精品久久久久久久软件| 久久久精品日韩| 亚洲小说区图片区| 在线观看成人av电影| 国产精品久久久久久久浪潮网站| 久久精品视频va| 一区二区激情视频| 欧美国产日本| 久久久中精品2020中文| 在线亚洲欧美专区二区| 在线观看欧美日韩| 国产一区成人| 国产欧美日韩一级| 欧美日本韩国| 欧美国产日韩精品| 美日韩精品免费| 久久精品国产亚洲aⅴ| 亚洲一级免费视频|