为什么c = ++(a + b)会出现编译错误?

dng*_*dng 111 c increment

在研究之后,我读到增量运算符要求操作数具有可修改的数据对象:https://en.wikipedia.org/wiki/Increment_and_decrement_operators.

由此我猜它会给出编译错误,因为它(a+b)是一个临时整数,所以不可修改.

这种理解是否正确?这是我第一次尝试研究问题所以如果有什么我应该寻找的请告知.

Bat*_*eba 117

这只是一个规则,就是这样,并且可能会(1)使编写C编译器变得更容易;(2)没有人说服C标准委员会放松它.

非正式地说,只有++foofoo可以出现在赋值表达式的左侧时才能编写foo = bar.既然你不能写a + b = bar,你也不能写++(a + b).

没有真正的理由为什么a + b不能产生一个++可以操作的临时性,其结果就是表达式的价值++(a + b).

  • 这是一个真正的原因恕我直言:如果`++'有时会产生修改某些东西的副作用,有时候却没有,那将是一个可怕的混乱. (29认同)
  • @Bathsheba这解释了为什么5 ++也会导致编译错误 (6认同)
  • @dng:的确如此; 这就是引入lvalue和rvalue这两个术语的原因,尽管事情比现在更复杂(特别是在C++中).例如,常量永远不能是左值:像5 = a这样的东西没有意义. (5认同)
  • 我认为第(1)点击中头部的钉子.只是看看C++中临时实现的规则可能会让人感到厌烦(但它很强大,但必须这样说). (4认同)
  • @StoryTeller:的确,与我们心爱的语言C++不同,C仍然可以相对简单地编译成汇编. (4认同)
  • @dng:绝对. (3认同)
  • @ o11c虽然这需要你说服c标准委员会需要引用和运算符重载. (2认同)
  • @Bathsheba这就是为什么建议在等式比较中将常量放在左边,例如`if(5 == a)`所以如果有一个拼写错误并且其中一个`=`被遗漏,程序将拒绝编译而不是静默设置`a`等于`5`而不是执行相等检查. (2认同)

Chr*_*ons 40

C11标准见6.5.3.1节

前缀增量或减量运算符的操作数应具有原子,限定或非限定的实数或指针类型,并且应为可修改的左值

并且"可修改的左值"在6.3.2.1小节第1节中描述

左值是一个表达式(对象类型不是void)可能指定一个对象; 如果左值在评估时未指定对象,则行为未定义.当一个对象被称为具有特定类型时,该类型由用于指定该对象的左值指定. 可修改的左值是一个左值,它没有数组类型,没有不完整的类型,没有const限定类型,如果是结构或联合,则没有任何成员(包括,递归地,任何成员)或具有const限定类型的所有包含聚合或联合的元素.

因此(a+b),不是可修改的左值,因此不符合前缀增量运算符的条件.


Roe*_*rel 21

你是对的.在++尝试将新值赋予给原来的变量.所以++a将取值a,添加1,然后再分配给它a.因为,正如你所说,(a + b)是临时值,而不是具有分配的内存地址的变量,因此无法执行赋值.


jgr*_*eve 12

我想你大多回答了你自己的问题.我可能会对你的措辞进行一些小改动,并将"临时变量"替换为"rvalue",如C.Gibbons所述.

当您了解C的内存模型时,术语变量,参数,临时变量等将变得更加清晰(这看起来像一个很好的概述:https://www.geeksforgeeks.org/memory-layout-of-c-program/).

当你刚刚开始时,术语"右值"可能看起来不透明,所以我希望以下内容有助于培养对它的直觉.

Lvalue/rvalue正在讨论等号(赋值运算符)的不同边:lvalue =左手边(小写L,而不是"one")rvalue =右手边

学习一下C如何使用内存(和寄存器)将有助于了解区分为何如此重要.在广泛的笔触,编译器创建的该计算表达式(右值)的结果的机器语言指令的列表,然后该结果某处(左值).想象一下处理以下代码片段的编译器:

x = y * 3
Run Code Online (Sandbox Code Playgroud)

在汇编伪代码中,它可能看起来像这个玩具示例:

load register A with the value at memory address y
load register B with a value of 3
multiply register A and B, saving the result in A
write register A to memory address x
Run Code Online (Sandbox Code Playgroud)

++运算符(及其对应的)需要一个"某处"来修改,基本上任何可以作为左值的东西.

理解C内存模型将会很有帮助,因为您可以更好地了解参数如何传递给函数以及(最终)如何使用动态内存分配,如malloc()函数.出于类似的原因,您可能会在某些时候研究一些简单的汇编编程,以便更好地了解编译器正在做什么.此外,如果您正在使用gcc,则-S选项"在编译阶段之后停止;不要组合".可能很有趣(虽然我建议在一个代码片段上尝试).

暂时不说:++指令自1969年以来一直存在(尽管它始于C的前身B):

(Ken Thompon)观察到的是,++ x的翻译小于x = x + 1的翻译."

翻译类似于这里的编译,但他们谈的是一个不寻常的硬件,PDP-7.接下来,维基百科的参考文献将带您阅读Dennis Ritchie("K&R C"中的"R")关于C语言历史的有趣文章,为方便起见,链接在这里:http://www.bell-labs.com/ usr/dmr/www/chist.html您可以在其中搜索"++".


Dam*_*mon 6

原因是标准要求操作数是左值.表达式(a+b)不是左值,因此不允许应用增量运算符.

现在,有人可能会说"好吧,这确实是原因,但实际上除此之外没有真正的*理由",但不幸的是,运营商如何运作的具体措辞确实需要这样.

表达式++ E等价于(E + = 1).

显然,E += 1如果E不是左值,你就不能写.这是一种耻辱,因为人们也可以说:"将E递增1"并完成.在这种情况下,在非左值上应用运算符(原则上)是完全可能的,但代价是使编译器稍微复杂一些.

现在,这个定义可以简单地改写(我认为它原本不是C而是B的传家宝),但这样做会从根本上将语言改为不再与之前版本兼容的东西.由于可能的好处相当小,但可能的影响是巨大的,从未发生过,可能永远不会发生.

如果你考虑C++以及C(问题标记为C,但是有关于运算符重载的讨论),那么故事就变得更加复杂了.在C中,很难想象会出现这种情况,但在C++中,结果(a+b)很可能是根本不能增加的结果,或者增量可能会产生非常大的副作用(不仅仅是添加1).编译器必须能够处理它,并在出现问题时对其进行诊断.在左值,这仍然是一件微不足道的检查.对于你在可怜的事情中抛出的括号内的任何一种随意表达都不是这样.
这不是为什么不能做到这一点的真正原因,但它确实可以解释为什么实施这一目标的人并不是精力充沛地添加这样的功能,这对于极少数人来说几乎没有什么好处.