任何人都可以举一个例子说明这个优先级很重要吗?

Can anyone show an example where this precedence matters?

我正在阅读优先级和关联性。在table,我观察到这两件事-

(i) 后缀递增(或递减)运算符的优先级大于前缀递增(或递减)运算符的优先级。

(ii) 后缀inc.(或dec.)运算符的结合律是从左到右,而前缀increment(或dec.)运算符的结合律是从右到左。

我不确定为什么需要它。任何人都可以通过显示代码(针对每种情况分别显示)来帮助我,这表明需要这两个事实吗?谢谢

我尝试考虑案例,但没有得到任何案例(因为我对编程还很陌生)。

这样的表达式需要这个
data = *pointer++;

你获取data中指针处的值,然后递增到下一个元素。如果后缀的优先级没有更高,您最终会得到一个递增的数据值。

并且前缀运算符的结合性是 right-to-left 因为你想要一个像

这样的表达式
data = **pointer_to_pointer;

从右到左计算,就好像您要写

data = *(*pointer_to_pointer);

(i) precedence of postfix operator is greater than precedence of prefix operator.

考虑代码:

int x[] = {1,2,3};
int *p = x;
int y;
y = ++p[0]; 

这将递增 x 的第一个元素并将其分配给 y。

与此相比,我们明确更改了优先级,因此前缀 ++ 获得更高的优先级:

y = (++p)[0];

这将不会递增x个元素,但会将p移动到x的第二个元素并赋值y有了它。

(ii) associativity of postfix operator is left-to-right but that of prefix operator is right-to-left.

这意味着: p->x->y 应该读作 (p->x)->y,而不是 p->(x->y),后者甚至没有任何意义。

后缀组的其他运算符也一样——RTL 关联性对它们没有意义: p[x][y][z]((p[x])[y])[z] 相同,但 p[x]([y]([z])) 无意义(且非法)。

从历史的角度来看,运算符的优先级受到B and BCPL programming languages的影响。在文章 The Development of the C Language 中,Dennis Ritchie 解释了他如何选择优先级。

An accident of syntax contributed to the perceived complexity of the language. The indirection operator, spelled * in C, is syntactically a unary prefix operator, just as in BCPL and B. This works well in simple expressions, but in more complex cases, parentheses are required to direct the parsing. For example, to distinguish indirection through the value returned by a function from calling a function designated by a pointer, one writes *fp() and (*pf)() respectively

没有明确的逻辑优先顺序--你需要记住它或者当你不确定优先顺序时使用括号。

运算符的优先级和结合性不在语言语法范围内。对于后缀和一元运算符,如下所示:

<em>postfix-expression</em>:
    <em>primary-expression</em>
    <em>postfix-expression</em> [ <em>expression</em> ]
    <em>postfix-expression</em> ( <em>argument-expression-list<sub>opt</sub></em> )
    <em>postfix-expression</em> . <em>identifier</em>
    <em>postfix-expression</em> -> <em>identifier</em>
    <em>postfix-expression</em> ++
    <em>postfix-expression</em> --
    ( <em>type-name</em> ) { <em>initializer-list</em> }
    ( <em>type-name</em> ) { <em>initializer-list</em> , }

<em>unary-expression</em>:
    <em>postfix-expression</em>
    ++ <em>unary-expression</em>
    -- <em>unary-expression</em>
    <em>unary-operator</em> <em>cast-expression</em>
    sizeof <em>unary-expression</em>
    sizeof ( <em>type-name</em> )
    _Alignof ( <em>type-name</em> )

<em>unary-operator</em>: one of
    & * + - ~ !

<em>cast-expression</em>:
    <em>unary-expression</em>
    ( <em>type-name</em> ) <em>cast-expression</em>

C 2011 Online Draft,附录A.2相结构语法

那么,这如何确定优先级和结合性,为什么它很重要?让我们从像 *p++ 这样的表达式开始——我们是取消引用 p++,还是递增 *p?这是两个截然不同的操作,所以语法的结构很重要。让我们追溯一下:

        *            p            ++
        |            |            |
        |         primary         |
        |        expression       |
        |            |            |
        |         postfix         |
        |        expression       |
        |            |            |
        |            +------+-----+
        |                   |
        |                postfix
        |               expression
        |                   |
        |                 unary
        |               expression
        |                   |
      unary                cast
     operator           expression
        |                   |
        +---------+---------+
                  |
                unary
              expression

英文:

  1. <em>unary-expression</em> 产生 <em>unary-operator cast-expression</em>
  2. <em>unary-operator</em> 产生 *
  3. <em>cast-expression</em> 产生 <em>unary-expression</em>
  4. <em>unary-expression</em> 产生 <em>postfix-expression</em>
  5. <em>postfix-expression</em> 产生 <em>postfix-expression</em> ++
  6. <em>postfix-expression</em> 产生 <em>primary-expression</em>
  7. <em>primary-expression</em> 产生 p

这意味着表达式 *p++ 被解析为 *(p++) - * 运算符将应用于 [=12] 的 结果 =].

对于 *p[i] 也是同样的事情 - 我们将取消引用 p[i] 处的指针,而不是下标 *p

对于涉及关联性的稍微复杂的示例,让我们看一下成员选择运算符 ->,如表达式 foo->bar->bletch->blurga 所示。 -> 成员选择运算符的语法是

<em>postfix-expression</em> -> <em>identifier</em>

这告诉我们 foo->bar->bletch 减少到 postfix-expressionblurga 减少到 identifier。因此,运算符的结合性是 right-to-left,表达式解析为 ((foo->bar)->blurga)->bletch,而不是 foo->(bar->(blurga->bletch)).

您正在查看的这些表格是语法设置方式的摘要。语法的设置方式使运算符和操作数分组有些直观。您期望像 ++foo.bar[i] 这样的表达式递增 foo.bar[i],您期望 *f() 取消引用从函数返回的指针值等。

在 C 中,与大多数语言一样,后缀运算符比前缀运算符绑定更紧密,前缀运算符比二元运算符绑定更紧密。 (第二部分有例外,但在 C 中没有。)这通常对应于对表达式含义的直觉。

例如,几乎每个人都会期待

-a[0]

表示 "the negative of element 0 of the array a",而不是 "element 0 of the array -a",特别是在像 C 这样的语言中,"the array -a" 是没有意义的。同样,

-a++

具有预期的含义 "the negative of the current value of a, which is subsequently incremented." 同样,递增 -a 是没有意义的,因为 -a 不是变量。

天真的直觉可能不适用于更晦涩的运算符,因此保持一致性很有用。虽然可以想象可能存在一个几乎总是需要用括号括起来的前缀运算符,因为它绑定得不够紧密,使该运算符成为一般规则的例外,我们可能会创造比它解决的更多的惊喜,而且很少语言走这条路。

因此 ++-- 的前缀和后缀使用具有此通用规则定义的语法。尽管如此,至少在 C 中,将两者应用于同一操作数 (++a--) 是错误的,因为前版本和 post-fix 版本返回的值都不是左值,而操作数必须是左值。从这个意义上说,比较前缀和后缀 ++-- 的优先级的特殊情况永远不会出现在正确的程序中。但是前缀和后缀运算符的其他组合可以,并且优先级别需要均匀应用。

从另一种意义上讲,优先级和结合性声明是多余的。谈论两个前缀运算符之间或两个后缀运算符之间的关联性在句法上是没有意义的。如果前缀和后缀运算符的优先级不同,那么谈论前缀和后缀运算符之间的结合性也是没有意义的。所以结合律是无关紧要的。

但是,您可以扩展结合性的概念,说所有一元运算符都具有相同的优先级,并且它们都在右侧结合。这实际上会产生一个正确的解析器,并用在 B(C 的前身)的定义中。但是对于大部分不习惯语法分析的人来说确实是太迷惑了[=​​23=]