21

什么是左值?

4

8 回答 8

29

左值是可以分配给的值:

lvalue = rvalue;

它是“left value”或“lefthand value”的缩写,它基本上只是符号左侧=值,即您分配的值。

作为不是左值(即仅右值)的示例:

printf("Hello, world!\n") = 100; // WTF?

该代码不起作用,因为printf()(返回 的函数int)不能是左值,只能是右值。

于 2009-02-23T21:23:31.100 回答
12

出现在分配左侧的东西,即可以分配到的东西。

请注意,在 C++ 中,如果满足以下条件,函数调用可能是左值:

int & func() {
   static int a = 0;
   return a;
}

然后:

func() = 42;     // is legal (and not uncommon)
于 2009-02-23T21:22:31.807 回答
12

它传统上是“=”运算符的左侧。然而,随着时间的推移,“左值”/“右值”的含义发生了变化。C++ 添加了“不可修改左值”的术语,它是任何不能分配给的左值:用“const”限定的数组和变量就是两个例子。在 C 中,您不能分配给任何右值(见下文)。同样,在 C++ 中,您不能分配给不属于某些用户定义的类类型的右值。

您可以说“左值”是一个表达式,它命名一个随时间持续存在并占用某个存储位置的对象。您是否可以分配给该表达式对于该分类并不重要。特别是引用也是一个左值,因为它的名称会随着时间的推移而持续存在。以下都是左值,因为它们都引用了命名对象。另请注意, aconst对左值没有任何影响。

int a; lvalue: a;
       lvalue: ++a;
int a[2]; lvalue: a;
int &ra = a; lvalue: ra;
int *pa = &a; lvalue: *pa;

术语“右值”用于诸如文字和枚举值之类的东西,以及不享受长寿乐趣并在完整表达式结束时立即销毁的临时对象。对于右值,重要的不是持久性方面,而是值方面。C++ 中的函数是左值,因为它们是持久的并且有地址,即使它们不是对象。我在上面的左值概述中将它们排除在外,因为当首先只考虑对象时更容易掌握左值。以下都是右值:

enum { FOO, BAR }; rvalue: BAR;
int a[2]; rvalue: (a+1);
rvalue: 42;
int a; rvalue: a++; // refering to a temporary
struct mystruct { }; mystruct f() { return mystruct(); } rvalue: f();

顺便说一句,通常你有一个左值,但运算符需要一个右值。例如,二进制内置“+”运算符添加两个值。左值表达式 first and for all 指定首先必须读出值的位置。因此,当您添加两个变量时,会发生“左值到右值”的转换。标准说,左值表达式中包含的值是它的右值结果:

int a = 0, b = 1;
int c = a + b; // read values out of the lvalues of a and b. 

其他运算符不采用右值,而是采用左值。他们不读取值。一个例子是地址运算符,&。您不能获取右值表达式的地址。有些右值甚至不是对象:它们不占用任何存储空间。示例再次是文字 (10, 3.3, ...) 和枚举值。

那可怕的东西有什么用?

那么区分左值和右值有几个优点

  • 允许编译器省略存储右值并使用寄存器/只读内存存储标量值
  • 将表达式标记为难以捉摸:右值不会长久
    • 允许编译器内部和 c++1x 中的高效复制语义也暴露给程序员(参见移动语义和右值引用):我们可以从右值中窃取资源,这些资源无论如何都会被销毁。
  • 允许在该属性上建立规则
    • 不允许从左值引用的尚未初始化的对象生成右值。但是左值可以引用未初始化的对象就好了
    • 右值永远不能是多态的。它们的静态类型也必须是它们的动态类型:简化 typeid 运算符的规则。

......还有更多,我觉得......

于 2009-02-23T21:46:23.200 回答
8

我所知道的最好的解释之一可以在这篇关于 RValue 参考的文章中找到。

确定表达式是否为左值的另一种方法是询问“我可以获取它的地址吗?”。如果可以,它是一个左值。如果你不能,它是一个右值。例如, &obj 、 &*ptr 、 &ptr[index] 和 &++x 都是有效的(即使其中一些表达式很愚蠢),而 &1729 、 &(x + y) 、 &std::string("meow ") 和 &x++ 都是无效的。为什么这行得通?地址运算符要求其“操作数应为左值”(C++03 5.3.1/2)。为什么需要这样?获取持久对象的地址很好,但是获取临时对象的地址会非常危险,因为临时对象会很快消失。

于 2009-02-23T21:33:52.403 回答
2

左值中的“L”通常被描述为代表“位置”。左值指定某物的位置;正如另一位回答者指出的那样,左值通常可以获取其地址。这就是为什么数字文字和非引用函数返回值不是左值的原因。

在 const 被引入 C++ 之前,L 曾经代表“左”。const 左值不能出现在赋值的左侧。

于 2009-02-23T22:05:44.650 回答
1

对于 C 编程语言C11 草案 n1570 6.3.2.1p1简洁地说:

左值是一个表达式(对象类型不是 void),它可能指定一个对象 [...]

它是如此简单。

撰写此答案当天的维基百科定义几乎一样好

[...]一个指向存储位置的值,可能允许分配新值或提供可以写入数据的变量的可访问内存地址的值,它被称为位置值。


左值表达式的例子?给定

int a, b[1], *c;

我们可以有以下左值表达式:ab并且c每个都明确指定一个对象。b[0]也指定一个对象,因此它是一个左值表达式。那么潜力呢?*c始终是左值表达式,但如果c不包含指向有效对象的指针,则不指定对象。b[1] 可以在语义上指定一个对象,但它不能,因为它访问数组越界。它们都是左值表达式。

当计算一个实际上不指定对象的左值表达式时,行为是undefined

左值表达式可能比这更复杂,例如:

((flag ? foo() : bar())->pointed_to_by_struct_member[42])[0] 

如果它编译,它是一个左值表达式。

基本上,您可以&在 C 中应用 (address-of operator) 的所有内容都是左值,除了函数,因为函数不是 C 中的对象。

那么L代表什么呢?

C11 n1570 脚注 64

64) 名称lvalue最初来自赋值表达式E1 = E2,其中要求左操作数E1是(可修改的)左值。将其视为表示对象定位器值可能更好。[...]


请注意,在 ISO C 中不需要编译为左E1 = E2 6.3.2.1p1继续E1

修改的左值是没有数组类型、没有不完整类型、没有const限定类型,并且如果它是结构体或联合体,则没有任何成员(递归地包括任何成员或所有包含的聚合或联合的元素)具有const- 限定类型。

在 C 之前的语言(例如 B)中曾经是所有定位符值都可能出现在赋值的左侧的情况,因此命名。这在 C 中不再是这种情况,因为数组永远不会被分配给,并且 ISO C 添加了const.


PS 相同的脚注解释了相关术语右值,如下所示:

[...] 有时称为右值的东西在本国际标准中被描述为表达式的值。[...]

于 2018-10-16T16:41:30.797 回答
0

从这篇文章。由于 OP 在问他的问题时有点懒惰(尽管有些人不同意,请参阅评论),我也会很懒惰,只是将整个相关部分粘贴在这里,可能违反了一些版权法。

对象是可以检查和存储的存储区域。左值是引用此类对象的表达式。左值不一定允许修改它指定的对象。例如,一个 const 对象是一个不能修改的左值。术语可修改左值用于强调左值允许更改和检查指定对象。以下对象类型是左值,但不是可修改的左值:

  • 数组类型
  • 不完整的类型
  • 一个 const 限定的类型
  • 对象是结构或联合类型,其成员之一具有 const 限定类型

因为这些左值是不可修改的,所以它们不能出现在赋值语句的左侧。

在 C++ 中,返回引用的函数调用是左值。否则,函数调用是右值表达式。在 C++ 中,每个表达式都产生一个左值、一个右值或不产生值。

某些运算符的某些操作数需要左值。下表列出了这些运算符以及对其使用的附加限制。

     操作员要求
     &(一元)操作数必须是左值。
     ++ -- 操作数必须是左值。
                                          这适用于两个前缀
                                            和后缀形式。
     = += -= *= %= >= &= ^= |= 左操作数必须是左值。

例如,所有赋值运算符都会计算其右操作数并将该值分配给其左操作数。左操作数必须是可修改的左值或对可修改对象的引用。

地址运算符 (&) 需要一个左值作为操作数,而递增 (++) 和减量 (--) 运算符需要一个可修改的左值作为操作数。

于 2009-02-23T21:25:07.167 回答
0

一个绝对不是左值的简单示例:

3 = 42;
于 2009-02-23T21:49:23.677 回答