Postfix / Prefix运算符的优先级和关联性

时间:2019-06-24 15:46:08

标签: c operators

我对后缀/前缀运算符的优先关联性感到困惑。

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

  

(* ip)++

     

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

更不用说后缀/前缀运算符之间的关联性差异了。两者均一视同仁。该书还指出,*和++具有相同的优先级。

另一方面,this page指出:

  

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

     

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

我应该信任哪个?这些年来,随着C版本的改变,情况有所改变吗?

3 个答案:

答案 0 :(得分:3)

TL; DR :这两个描述使用相同的单词和符号(意思略有不同)来说同一件事。

  

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

     
(*ip)++
         

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

  
     

更不用说两者之间的关联性差异了   后缀/前缀运算符。两者均一视同仁。这本书也   指出*和++具有相同的优先级。

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

  

另一方面,此页面指出:

     
    

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

         

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

  

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

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

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

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

对于从左到右的比较[em] 从右到左的联想,问题再次是,讨论仅包含前缀或仅包含后缀运算符的优先级。但是,将后缀运算符描述为从左到右关联,将前缀运算符描述为从右到左关联,这与其操作的语义顺序是一致的。

答案 1 :(得分:2)

您可以参考the C11 standard,尽管它关于优先级的部分很难理解。参见秒。 6.5.1。 (脚注85说”“语法指定了运算符在表达式计算中的优先级,这是相同的 作为该子节的主要子节的顺序,最高优先级在前。”

基本上,后缀运算符的优先级高于前缀,因为后缀运算符在6.5.2.4和6.5.3.1节中排在前面。因此,K&R是正确的(在这里就不足为奇了!)json.dumps()的意思是*ip++,与*(ip++)有所不同,但是它的含义是由于关联性,这有点误导我会说。而且geeksforgeeks网站的第2点也是正确的。

答案 2 :(得分: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++将增加指针。