经常使用很少定义的术语:左值

时间:2009-02-23 21:20:43

标签: terminology language-theory

什么是左值?

8 个答案:

答案 0 :(得分:27)

左值是一个可以分配给的值:

lvalue = rvalue;

它是“左值”或“左手值”的缩写,它基本上只是=符号的的值,即你指定的值。

作为左值的例子(即仅左值):

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

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

答案 1 :(得分:12)

出现在作业左侧的东西,即可以分配给的东西。

请注意,在C ++中,函数调用可能是左值,如果:

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

然后:

func() = 42;     // is legal (and not uncommon)

答案 2 :(得分:12)

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

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

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

术语“rvalue”用于诸如文字和枚举值之类的东西,以及不能享受长寿的乐趣,并且在完整表达结束时立刻被摧毁的临时用语。对于右值,不是持久性方面很重要,而是价值方面。 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();

顺便提一下,通常你有左值,但是操作员需要右值。例如,二进制内置“+”运算符添加两个值。首先和全部的左值表达式指定首先必须读出值的位置。因此,当您添加两个变量时,会发生“左值到右值”转换。标准表示左值表达式中包含的值是其右值结果:

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

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

那些可怕的东西有用吗?

区分左值和右值

有几个好处
  • 允许编译器省略为rvalues存储并使用寄存器/只读内存作为标量值
  • 将表达式标记为难以捉摸:rvalues不会长寿
    • 允许内部编译器的高效复制语义,并且c ++ 1x也向程序员公开(参见移动语义和右值引用):我们可以从可能被销毁的rvalues中窃取资源。
  • 允许在该属性上构建规则
    • 不允许从左值引用的尚未初始化的对象生成rvalues。但是左值可以很好地引用未初始化的对象
    • rvalues永远不会是多态的。它们的静态类型也必须是它们的动态类型:简化typeid运算符的规则。

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

答案 3 :(得分:8)

我所知道的最佳解释之一可以在this article on RValue references中找到。

  

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

答案 4 :(得分:2)

左值中的“L”通常被描述为代表“位置”。左值指定某物的位置;正如另一位回答者所指出的,左撇子通常可以得到他们的地址。这就是数字文字和非引用函数返回值不是左值的原因。

L代表“左”,直到将const引入C ++。 Const值不能出现在赋值的左侧。

答案 5 :(得分:1)

对于C编程语言C11 draft n1570 6.3.2.1p1来说,它很简洁:

  

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

就这么简单。

撰写此答案之日的维基百科definition几乎一样好

  

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


左值表达式的例子?给定

int a, b[1], *c;

我们可以具有以下左值表达式:abc,每个确定地指定一个对象。 b[0]也指定一个对象,因此它是一个左值表达式。那潜力呢? *c始终是左值表达式,但是如果c没有持有指向有效对象的指针,则它不会指定对象。 b[1] 可以语义上指定一个对象,但这不是因为它无限制地访问数组。它们都是左值表达式。

当评估未实际指定对象的左值表达式时,其行为为未定义

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

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

如果编译,则为左值表达式。

基本上,您可以在C中将&(运算符的地址)应用于的所有内容都是 lvalue ,除了 function 之外,因为 functions 不是C语言中的对象。

那么 L 代表什么?

C11 n1570 Footnote 64

  

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


请注意,E1 = E2不需要 进行编译,以将E1转换为ISO C中的左值。6.3.2.1p1继续:

  

可修改的左值是不具有数组类型,不完整的类型,不具有const限定类型的左值,并且它是结构或联合没有任何具有const限定类型的成员(包括所有包含的集合或联合的任何成员或元素)。

过去在C之前的语言(例如B)中,所有定位符值都可能出现在分配的左侧,因此命名。在C中,情况已不再如此,因为无法分配数组,并且ISO C添加了const


P.S。相同的脚注解释了相关术语 rvalue ,如下所示:

  

[...]在本国际标准中有时称为 rvalue ,称为表达式的。 [...]

答案 6 :(得分:0)

来自article。由于OP在提出他的问题时有点懒惰(尽管有些人不同意,请参阅评论),我也会很懒,只需在此处粘贴整个相关部分,可能会违反一些版权法。

  

对象是存储区域   可以检查并存储到。一个   左值是一个表达式   这样的对象。左值没有   必然允许修改   它指定的对象。例如,a   const对象是一个不能的左值   被修改。术语可修改   左值是用来强调的   左值允许指定的对象   被改变以及检查。该   以下对象类型是左值,   但不是可修改的左值:

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

因为这些左值不是   可修改的,它们不能出现在   转让声明的左侧。

     

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

     

某些运营商需要左值   他们的一些操作数。桌子   下面列出了这些运营商和   对其使用的其他限制。

     Operator                          Requirement
     & (unary)                         Operand must be an lvalue.
     ++ --                             Operand must be an lvalue.
                                          This applies to both prefix
                                            and postfix forms.
     = += -= *= %= >= &= ^= |=         Left operand must be an lvalue.
  

例如,所有赋值运算符   评估他们的正确操作数和   将该值分配给它们的左侧   操作数。左操作数必须是a   可修改的左值或对a的引用   可修改的对象。

     

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

答案 7 :(得分:0)

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

3 = 42;