【问题标题】:Does && force evaluation of relational, arithmetic operators on left of it before evaluation of higher ranking operators on right (C99)? [duplicate]&& 是否在评估右侧较高级别的运算符之前强制评估左侧的关系算术运算符(C99)? [复制]
【发布时间】:2021-05-10 18:44:35
【问题描述】:

考虑以下代码:

int y, x; y = 2; x = 0;

x!=0&&y/x>5

我的讲师关于 C 的教科书为“算术、关系和逻辑运算符的优先级层次结构”提供了一个表格,使用该表格来评估上述表达式中的运算符我得到以下优先级:

(1) 是 /,因此,先做 y/x

(2) 是 >,(3) 是 !=,(4) 是 &&。

如果我使用此优先级层次结构评估上述代码,则要评估的第一个子表达式是: y / x 这是 2 / 0,或未定义...

给出的答案是 0。

后来,教科书还指出,根据 ANSI C 标准,&& 左边的操作数总是首先被计算,只有当它是 0 时,右边的操作数才会被计算。

这是否意味着每当一个 logical and (&&) (或一个 logical or (||) 就此事而言) 出现在一个表达式以及算术和关系运算符,实际上我需要将整个表达式的术语分成两组 - && 的左侧术语和右侧术语,然后才开始应用 "算术、关系和逻辑运算符的优先级层次结构” 到整个“左手操作数”中包含的运算符?

这将导致上述表达式被评估为:

(x!=0) && (y/x>5)

从左操作数 (x!=0) 开始,即 (0!=0),为假,因此为 0,因此不再进行评估。

--

如果是这种情况,如果 && 运算符包含在表达式中规定必须首先做什么,为什么它会出现在优先级层次结构的低位?

【问题讨论】:

  • Precedence 控制运算符和操作数的分组 - 它不控制表达式的计算顺序。

标签: c operator-precedence


【解决方案1】:

优先级和关联性仅说明如何构建表达式。他们没有说如何评估它。考虑x+y*z。如果没有优先级,我们可以将此表达式构造为(x+y)*z,如左侧所示,或x+(y*z),如右侧所示。

* + / \ / \ + z x * / \ / \ xyyz

Precedence 告诉我们使用后者。但我们仍然可以按任意顺序评估xyz。假设xyz实际上是有副作用的函数调用;也许他们会打印“x”、“y”和“z”。如果我们评估z并记住它的结果,然后评估y,然后将它们相乘,然后评估x,然后再相加,我们将得到与评估x,然后y相同的结果,然后z,然后相乘,再相加。

这两种求值顺序都使用相同的表达式结构,即后一种。所以给我们表达式结构的优先级并没有告诉我们如何评估表达式。 (几乎——结构确实迫使我们在加法中使用它之前得到乘法的结果。)

&&|| 运算符具有超越优先级的属性:它们有一个规则,即先计算左操作数,仅当左操作数不能确定结果时才计算右操作数。

深入挖掘,有两个计算与一些表达式相关联。每个表达式都有其主要作用:产生一个值,例如其操作数的和、乘积或逻辑与。一些表达式也有副作用,比如增加一个对象(x++)或写输出(putchar('c'))。副作用不必与主效应同时发生。一般来说,它可以在它所在的完整表达式中的任何时间完成,它可以在主效果之前、期间或之后。

&&|| 运算符的排序属性扩展到副作用:它们要求在计算右操作数的任何部分之前完成其左操作数的所有副作用。

【讨论】:

  • 谢谢大家。我用你的回答做了仔细、简洁的笔记。 ;-)
【解决方案2】:

运算符的优先级清楚地表明您的表达式x!=0&&y/x>5 将如何被解释。将其视为设置大括号。因此编译器会将您的表达式读取为(x!=0) && ((y/x)>5)。请注意,优先级最高的运算符首先获得大括号,优先级较低的运算符稍后获得大括号。

现在进行评估。评估是从外到内进行的。最外面的运算符是优先级最低的运算符,&&。它有两个操作数,左边一个,右边一个。现在标准说首先要评估左边的。如果是false,则&& 返回false(快捷方式)。如果是true,则需要计算正确的操作数。

这种有保证的评估顺序的好处是您现在可以安全地编写例如if (x!=0 && y/x>10)if (pointer!=NULL && pointer->data != 3)

【讨论】:

    【解决方案3】:

    在这种情况下,您会遇到一种称为“短路评估”的情况。将您的状况if (x != 0 && y / x > 5) 视为if (somethingIsTrue && somethingIsTrue)。为了实现这一点,它必须变为if (true && true)。在短路评估中,它看到 x !=0 为假并立即停止评估,因为无论后面发生什么,第一件事都是假的,所以它永远不会是真的。

    是的,就您的观点而言,您可以将其视为将表达式分成 && 和 || 之间的组。陈述。这些单独的表达式中的每一个都被评估为真或假,然后将其视为 && 和 || 之间的一堆真或假语句。陈述。所以,对于像if (blah1 && blah2 || blah3) 这样的东西,无论blah1、blah2 和blah3 是什么,它们都会被评估为真或假。然后你就会看到它是如何发挥作用的,例如 if (true && true || false) 或类似的东西。

    顺便说一句,不要为了记住优先规则而将头撞到墙上。它们中的大多数都很直观,随着您的编程更多,您将掌握正确的做事方式。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-12-15
      • 2014-11-29
      • 2018-05-23
      • 1970-01-01
      • 2019-01-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多