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

Terminology 经常使用但很少定义的术语:左值,terminology,language-theory,Terminology,Language Theory,什么是左值?出现在赋值左边的东西,即可以赋值的东西 注意,在C++中,函数调用可能是一个LValuy: int & func() { static int a = 0; return a; } 然后: 左值是可以分配给以下对象的值: lvalue = rvalue; 它是“left value”或“lefthand value”的缩写,基本上只是=符号左侧的值,即您分配给某个对象的值 作为非左值(即仅右值)的示例: 该代码不起作用,因为printf()(返回int的函数)

什么是左值?

出现在赋值左边的东西,即可以赋值的东西

<>注意,在C++中,函数调用可能是一个LValuy:
int & func() {
   static int a = 0;
   return a;
}
然后:

左值是可以分配给以下对象的值:

lvalue = rvalue;
它是“left value”或“lefthand value”的缩写,基本上只是
=
符号左侧的值,即您分配给某个对象的值

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


该代码不起作用,因为
printf()
(返回
int
的函数)不能是左值,只能是右值。由于OP在问他的问题时有点懒(尽管有些人不同意,请参见评论),我也会懒,只是简单地将整个相关部分粘贴在这里,可能违反了一些版权法

对象是一个存储区域,它 可以检查并存储到。一 左值是指 这样一个物体。左值不等于零 必须允许修改 它指定的对象。例如,一个 const对象是一个左值,不能 可以修改。可修改一词 左值用于强调 左值允许指定的对象 既要检查也要改变。这个 以下对象类型是左值, 但不可修改的左值:

  • 数组类型
  • 不完整的类型
  • 常量限定类型
  • 对象是结构或联合类型,其成员之一具有 常量限定类型
因为这些左值不是 可修改,它们不能出现在屏幕上 赋值语句的左侧

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

某些运算符需要为 它们的一些操作数。桌子 下面列出了这些操作符和 对其使用的附加限制

操作员要求 &(一元)操作数必须是左值。 ++--操作数必须是左值。 这适用于两个前缀 和后缀形式。 =+=-=*=%=>=&=^=|=左操作数必须是左值。 例如,所有赋值运算符 计算其右操作数并 将该值分配到其左侧 操作数。左操作数必须是 可修改的左值或对 可修改对象

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


我所知道的最好的解释之一可以在中找到

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


它通常位于“=”运算符的左侧。然而,随着时间的推移,“左值”/“右值”的含义发生了变化。C++中添加了一个“不可修改的LValk”,它是任何不能赋值的值:数组和变量“const”是两个例子。在C语言中,不能分配给任何右值(见下文)。同样,在C++中,不能分配给不是用户定义的类类型的rValue/ 您可以说“左值”是一个表达式,它命名一个对象,该对象随时间持续存在并占用某些存储位置。是否可以指定给该表达式对于该分类并不重要。尤其是引用也是左值,因为它的名称会随着时间的推移而持续存在。以下所有内容都是左值,因为它们都引用命名对象。还要注意,
const
对左值没有任何影响

int a; lvalue: a;
       lvalue: ++a;
int a[2]; lvalue: a;
int &ra = a; lvalue: ra;
int *pa = &a; lvalue: *pa;
术语“rvalue”用于文字量和枚举数值,以及不享受长寿乐趣的临时值,它们在完整表达式结束时会立即被销毁。对于右值,重要的不是持久性方面,而是值方面。C++中的函数是LValk,因为它们是持久的,并且它们有地址,即使它们不是对象。我在上面的左值概述中省略了它们,因为当只考虑对象时,更容易理解左值。以下所有值均为右值:

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,…)和枚举数值

那吓人的东西怎么有用? 区分左值和右值有几个好处

  • 允许编译器省略对右值的存储和使用reg
    int a; lvalue: a;
           lvalue: ++a;
    int a[2]; lvalue: a;
    int &ra = a; lvalue: ra;
    int *pa = &a; lvalue: *pa;
    
    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. 
    
    3 = 42;
    
    int a, b[1], *c;
    
    ((flag ? foo() : bar())->pointed_to_by_struct_member[42])[0]