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

时间:2018-06-20 14:59:29

标签: c increment

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

据此我猜想它会产生编译错误,因为(a+b)是一个临时整数,因此不可修改。

这种理解正确吗?这是我第一次尝试研究问题,因此,如果有什么我应该寻找的,请提出建议。

8 个答案:

答案 0 :(得分:117)

仅此而已,并且可能(1)使编写C编译器更容易,以及(2)没有人说服C标准委员会放松它。

非正式地说,如果++foo可以出现在诸如foo之类的赋值表达式的左侧,则只能写foo = bar。由于您不会写a + b = bar,因此也无法写++(a + b)

a + b不能产生++可以在其上进行操作的临时原因没有真正的原因,其结果就是表达式++(a + b)的值。

答案 1 :(得分:40)

C11标准在6.5.3.1节中规定

  

前缀递增或递减运算符的操作数应具有   原子,合格或不合格实数或指针类型,并且应为   可修改的左值

第6.3.2.1节第1小节介绍了“可修改的左值”

  

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

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

答案 2 :(得分:21)

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

答案 3 :(得分:12)

我认为您基本上回答了自己的问题。 我可能会对您的措辞进行一些小的更改,然后将C.Gibbons所提到的“临时变量”替换为“ rvalue”。

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

刚开始时,“ rvalue”一词似乎不透明,因此我希望以下内容有助于建立直觉。

左值/右值是指等号(赋值运算符)的不同方面: 左值=左手边(小写L,不是“ 1”) rvalue =右侧

稍微了解一下C如何使用内存(和寄存器)将有助于理解为什么区别很重要。在广泛的笔触中,编译器创建一列机器语言指令,这些指令计算表达式的结果(右值),然后在某处(左值)输入 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

++运算符(及其对应的运算符)需要一个“某处”进行修改,实质上是任何可以用作左值的东西。

了解C内存模型将有所帮助,因为您将对如何将参数传递给函数以及(最终)如何使用动态内存分配(例如malloc()函数)有一个更好的了解。出于类似的原因,您可能会在某些时候研究一些简单的汇编程序设计,以更好地了解编译器在做什么。另外,如果您使用的是 gcc ,则 -S 选项“在编译正常后停止;请勿汇编”。可能很有趣(尽管我建议在 small 代码片段上尝试一下)。

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

  

(肯·汤普森的观察)是++ x的翻译小于x = x + 1的翻译。”

在参考维基百科之后,您将获得Dennis Ritchie(“ K&R C”中的“ R”)有关C语言历史的有趣文章,为方便起见,请点击此处链接:http://www.bell-labs.com/usr/dmr/www/chist.html,您可以在其中进行搜索表示“ ++”。

答案 4 :(得分:6)

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

现在,也许有人会说“是的,这确实是原因,但是除此以外,实际上没有*真正*的原因” ,但不幸的是,操作员实际上是如何工作的特定措辞确实是这种情况。

  

表达式++ E等效于(E + = 1)。

很显然,如果E += 1不是左值,则不能写E。真可惜,因为人们可能还说过:“将E递增1” 并完成。在那种情况下,完全有可能在非左值上应用运算符,但要使编译器稍微复杂些。

现在,该定义可以改写为小词(我认为它甚至不是最初的C,而是B的传家宝),但是这样做将从根本上将语言更改为与以前的版本不再兼容的语言。由于可能的收益很小,但潜在的影响却是巨大的,因此从未发生过,而且可能永远不会发生。

如果除了C之外还考虑使用C ++(问题被标记为C,但是这里讨论了运算符重载),那么情况就变得更加复杂了。在C语言中,很难想象会发生这种情况,但是在C ++语言中,(a+b)的结果很可能是您根本无法增加的,或者增加可能会产生非常大的副作用(而不仅仅是加1 )。编译器必须能够解决这个问题,并在出现问题时进行诊断。在左值上,这仍然是微不足道的检查。对于扔在可怜的东西上的圆括号内的任何偶然表达而言,并非如此。
这不是不能完成的真实原因,但是它确实可以作为解释,为什么实施此操作的人并不十分乐意添加这样的内容该功能几乎不会给很少的人带来好处。

答案 5 :(得分:3)

(a + b)得出一个右值,不能递增。

答案 6 :(得分:3)

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

答案 7 :(得分:2)

执行++(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);
相关问题