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

Why does c = ++(a+b) give compilation error?

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

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

这个理解对吗?这是我第一次尝试研究问题,所以如果有我应该寻找的东西请指教。

你是对的。 ++ 尝试将新值分配给原始变量。所以 ++a 将获取 a 的值,将 1 添加到它,然后将其分配回 a。因为,正如您所说,(a+b) 是一个临时值,而不是分配了内存地址的变量,因此无法执行分配。

这只是一条规则,仅此而已,并且可能 (1) 使编写 C 编译器更容易,并且 (2) 没有人说服 C 标准委员会放宽它。

非正式地说,如果 foo 可以出现在赋值表达式(如 foo = bar)的左侧,则只能写 ++foo。既然你不会写a + b = bar,你也不会写++(a + b)

a + b 无法生成 ++ 可以对其进行操作的临时值并没有真正的原因,其结果就是表达式 ++(a + b) 的值。

C11 标准在第 6.5.3.1 节中规定

The operand of the prefix increment or decrement operator shall have atomic, qualified, or unqualified real or pointer type, and shall be a modifiable lvalue

而"modifiable lvalue"在6.3.2.1小节1

中有描述

An lvalue is an expression (with an object type other than void) that potentially designates an object; if an lvalue does not designate an object when it is evaluated, the behavior is undefined. When an object is said to have a particular type, the type is specified by the lvalue used to designate the object. 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.

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

我认为你基本上回答了你自己的问题。 我可能会对您的措辞做一些小改动,并将 "temporary variable" 替换为 "rvalue",如 C.Gibbons 所述。

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

"rvalue" 一词在您刚开始时可能看起来不透明,所以我希望以下内容有助于培养对它的直觉。

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

稍微了解一下 C 如何使用内存(和寄存器)将有助于了解区别的重要性。在 broad brush strokes 中,编译器创建了一个机器语言指令列表,用于计算表达式的结果(右值),然后 put 结果某处(左值)。想象一个编译器处理以下代码片段:

x = y * 3

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

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

++ 运算符(及其对应的 --)需要 "somewhere" 来修改,基本上任何可以作为左值的东西。

了解 C 内存模型会有所帮助,因为您会在脑海中更好地了解如何将参数传递给函数以及(最终)如何使用动态内存分配,例如 malloc() 函数。出于类似的原因,您可能会在某个时候学习一些简单的汇编编程,以更好地了解编译器在做什么。此外,如果您使用 gcc-S 选项 "Stop after the stage of compilation proper; do not assemble." 可能会很有趣(尽管我建议您在代码片段)。

顺便说一句: ++指令has been around since 1969(虽然它开始于C的前身B):

(Ken Thompson's) observation (was) that the translation of ++x was smaller than that of x=x+1."

根据该维基百科参考,您将看到 Dennis Ritchie("K&R C" 中的 "R")关于 C 语言历史的有趣文章,为方便起见链接在这里:http://www.bell-labs.com/usr/dmr/www/chist.html 您可以在其中搜索“++”。

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

现在,有人可能会说 "OK, that's indeed the reason, but there is actually no *real* reason other than that",但不幸的是,运算符实际工作方式的特定措辞 确实 要求正是如此。

The expression ++E is equivalent to (E+=1).

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

现在,可以轻松地改写定义(我认为它甚至不是最初的 C,而是 B 的传家宝),但这样做将从根本上改变语言,使其不再与其以前的版本兼容。由于可能的好处很小,但可能的影响很大,所以这从未发生过,也可能永远不会发生。

如果除了 C 之外还考虑 C++(问题标记为 C,但有关于运算符重载的讨论),情况会变得更加复杂。在 C 中,很难想象会出现这种情况,但在 C++ 中,(a+b) 的结果很可能是根本无法递增的结果,或者递增可能会产生非常大的副作用(不仅仅是加 1 ).编译器必须能够应对这种情况,并在出现问题时对其进行诊断。在左值上,检查起来仍然有点微不足道。对于你扔给可怜的东西的括号内的任何一种随意的表达,情况并非如此。
这不是 无法 完成的 真正 原因,但它确实可以解释为什么实施此操作的人没有这样做添加这样一个对极少数人几乎没有好处的功能真是令人欣喜若狂。

当执行++(a+b)表达式时,例如:

int a, b;
a = 10;
b = 20;
/* NOTE :
 //step 1: expression need to solve first to perform ++ operation over operand
   ++ ( exp );
// in your case 
   ++ ( 10 + 20 );
// step 2: result of that inc by one 
   ++ ( 30 );
// here, you're applying ++ operator over constant value and it's invalid use of ++ operator 
*/
++(a+b);

(a+b) 求值为右值,不能递增。

++ 尝试将值赋予原始变量,并且由于 (a+b) 是临时值,因此无法执行该操作。它们基本上是 C 编程约定的规则,使编程变得容易。而已。