【问题标题】:Postfix/Prefix operator precedence and associativity后缀/前缀运算符优先级和关联性
【发布时间】:2019-11-06 11:08:40
【问题描述】:

我对后缀/前缀运算符的 precedenceassociativity 感到困惑。

一方面,当我在阅读 K&R 的书时,它指出:

(*ip)++

在最后一个例子中括号是必需的;没有它们,表达式将增加 ip 而不是它指向的内容,因为像 * 和 ++ 这样的一元运算符从右到左关联。

没有提及后缀/前缀运算符之间的关联性差异。两者一视同仁。该书还指出 * 和 ++ 具有相同的优先级。

另一方面,this page 声明:

1) 前缀 ++ 和 * 的优先级相同。两者的关联性是从右到左。

2) 后缀 ++ 的优先级高于 * 和前缀 ++。后缀++的结合性是从左到右的。

我应该信任哪一个?多年来随着 C 版本的变化,它是否发生了变化?

【问题讨论】:

  • 找到(至少)第三个来源?
  • 就个人而言,我信任this one。当然,您总是不能信任其中的任何,而只是达到语言标准本身。
  • @ScottHunter 因为 K&R 的书应该是一本圣经,它仍然令人困惑。
  • K&R 书是一个很好的起点,但 ANSI/ISO 标准才是真正的圣经。
  • 我,我相信this reference(虽然我有偏见)。

标签: c operators


【解决方案1】:

@GaryO 的回答很到位! Postfix 具有更高的优先级,因为它们来得更早。

这里有一个小测试来说服自己。 我创建了两个整数数组和一个指向每个数组开头的指针,然后在两个指针上运行 (*p)++ 和 *p++。我打印了前后的指针和数组状态供参考。

#include <stdio.h>

#define PRINT_ARRS printf("a = {%d, %d, %d}\n", a[0], a[1], a[2]); \
printf("b = {%d, %d, %d}\n\n", b[0], b[1], b[2]); 

#define PRINT_PTRS printf("*p1 = a[%ld] = %d\n", p1 - a, *p1); \
printf("*p2 = b[%ld] = %d\n\n", p2 - b, *p2);

int main()
{
int a[3] = {1 , 1, 1}; 
int b[3] = {10,10, 10};

int *p1 = a;
int *p2 = b;


PRINT_ARRS
PRINT_PTRS

printf("(*p1)++: %d\n", (*p1)++);
printf("*p1++  : %d\n\n", *p2++);

PRINT_ARRS
PRINT_PTRS

}

用 gcc 编译并在我的机器上运行会产生:

a = {1, 1, 1}
b = {10, 10, 10}

*p1 = a[0] = 1
*p2 = b[0] = 10

(*p1)++: 1
*p2++  : 10

a = {2, 1, 1}
b = {10, 10, 10}

*p1 = a[0] = 2
*p2 = b[1] = 10

您可以看到(*p1)++ 增加了数组值,而*p2++ 增加了指针。

【讨论】:

  • 感谢@ChristianGibbons!
【解决方案2】:

TL;DR:这两个描述说的是同一件事,使用了相同的词和符号,但含义略有不同。

一方面,当我在阅读 K&R 的书时,它指出:

(*ip)++

在最后一个例子中括号是必需的;没有它们,表达式将增加 ip 而不是它所指向的, 因为像 * 和 ++ 这样的一元运算符从右到左关联。

没有提及两者之间的关联性差异 后缀/前缀运算符。两者一视同仁。该书还 声明 * 和 ++ 具有相同的优先级。

目前尚不清楚您正在阅读哪个版本的 K&R,但至少第一版确实将递增和递减运算符的前缀和后缀版本分别视为单个运算符,其效果取决于它们的操作数是在之前还是之后他们。

另一方面,此页面声明:

1) 前缀 ++ 和 * 的优先级相同。两者的关联性是 从右到左。

2) 后缀 ++ 的优先级高于 * 和前缀 ++。 后缀++的结合性是从左到右的。

语言标准和大多数现代处理方法将前缀和后缀版本描述为不同的运算符,通过它们相对于其操作数的位置来消除歧义。这个答案的其余部分解释了这是对同一事物的另一种描述。

请注意,当仅涉及一元运算符时,关联性问题仅出现在具有相同优先级的一个前缀和一个后缀运算符之间。在仅前缀或仅后缀操作的链中,它们的关联方式没有歧义。例如,给定- - x,您无法将其有意义地分组为(- -) x。唯一的选择是- (- x)

接下来,观察所有最高优先级运算符都是后缀一元运算符,并且在 K&R 中,所有第二优先级运算符都是前缀一元运算符,除了 ambi-fix ++--。然后,将从右到左的关联性应用于第二优先级运算符,仅消除涉及后缀 ++-- 和前缀一元运算符的表达式的歧义,并且这样做有利于后缀运算符。这相当于区分这些运算符的后缀和前缀版本并为后缀版本分配更高优先级的现代方法。

为了了解现代描述的其余部分,请考虑我已经提出的观察结果,即只有当前缀和后缀运算符链接时,一元运算符才会出现关联性问题,并且所有最高优先级运算符都是后缀一元运算符。将后缀 ++-- 区分为比其前缀版本更高优先级的独立运算符后,可以将它们放在其他后缀运算符和所有前缀运算符之间的自己的层中,但将相反,它们与所有其他后缀运算符位于同一层,不会改变任何表达式的解释方式,并且更简单。这就是它现在通常的表现方式,包括在您的第二个资源中。

至于从左到右的vs. 从右到左的关联性,问题是,对于仅包含前缀或仅包含后缀运算符的优先级层,问题再次存在争议。但是,将后缀运算符描述为从左到右关联,将前缀运算符描述为从右到左关联,这与它们的语义运算顺序是一致的。

【讨论】:

  • 非常感谢您的详尽回答。
【解决方案3】:

您可以参考the C11 standard,尽管它关于优先级的部分有点难以理解。见秒。 6.5.1. (脚注 85 说 “语法指定运算符在表达式求值中的优先级,这与 作为本小节主要小节的顺序,最高优先级在前。")

基本上,后缀运算符的优先级高于前缀,因为它们在该部分中较早出现,即 6.5.2.4 与 6.5.3.1。所以 K&R 是正确的(这并不奇怪!)*ip++ 的意思是 *(ip++),这与 (*ip)++ 不同,但是我想说的是,它的观点是由于关联性有点误导。 geeksforgeeks 网站的第 2 点也是正确的。

【讨论】:

  • K&R的结论确实是正确的,但我认为因果关系是错误的。它指出 因为像 * 和 ++ 这样的一元运算符从右到左关联。,但这不是重点。这是因为后缀 ++ 优先于 *。如果我错了,请纠正我。
  • 你至少错了一半,@fassn。如问题所述,K&R 将相同的优先级分配给一元 *++在他们的框架中,他们正确地将括号的要求归因于关联性,而不是优先级。我在自己的回答中更详细地讨论了这一点。
猜你喜欢
  • 1970-01-01
  • 2011-08-30
  • 2021-10-25
  • 2018-08-22
  • 2015-09-16
  • 2016-06-07
  • 2012-04-10
  • 1970-01-01
  • 2021-05-05
相关资源
最近更新 更多