C 语言 Lvalue 和 Rvalue

28 Aug 2024 | 5 分钟阅读

在本文中,我们将结合示例讨论 C 语言中的左值 (lvalue)右值 (rvalue)

什么是左值?

左值 仅仅表示一个可识别内存地址的项(即具有地址的项)。任何赋值语句都必须允许数据存储在“左值”中。函数、表达式(如a+b)或常量(如3, 4 等)不能成为左值

“l-value” 这个术语描述了一个用于标识对象的内存位置赋值运算符 (=)左侧右侧都可能出现左值左值常被用作标识。“可修改的左值”是指指向可修改位置的表达式。对于可修改的左值,禁止使用具有数组类型、不完整类型或 const 属性的类型。为了使其成为可编辑的左值,结构或联合体中不能有任何具有 const 属性的成员。

变量的值是存储在由标识符名称指定的位置的值。如果一个标识符指向一个内存位置,并且其类型是算术类型、结构体、联合体指针,那么它就符合可修改左值的条件。例如,如果 ptr 是指向某个存储区域的指针,那么 *ptr 就是一个可修改的左值,它标识了 ptr 指向的存储区域。C 语言中,定位(指向)对象的表达式被赋予了一个新的术语,即“定位值”。左值由以下一项描述:

  • 任何变量的名称,例如整型浮点型指针结构体联合体类型标识符
  • 一个下标 ([]) 表达式,该表达式不计算数组。
  • 一个一元解引用 (*) 表达式,该表达式不指向数组。
  • 一个嵌套的左值
  • 一个 const 对象(一个不可修改的左值)。
  • 通过指针解引用(如果不是函数指针)会产生期望的效果。
  • 通过指针进行成员访问 (-> 或 .) 的结果。

示例

输出

The value of 'a' is 1
The value of 'b' is 1

说明

在此示例中,ab 被声明为整型变量。将值 1 赋给 a,然后将相同的值赋给 b。结果将显示 ab 的值均为 1。

由于赋值运算符 (=)左侧需要一个左值,因此行 9 = a; 将导致编译错误。在此示例中,字面量 9 不能被赋新值,因此它不是一个合法的左值

什么是右值?

右值 仅仅表示一个没有地址或内存中可查找位置的对象。它可以产生一个常量表达式作为返回值。一个简单的表达式如 a+b 会得到一个常量。

“r-value” 这个术语描述了一个存储在内存中特定地址的数据项。右值 是一个不能被赋值的表达式。因此,它只能出现在赋值运算符 (=)右侧,而不能出现在左侧。

示例

输出

The value of 'a' is 1
The value of 'b' is 0
The value at index 12 of 'arr' is 42
The value of 'obj.m' is 24

说明

在此示例中,变量 a, b, p, q, arr, objptr 都被声明了。这里有几个涉及左值右值赋值运算示例。

输出将分别显示 a, b 的值,以及 arr 中索引为 12 的元素的值,和 obj 的成员 m 的值,依次为 1, 0, 4224

注意:一元 & (取地址) 运算符需要一个左值作为操作数。换句话说,当且仅当 n 是左值时,&n 才是一个合法的表达式。因此,像 &12 这样的表达式是不正确的。再次说明,12 是不可寻址的,因为它不指向一个对象。

例如:

输出

The value of 'a' is undefined (garbage value)
The value of 'x' is undefined (garbage value)
The value of 'y' is undefined (garbage value)

说明

在此示例中,a, p, x,y 被声明为变量。赋值运算符 (=) 用于使 p 能够访问 a 的地址。然而,因为 a 的地址(&a)是一个右值,不能被赋新值,所以行 &a = p; 将导致编译错误。

(x< y? y: x) = 0 使用了三元表达式。三元表达式的结果是一个左值,这使得我们可以根据语句 (x y) 的结果,将 yx 赋值为 0