4

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

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

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

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

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

4

3 回答 3

8

左值?正确的价值?

如果您在谈论lvaluesrvalues,那么作为 lvalue 或 rvalue 的属性适用于表达式的结果,这意味着您必须考虑--iand的结果i--。在 C 语言中, --ii--都是右值。因此,您的问题是基于 C 语言领域的不正确前提。--i不是C 中的左值。我知道您要通过引用 C99 标准来说明什么,因为它明确指出两者都不是左值。此外,还不清楚i--返回 a是什么意思void。不,内置的后缀--永远不会返回void

--i左值与右值的区别i--仅存在于 C++ 中。

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

如果您想查看--ii--表达式之间的区别,您必须使用它们的结果。例如

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--. 该临时值只是一个值,而不是一个对象。它不需要驻留在内存中,这就是它不能是左值的原因。

于 2012-12-21T03:17:03.760 回答
1

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

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

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

当你写类似的东西i-- = 5;时,编译器会抱怨,因为后减的语义本质上是减量有问题的东西,但返回它的旧值以供进一步使用。返回的东西将是临时的,因此为什么会i--产生一个 r 值。

于 2012-12-21T03:17:33.673 回答
0

术语“左值”和“右值”源自赋值表达式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并执行一些算术运算的结果。(这些表达式也会改变,但这是运算符的副作用,而不是它返回的结果的一部分。)左值和右值的“左”和“右”与or运算符是否在左侧i无关或名字的右边;它们与作业的左侧或右侧有关。正如其他答案所解释的,在 C++ 中,当它们位于左值的左侧时,它们返回一个左值。然而,这是巧合;C++ 中运算符的这种定义是在“左值”一词创建多年后出现的。--++

于 2012-12-21T04:39:21.653 回答