在 C 中,有没有办法识别右值和左值?
其中一些很容易识别,比如在赋值中,左边的值是左值,右边的值是右值。
但在其他情况下,很难识别这样的规则。
例如:*p++
and i++
(其中 p 是指向整数的指针,i 是整数) - 如何识别它是右值还是左值?上下文是++*p++
有效的,而++i++
不是因为i++
是右值(正如严肃的人所说)。
如何识别表达式中的右值和左值?
左值这个术语一直在 C 中(并被带到 C++ 并在以后扩展)。一开始就没有右值。我的草稿 (N1570) 确实列出了两次出现的右值一词——一次在脚注 #64 中,一次在索引中。
简而言之:在 C 世界中,您有两种类型的对象——左值和其他所有对象。
请注意,脚注不是标准的一部分,但它们可以提供一些有用的见解。下面是脚注 64:
64) 名称“左值”最初来自赋值表达式 E1 = E2,其中要求左操作数 E1 是(可修改的)左值。将其视为表示对象“定位器值”可能更好。有时被称为“右值”的东西在本国际标准中被描述为“表达式的值”。
左值的一个明显示例是对象的标识符。再举一个例子,如果 E 是一个一元表达式,它是一个指向对象的指针,那么 *E 是一个左值,它指定 E 指向的对象。
这提供了一个良好的开端。现在,请记住,表达式是由对象(和运算符,但我们稍后会介绍)构建的,在处理对象时需要担心两个基本的事情:类型和值。让我们看看标准对类型限制的说明(6.3.2.1/p1):
左值是一个表达式(对象类型不是 void),它可能指定一个对象;64) 如果一个左值在计算时没有指定一个对象,则行为未定义。
另外,请注意下一行很重要:
当一个对象被称为具有特定类型时,该类型由用于指定该对象的左值指定。
因此,左值可以用作类型的替代品(我们也会看到这一点)。接下来,让我们看一下对象是左值的上下文(6.3.2.1/2):
当它是 sizeof 运算符、_Alignof 运算符、一元 & 运算符、++ 运算符、-- 运算符或 . 运算符或赋值运算符
因此,这些是您需要留意的运营商。在所有其他情况下:
没有数组类型的左值被转换为存储在指定对象中的值(并且不再是左值);这称为左值转换。
有两种特殊类型:数组和函数指示符。这些衰减即被转换为类型为''pointer to type''的表达式,该表达式指向数组对象的初始元素,而不是左值/ “指向函数返回类型的指针”。(请记住,我们已经暂停了左值可以作为类型工作的事实——这正是他们所做的,sizeof
并且_Alignof
!)
lvalue(来自左侧(LHS)值)指的是内存(或寄存器)存储并且您可以为其分配值。*p++
是一个左值,因为它是一个取消引用的指针(即指向内存中的位置,ptr
而其ptr
自身的值是该位置的地址)并且++*ptr++
实际上意味着:*ptr = *ptr + 1; ptr = ptr + 1;
- 它递增指向的ptr
值,然后递增指针值本身。i++
不是左值,因为它是i
1 递增的值,并且不引用内存中的位置。您可以将此类值视为最终值 - 它们无法进一步修改,只能用作分配给左值的值. 这就是为什么它们被称为右值(来自右侧(RHS)值)。
LHS 和 RHS 指的是赋值表达式的两边A = B;
。A
是 LHS,B
是 RHS。
来自 Deitel 和 Deitel:
变量名被称为左值(对于“左值”),因为它们可以用在赋值运算符的左侧。常量被称为右值(对于“正确的值”),因为它们只能用于赋值运算符的右侧。请注意,左值也可以用作右值,但反之则不行。
x = 3; /* here, x is an lvalue */
c = x; /* and in the next line it is an rvalue */