赋值语句中关于左值和右值的疑惑

发布于 2022-09-02 09:37:05 字数 384 浏览 14 评论 0

对于左值和右值还有一些模糊的地方:

书上说左值(L-value)是指地址值,右值(R-value)是指数据值。
但对于***右值指的是引用了一个存储在某个内存地址里的数据***这句话不是特别清楚,比如说:a = 5, 
                1、数值5是来自于内存中的某个特定存储区域吗,而且无论数值大小均是这么来的吗(记忆中好像不是的呢)?
                2、使用数值5的时候是以copy的方式还是其他的方式得到的?
                3、或者是在赋值之前临时创建的大小为int的空间来存储5,并非是从内存中的引用?
                4、关于这方面有什么相关的书籍推荐么?
                

先谢谢各位不吝赐教。

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

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

发布评论

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

评论(5

把时间冻结 2022-09-09 09:37:05

不太清楚题主问的是 C 还是 C++,如果是 C++ 的话,是哪一版标准的 C++。我这里只说 C++11 中关于左值和右值的问题。

粗略的来讲,当一个对象被用作右值(rvalue)的时候,使用的是这个对象的值(它的内容);当一个对象被用作左值(lvalue)的时候,使用的是这个对象的身份(它在内存中的位置)。

对于表达式a = 5;,假设aint类型。其中等号左边的表达式a的类型是左值,因为我们需要用到它的身份(它在内存中的位置),才能把值赋给它。等号右边的表达式5的类型是右值,因为我们需要用到它的值(它的内容)。

1、数值5是来自于内存中的某个特定存储区域吗,而且无论数值大小均是这么来的吗(记忆中好像不是的呢)?

数值5是 一个 literal,它的类型是右值(更准确的说是 prvalue,见下文),应该是保存在静态内存(static memory)中。无论大小,只要是 literal 都如此,例如 2333333333333333ULL

2、使用数值5的时候是以copy的方式还是其他的方式得到的?

在表达式a = 5;中,应该是内置赋值运算符将5从静态内存复制到变量a中。

对于自定义的赋值运算符,就要看:1. 是否定义了拷贝赋值运算符(copy-assignment operator)和移动赋值运算符(move-assignment operator),2. 等号右侧表达式的值类型,从而确定是拷贝还是移动。

3、或者是在赋值之前临时创建的大小为int的空间来存储5,并非是从内存中的引用?

不是。

4、关于这方面有什么相关的书籍推荐么?

不清楚有没有专门论述这方面的书籍,不过你可以看看本文末尾参考部分的文档和 C++11 标准。


以下为引申内容。

前提

C++ 中的表达式(expression,例如操作符和它的操作数、变量名等)是根据两个方面进行分类的:类型值类别。每个表达式都必定属于三个主要值类别(见下文)之一。

C++11 中表达式的值类别

在 C++11 之前,表达式的值类别分为左值(lvalue)和右值(rvalue)两种,而自 C++11 起,表达式的值类别分为 lvalue、xvalue、prvalue、glvalue、rvalue 五种。之所以引入了这么多种值类型,是因为 C++11 中新增的移动语义(move semantics)。

他们之间的关系如下图所示:

           expression
               |
        +------+-------+
        |              |
        v              v
     glvalue         rvalue        <-- 混合值类别(mixed value categories)
        |              |
  +-----+-----+  +-----+-----+
  |           |  |           |
  v           v  v           v
lvalue       xvalue       prvalue  <-- 主要值类别(primary value categories)

表达式的值类别分类的标准是两点:

  • 有无身份(has identity) 即是否能通过比较两个表达式的身份判断他们是否是同一个表达式。

  • 能否被移走(can be moved from) 即能把表达式的值移到其它变量中去,使原变量处于一种不确定但有效的状态(例如能正常析构)。

这5个值类别之间的关系如下表所示:

|   value    | has identity | can be moved from |
+============+==============+===================+
|   lvalue   |     yes      |        no         |
|   xvalue   |     yes      |        yes        |
|  prvalue   |     no       |        yes        |
| (not used) |     no       |        no         |
+------------+--------------+-------------------+
|  glvalue   |     yes      |        ?          | pre-C++11 lvalue
|   rvalue   |     ?        |        yes        | pre-C++11 rvalue

C++11 标准原文

From C++11 standard § 3.10

— An lvalue (so called, historically, because lvalues could appear on the left-hand side of an assignment expression) designates a function or an object. [ Example: If E is an expression of pointer type, then *E is an lvalue expression referring to the object or function to which E points. As another example, the result of calling a function whose return type is an lvalue reference is an lvalue. —end example ]
— An xvalue (an “eXpiring” value) also refers to an object, usually near the end of its lifetime (so that its resources may be moved, for example). An xvalue is the result of certain kinds of expressions involving rvalue references (8.3.2). [ Example: The result of calling a function whose return type is an rvalue reference is an xvalue. —end example ]
— A glvalue (“generalized” lvalue) is an lvalue or an xvalue.
— An rvalue (so called, historically, because rvalues could appear on the right-hand side of an assignment expression) is an xvalue, a temporary object (12.2) or subobject thereof, or a value that is not associated with an object.
— A prvalue (“pure” rvalue) is an rvalue that is not an xvalue. [ Example: The result of calling a function whose return type is not a reference is a prvalue. The value of a literal such as 12, 7.3e5, or true is also a prvalue. —end example ]

参考

萌梦深 2022-09-09 09:37:05

这种常量都在代码段里(.text section)。
推荐: 深入理解计算机系统

小帐篷 2022-09-09 09:37:05

C语言不是很精通,但是还是在此说一下自己的理解,如有不正确请指出。

  1. 个人觉得,“右值”不应该仅仅是指内存地址中的数据。比如a = 5, 5就是一个直接数而非内存地址,正如汇编中可以直接往寄存器中写直接数,也可以写内存地址中的数一样。因为对这种直接数取地址的话是没有意义的。

  2. 我只知道java中的String在直接赋值为字符串时,该字符串是共享常量池中的字符串的,但是C语言中不知道有没有类似的机制。对于a = 5这种赋值来说应该和直接的立即数写入差不多;而对于int a;int b = 5; a = b;这种赋值来说应该是拷贝无疑。这时b就是“引用了某个内存地址里的数据”。

  3. 个人觉得对于这种简单赋值来说临时创建空间存储然后拷贝过去在实现上看应该没有必要(没有求证过)。

  4. 平常不用C,而且本人学习不深,无法给出有价值的推荐。

以上都是个人见解,没有求证过,供题主参考。如有错误还请指出。

软糖 2022-09-09 09:37:05

不晓得你看的什么书,我不大明白这个地址值和数据值的定义

一个左值可能是一个函数或者对象。
一个右值可能是一个对象、临时对象或者临时对象的子对象、或者不是对象的值
具体可以参阅标准3.10小节,讲的就是左值与右值

至于5所在的内存位置,对于这种整数来说,对于编译器来讲一般都是使用立即数直接赋值,5并没有临时存在于什么地方,
就像是把5放在a所在的栈上的位置,那么这句话就是 a = 5;
如果是字符串,有可能存储在其他位置比如代码段,然后拷贝过来,这样就是 str = "123143";

独享拥抱 2022-09-09 09:37:05

其他的我不是很懂
但是:
左值和右值都是针对表达式而言的,左值是指表达式结束后依然存在的持久对象,右值是指表达式结束时就不再存在的临时对象。
一个区分左值与右值的便捷方法是:看能不能对表达式取地址,如果能,则为左值,否则为右值

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