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

发布于 2024-07-14 05:42:29 字数 13 浏览 4 评论 0原文

什么是左值?

What is an lvalue?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

躲猫猫 2024-07-21 05:42:29

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

lvalue = rvalue;

它是“左值”或“左手值”的缩写,基本上就是左侧的值>= 符号,即您分配的值。

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

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

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

An lvalue is a value that can be assigned to:

lvalue = rvalue;

It's short for "left value" or "lefthand value" and it's basically just the value on the left of the = sign, i.e. the value you assign something to.

As an example of what is not an lvalue (i.e rvalue only):

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

That code doesn't work because printf() (a function that returns an int) cannot be an lvalue, only an rvalue.

定格我的天空 2024-07-21 05:42:29

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

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

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();

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

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 运算符的规则。

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

It's traditionally the left side of the "=" operator. However, with time, meaning of "lvalue"/"rvalue" changed. C++ added the term of a "non-modifiable lvalue" which is any lvalue that cannot assigned to: arrays and variables that are qualified with "const" are two examples. In C, you cannot assign to any rvalue (see below). Likewise, in C++, you cannot assign to rvalues that are not of some user defined class type.

You can say an "lvalue" is an expression that names an object which persists over time and occupies some location of storage. Whether or not you can assign to that expression is not important for that classification. A reference, in particular, is also an lvalue, because it has a name that persists over time. All the following are lvalues, because they all refer to named objects. Also note that a const does not have any effect on the lvalue-ness.

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

The term "rvalue" is used for things like literals and enumerator values and for temporaries that do not enjoy the fun of having a long life and are destroyed right away at the end of a full expression. For rvalues, not the aspect of persistence is important, but the value-aspect. Functions in C++ are lvalues, because they are persistent and they have an address, even though they are not objects. I've left them out in the above overview of lvalues, because it's easier to grasp lvalues when first only taking objects into account. All the following are rvalues:

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();

Incidentally, often you have an lvalue, but an operator needs an rvalue. For example the binary builtin "+" operator adds two values. An lvalue expression first and for all specifies a location where a value first has to be read out. So when you add two variables, an "lvalue to rvalue" conversion takes place. The Standard says that the value contained in an lvalue expression is its rvalue result:

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

Other operators do not take rvalue, but lvalues. They don't read a value. An example is the address-of operator, &. You cannot take the address of an rvalue expressions. Some rvalues are not even objects: They do not occupy any storage. Examples are again, literals (10, 3.3, ...) and enumerator values.

How is that scary stuff useful?

Well it has several advantages to have the distinction of lvalue and rvalue

  • Allowing the compiler to omit taking storage for rvalues and using registers/readonly memory for scalar values
  • Flagging expressions as elusive: rvalues will not live long
    • Allows efficient copy semantics for the compiler internally and in c++1x also exposed to the programmer (see move semantics and rvalue references): We can steal away resources from rvalues that are going to be destroyed anyway.
  • Allows to build rules upon that property
    • rvalues are not allowed to be generated from a yet uninitialized objects where an lvalues refers to. But lvalues may refer to uninitialized objects just fine
    • rvalues can never be polymorphic. Their static type must also be their dynamic type: Simplifies rules for the typeid operator.

... There is more to it, i feel it ...

机场等船 2024-07-21 05:42:29

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

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

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

then:

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

Something that appears on the left hand side of an assignment i.e. something that can be assigned to.

Note that in C++ a function call may be an lvalue if:

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

then:

func() = 42;     // is legal (and not uncommon)
筱武穆 2024-07-21 05:42:29

我所知道的最好的解释之一可以在 这篇关于 RValue 引用的文章

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

One of the best explanations I know of can be found in this article on RValue references.

another way to determine whether an expression is an lvalue is to ask "can I take its address?". If you can, it's an lvalue. If you can't, it's an rvalue. For example, &obj , &*ptr , &ptr[index] , and &++x are all valid (even though some of those expressions are silly), while &1729 , &(x + y) , &std::string("meow") , and &x++ are all invalid. Why does this work? The address-of operator requires that its "operand shall be an lvalue" (C++03 5.3.1/2). Why does it require that? Taking the address of a persistent object is fine, but taking the address of a temporary would be extremely dangerous, because temporaries evaporate quickly.

猫七 2024-07-21 05:42:29

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

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

The "L" in lvalue is usually described as standing for "location". An lvalue specifies the location of something; as another answerer pointed out, lvalues can typically have their address taken. This is why numeric literals and non-reference function return values are not lvalues.

The L used to stand for "left" until const was introduced into C++. Const lvalues cannot appear on the left hand side of an assignment.

你的背包 2024-07-21 05:42:29

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

左值是一个表达式(具有除 void 之外的对象类型),可能指定一个对象 [...]

就这么简单。

撰写此答案之日的维基百科定义几乎同样好

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


左值表达式的示例? 假设

int a, b[1], *c;

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

当计算实际上并不指定对象的左值表达式时,行为是未定义

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

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

如果它可以编译,那么它就是左值表达式。

基本上,您可以在 C 中应用 &(地址运算符)的所有内容都是左值函数除外,因为函数在 C 中不是对象。

那么 L 代表什么呢?

C11 n1570 脚注 64

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


请注意,将 E1 编译为 ISO C 中的左值不需要 E1 = E26.3.2.1p1 继续:

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

在 C 之前的语言(例如 B)中,所有定位器值都可能出现在赋值的左侧,因此命名。 在 C 中不再是这种情况,因为永远无法对数组进行赋值,并且 ISO C 添加了 const


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

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

For the C programming language C11 draft n1570 6.3.2.1p1 puts it succinctly:

An lvalue is an expression (with an object type other than void) that potentially designates an object [...]

It is as simple as that.

The Wikipedia definition on the day of writing this answer was almost as good

[...] a value that points to a storage location, potentially allowing new values to be assigned or value that provide accessible memory address of a variable where data can be written, it is referred as location value.


Examples of lvalue expressions? Given

int a, b[1], *c;

we can have the following lvalue expressions: a, b and c each of which definitely designate an object. b[0] designates an object as well, hence it is an lvalue expression. What about the potentiality then? *c is always an lvalue expression, but it does not designate an object if c does not hold a pointer to a valid object. b[1] could semantically designate an object but it doesn't because it accesses the array out of bounds. Both of them are lvalue expressions.

When an lvalue expression that does not actually designate an object is evaluated, the behaviour is undefined.

An lvalue expression can be more complex than that, for example:

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

if it compiles, it is an lvalue expression.

Basically everything that you can apply the & (address-of operator) to in C is an lvalue, except functions, since functions are not objects in C.

So what does the L then stand for?

C11 n1570 Footnote 64:

64) The name lvalue comes originally from the assignment expression E1 = E2, in which the left operand E1 is required to be a (modifiable) lvalue. It is perhaps better considered as representing an object locator value. [...]


Notice that E1 = E2 is not required to compile for E1 to be an lvalue in ISO C. 6.3.2.1p1 continues:

A modifiable lvalue is an lvalue that does not have array type, does not have an incomplete type, does not have a const-qualified type, and if it is a structure or union, does not have any member (including, recursively, any member or element of all contained aggregates or unions) with a const-qualified type.

It used to be case in languages before C (for example B) that all locator values could appear on the left-hand side of the assignment, hence the naming. This is no longer the case in C since arrays could never have been assigned to, and ISO C added const.


P.S. The same footnote explains the related term rvalue like this:

[...] What is sometimes called rvalue is in this International Standard described as the value of an expression. [...]

对岸观火 2024-07-21 05:42:29

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

3 = 42;

A simple example of what is definitly not an lvalue:

3 = 42;
非要怀念 2024-07-21 05:42:29

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

对象是一个存储区域
可以检查并存储到。 一个
左值是一个表达式,引用
这样一个物体。 左值不
必须允许修改
它指定的对象。 例如,一个
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 的引用
可修改的对象。

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

From this article. Since the OP was a bit lazy in asking his question (although some people disagree, see comments), I'll be lazy as well and simply paste the entire relevant part here, probably breaking some copyright laws.

An object is a region of storage that
can be examined and stored into. An
lvalue is an expression that refers to
such an object. An lvalue does not
necessarily permit modification of the
object it designates. For example, a
const object is an lvalue that cannot
be modified. The term modifiable
lvalue is used to emphasize that the
lvalue allows the designated object to
be changed as well as examined. The
following object types are lvalues,
but not modifiable lvalues:

  • An array type
  • An incomplete type
  • A const-qualified type
  • An object is a structure or union type and one of its members has
    a const-qualified type

Because these lvalues are not
modifiable, they cannot appear on the
left side of an assignment statement.

In C++, a function call that returns a
reference is an lvalue. Otherwise, a
function call is an rvalue expression.
In C++, every expression produces an
lvalue, an rvalue, or no value.

Certain operators require lvalues for
some of their operands. The table
below lists these operators and
additional constraints on their usage.

     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.

For example, all assignment operators
evaluate their right operand and
assign that value to their left
operand. The left operand must be a
modifiable lvalue or a reference to a
modifiable object.

The address operator (&) requires an
lvalue as an operand while the
increment (++) and the decrement (--)
operators require a modifiable lvalue
as an operand.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文