C 中的哪些运算符有错误的优先级?

Which operator(s) in C have wrong precedence?

在K&R C (2E)的"Introduction"部分有这样一段:

C, like any other language, has its blemishes. Some of the operators have the wrong precedence; ...

这些是哪些运算符?他们的优先级怎么错了?

this 是这些情况之一吗?

这取决于考虑哪个优先级约定"correct"。没有物理法则(或土地法则)要求以某种方式优先。它是随着时间的推移通过实践而发展起来的。

在数学中,运算符优先级通常取"BODMAS"(括号、阶、除、乘、加、减)。括号在前,减法在后。Ordering Mathematical Operations | BODMAS Order of operations

编程中的运算符优先级需要更多的规则,因为有更多的运算符,但您可以提炼出它与 BODMAS 的比较。

此处显示了 ANSI C 优先级方案:

如您所见,一元加法和减法在第 2 级 - 在第 3 级的乘法和除法之上。这可能会让粗浅阅读的数学家感到困惑,因为 suffix/postfix 递增和递减。

就此而言,始终值得考虑在您的数学代码中添加括号 - 即使在语法上不必要的地方 - 以确保人类 reader 您的意图是明确的。这样做你不会有任何损失(尽管你可能会被一个严格的代码审查员激怒,在这种情况下你可以对编码风险管理进行反击)。你可能会失去可读性,但在调试时意图总是更重要。

是的,您提供的 link 就是一个很好的例子。由此造成了无数代价高昂的生产错误。

是的,您 link 的消息中讨论的情况是 C 中运算符优先级的主要抱怨。

从历史上看,C 在没有 && 的情况下开发。为了执行逻辑与运算,人们会使用按位与,因此 a==b AND c==d 将用 a==b & c==d 表示。为了促进这一点,== 的优先级高于 &。尽管 && 后来被添加到该语言中,但 & 的优先级一直低于 ==

一般来说,人们可能更喜欢写 (x&y) == 1 之类的表达式,而不是 x & (y==1)。所以如果 & 的优先级高于 == 会更好。因此人们对C运算符优先级的这一方面不满意。

这通常适用于 &^|,其优先级低于 ==!=<><=>=

有一个明确的优先规则是无可争议的。 规则非常清楚,对于 强类型 系统(想想 Pascal),错误的优先级会在编译时给出明确的语法错误。 C 的问题在于,由于其类型系统是自由放任的,因此错误结果是更多的逻辑错误,导致错误,而不是在编译时可捕获的错误。

规则

令○□为两个类型为

的运算符

○ : α × α → β
□ : β × β → γ
并且 α 和 γ 是不同的类型。

然后

x ○ y □ z can only mean (x ○ y) □ z, with type assignment
x: α, y: α, z: β

而 x ○ (y □ z) 将是一个类型错误,因为 ○ 只能取一个 α 而正确的子表达式只能产生一个 γ 而不是 α

现在让

将此应用于 C

C 大部分都做对了

(==) : 数字 × 数字 → 布尔值
(&&) : 布尔值 × 布尔值 → 布尔值

所以 && 应该在 == 下面,确实如此

同样

(+) : 数×数→数
(==) : 数字 × 数字 → 布尔值

所以 (+) 必须在 (==) 以上,这又是正确的

但是对于按位运算符

&/|两个位模式又名数字产生一个数字 即
(&), (|) : 数×数→数
(==) : 数字 × 数字 → 布尔值

等等典型的掩码查询,例如。 x & 0x777 == 0x777
仅当 (&) 被视为算术运算符时才有意义,即上面的 (==)

C 把它放在下面根据上面的类型规则是错误的

当然我用math/type-inference

表达了上面的内容

在更实用的 C 术语中 x & 0x777 == 0x777 自然分组为 x & (0x777 == 0x777)(在没有显式括号的情况下)

这样的分组什么时候可以合法使用?
我(个人)不相信有任何

IOW Dennis Ritchie 关于这些优先顺序错误的非正式声明可以给出更正式的理由

错了可能听起来有点太刺耳了。普通人一般只关心像+-*/^这样的基本运算符,如果这些运算符不像他们在数学中写的那样,那可能会被称为错误。幸运的是,这些在 C 中是“有序的”(除了不存在的幂运算符)

但是,还有一些其他运算符可能无法像许多人预期的那样工作。例如,按位运算符 的优先级低于 比较运算符,Eric Postpischil 已经提到了这一点。这 不太方便 但仍然不是很“错误”,因为之前没有任何定义的标准。它们是在上个世纪计算机出现期间发明的

另一个例子是移位运算符 << >>,它的优先级低于+-。移位被认为是乘法和除法,所以人们可能期望它应该在比+-更高的层次上。写成x << a + b可能很多人会以为是x*2a + b,直到看到优先级table .此外 (x << 2) + (x << 4) + (y << 6) 也不如不带括号的简单加法方便。 Golang 是通过将 <</>> 置于比 +-

更高的优先级来解决此问题的语言之一

在其他语言中有很多“错误”优先级的真实例子