有人在汇编语言级别解释左值和右值吗?

Anyone explain left value and right value in Assembly language level?

本文关键字:解释 汇编语言      更新时间:2023-10-16

我想这里的每个人都知道--i是左值表达式,而i--是右值表达式。但是我阅读了两个表达式的汇编代码,发现它们被编译为相同的汇编代码:

mov eax,dword ptr [i]
sub eax,1
mov dword ptr [i],eax

在 C99 语言标准中,左值定义为具有对象类型或 void 以外的不完整类型的表达式。

所以我可以确保--i返回一个不是 void 类型的值,而i--返回一个 void 或临时变量的值。

但是当我给出诸如i--=5之类的赋值时,编译器会给我一个错误,指示i--不是左值,我不知道为什么不是以及为什么返回值是临时变量。编译器如何做出这样的判断?谁能用汇编语言给我一些解释?谢谢!

左值?正确的价值?

如果您谈论的是左值和右值,那么左值或右的属性适用于表达式的结果,这意味着您必须考虑--ii--的结果。在C语言中,--ii--都是右值。所以,你的问题是基于C语言领域的错误前提。--i不是 C中的左值。我不知道您通过参考 C99 标准想表达什么观点,因为它清楚地表明两者都不是左值。另外,不清楚您所说的i--返回void是什么意思。不,内置后缀--永远不会返回void

--ii--的情况下,左值与右值的区别只存在于C++中。

无论如何,如果您只查看--i;i--;表达式语句,则不会使用这些表达式的结果。你正在丢弃它们。使用独立--ii--的唯一一点是它们的副作用(i的减少)。但由于它们的副作用是相同的,因此完全可以预期生成的代码是相同的。

如果要查看--i表达式和i--表达式之间的差异,则必须使用它们的结果。例如

int a = --i;
int b = i--;

将为每个初始化生成不同的代码。

不过,这个例子与他们结果的净值或右值无关。如果你想观察那边的差异(正如我上面所说,它只存在于C++中),你可以试试这个

int *a = &--i;
int *b = &i--;

第一次初始化将以 C++ 格式编译(因为结果是左值),而第二次初始化不会编译(因为结果是右值,并且您无法将内置的一元&应用于右值)。

此规范背后的基本原理相当明显。由于--i计算值为新的i,因此完全有可能使该运算符返回对i自身的引用作为其结果(并且C++语言,而不是C,更喜欢尽可能返回左值)。同时,i--需要返回i的旧值。由于当我们分析结果时哦i--i本身可能保存值,因此我们无法返回对i的引用。我们必须在某个辅助临时位置保存(或重新创建)i的旧值,并将其作为i--的结果返回。该临时值只是一个值,而不是一个对象。它不需要驻留在内存中,这就是为什么它不能是左值的原因。

[注意:我是从C++的角度回答这个问题的。

假设i是内置类型,如果您只是编写--i;i--;而不是j = ++i;j = i++;,那么编译器将它们编译为汇编代码也就不足为奇了 - 它们正在做同样的事情,即减少i。只有当您对结果执行某些操作时,差异才会在程序集级别变得明显,否则它们实际上具有相同的语义。

(请注意,如果我们考虑用户定义类型的重载前递减和减后运算符,则生成的代码将不同。

当你写类似i-- = 5;的东西时,编译器非常正确地抱怨,因为后递减的语义本质上是递减所讨论的东西,但返回它的旧值以供进一步使用。返回的东西将是临时的,因此i--产生 r 值的原因。

术语"lvalue"和"rvalue"源自赋值表达式E1 = E2,其中左操作数E1用于标识要修改的对象,右操作数E2标识要使用的值。(见C 1999 6.3.2.1,注53。

因此,仍然具有一些与之关联的对象的表达式可用于定位该对象并写入该对象。这是一个左值。如果表达式不是左值,则可能称为右值。

例如,如果你有i,某个对象的名称,它是一个左值,因为我们可以找到i在哪里,我们可以分配给它,就像i = 3一样。

另一方面,如果我们有表达式i+1,那么我们取了 i 的值并加了 1,我们现在有一个值,但它不与特定对象相关联。此新值不在i中。它只是一个临时值,没有特定位置。(可以肯定的是,编译器必须将其放在某个地方,除非优化完全删除表达式。但它可能在寄存器中,永远不会在内存中。即使由于某种原因它在内存中,C 语言也不会为您提供找出位置的方法。所以i+1不是左值,因为你不能在作业的左侧使用它。

--ii++都是通过获取i值并执行一些算术而产生的表达式。(这些表达式也会更改i,但这是运算符的副作用,而不是它返回的结果的一部分。左值和右值的"左"和"右"与--++运算符是在名称的左侧还是右侧无关;它们与作业的左侧或右侧有关。正如其他答案所解释的那样,在C++中,当它们位于左值的左侧时,它们会返回左值。然而,这是巧合;C++中运算符的这个定义是在"左值"一词创建多年后出现的。