为了全面解释这一点,它把我们带到了相当深的语言律师领域.我会试着一步一步地解释.
当预处理器解析表达式++++b
时,它寻找将形成有效运算符的最长字符序列,即所谓的"最大咀嚼规则".在本例中,这意味着代码将被视为我们已经编写了++ ++b
(而不是++ + +b
或更多).
正如您从形式语法中注意到的,前缀++和--可以与其他一元表达式组合,因此我们得到了"从右到左的结合性",意思是(++(++b)
.然而,C语法只规定syntax-wise像这样组合多个一元操作符是可以的,但它是否是有效代码取决于单个操作符.
从那时起,我们将思考一些正式的C称为"左值"的东西,本质上是一个可修改的内存位置,而不是表达式的临时结果.例如,在有两个变量的情况下,a
和b
分别是对象和左值,但加法表达式的结果不是.
让我们首先来看一个有效的表达式.如果您编写了++*ptr
,那么这将是有效的C,因为本例中的两个一元运算符指定了以下内容:
C17 6.5.3.1,重点是地雷
Constraints
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.
C17 6.5.3.2,重点是地雷
The unary 100 operator denotes indirection.如果操作数指向一个函数,则结果是一个函数指示符;if it points to an object, the result is an lvalue指定该对象.
所以前缀++
期望一个可修改的左值,而前缀*
提供了这个,所以++*ptr
是好的.
然而,在++++b
的情况下,第一个++b
的结果是一个左值not.因此,通过在第一个上添加另一个++
,我们违反了上面引用的约束,这意味着代码是无效的C.
那么,为什么++b
的结果不是最重要的呢?6.5.3.1说的是:
前缀
++
运算符的操作数的值递增.结果是递增后的操作数的新值.表达式++E
相当于(E+=1)
.
Ok so we have to go dig up the rules of +=
(compound) assignment operators...
We find the relevant explanation in C 17 6.5.16, emphasis mine:
赋值运算符将值存储在由左操作数指定的对象中.赋值表达式的赋值后左操作数的值为but is not an lvalue.
因此,由于违反约束,编译器必须提供诊断消息.我们没有违反语法规则,但我们违反了约束.如果您希望将代码称为严格符合的C程序,则语法和约束都是一种"不允许异常".