C语言 一元算子

zxlwwiss  于 2023-10-16  发布在  其他
关注(0)|答案(4)|浏览(89)

我试图理解C语言中一元运算符背后的语法。根据这个版本的标准link,在第6.5.3节中,一元运算符具有以下语法:

unary-expression:
  ...
  ++ unary-expression
  ...

这意味着从语法的Angular 来看,这样的东西是法律的:a = ++++b。但是,gcc编译器提供了以下错误:lvalue required as increment operand
我不明白为什么?
根据标准,++b相当于(b += 1)。这意味着a=++++b应该扩展为a=((b+=1)+=1)。为什么编译器给予上述错误?

lg40wkob

lg40wkob1#

为了充分解释这一点,我们需要深入到语言律师的领域。我会一步一步地解释。
当预处理器解析表达式++++b时,它会寻找最长的字符序列,以形成一个有效的运算符,即所谓的“最大咀嚼规则”。在这种情况下,这意味着代码将被视为我们已经编写了++ ++b(而不是++ + +b或类似的代码)。
正如你从形式语法中注意到的,前缀++和--可以与其他一元表达式组合,所以我们得到“从右到左的结合性”,意思是(++(++b)。然而,C语法只指定了 * syntag-wise *,像这样合并组合多个一元运算符是可以的,但它是否有效取决于各个运算符。
从那里我们将结束思考正式C称为“左值”的东西,本质上是一个可修改的内存位置,而不是表达式的临时结果。例如,在两个变量的情况a + b中,ab分别是对象和左值,但加法表达式的结果不是。
让我们先看一个有效的表达式。如果你写了++*ptr,那么这将是有效的C,因为在这种情况下,两个一元运算符指定了以下内容:
C17 6.5.3.1,重点矿山

约束条件
前缀递增或递减运算符的操作数必须是原子、限定或非限定的真实的或指针类型,必须是可修改的左值

C17 6.5.3.2,重点矿山

一元*运算符表示间接。如果操作数指向函数,则结果是函数指示符;如果它指向一个对象,则结果是一个左值,指定该对象。

所以前缀++需要一个可修改的左值,而前缀*提供了这个值,所以++*ptr是可以的。
然而,在++++b的情况下,第一个++b的结果是 * 不是 * 左值。因此,在第一个++上添加另一个++,我们违反了上面引用的约束,这意味着代码是无效的C。
那么为什么++b的结果不是左值呢?6.5.3.1说:
前缀++运算符的操作数的值递增。结果是递增后操作数的新值。表达式++E等价于(E+=1)
好了,我们必须去挖掘+=(复合)赋值运算符的规则。
我们在C17 6.5.16中找到了相关的解释,重点是:
赋值运算符将值存储在左操作数指定的对象中。赋值表达式在赋值后具有左操作数的值,但不是左值。
因此,由于约束冲突,编译器必须提供诊断消息。我们没有违反语法规则,但我们违反了约束。如果您希望将代码称为严格遵循的C程序,那么语法和约束都是“不允许例外”的。

ylamdve6

ylamdve62#

在C文法中,++b不是左值,因此不能作为++运算符的操作数。

  • lvalue* 的概念在C标准中定义为:
    6.3.2.1左值、数组和函数指示符

lvalue 是一个表达式(对象类型不是void),它可能指定一个对象;如果一个 lvalue 在计算时没有指定一个对象,则行为是未定义的。当一个对象被称为具有特定类型时,该类型由用于指定该对象的 lvalue 指定。
虽然没有C表达式是 * 左值 * 的列表,但是C标准在很多地方提到了一些表达式是左值,而另一些不是。没有提到前缀运算符的应用,但是赋值b += 1显式地不是左值per:

6.5.16赋值运算符

[...]赋值运算符将值存储在左操作数指定的对象中。赋值表达式在赋值后具有左操作数的值,但不是 * 左值 *。
请注意,C++语法在这方面可能有所不同。

o7jaxewo

o7jaxewo3#

++b子表达式产生一个没有可寻址存储位置的临时值。尝试在该表达式上应用++失败,因为该运算符需要可修改的位置。像++5这样的表达式将失败,并显示相同的错误消息。
你必须递归地思考它,然后它变得明显:

(++(++b))

++b的值只能用作不需要修改存储位置的操作符的操作数,比如+

a + (++b)
57hvy0tb

57hvy0tb4#

int a=((b+=1)+=1);

也给出了错误:

lvalue required as left operand of assignment

(措辞几乎相同)

相关问题