在 C++ 中,为什么 true &&真实 ||假&&假==真?

发布于 2024-09-29 23:00:52 字数 289 浏览 5 评论 0原文

我想知道是否有人知道编译器解释以下代码的方式:

#include <iostream>
using namespace std;

int main() {
 cout << (true && true || false && false) << endl; // true
}

这是真的吗,因为 &&优先级高于||或者因为 ||是短路运算符(换句话说,短路运算符是否忽略所有后续表达式,或仅忽略下一个表达式)?

I'd like to know if someone knows the way a compiler would interpret the following code:

#include <iostream>
using namespace std;

int main() {
 cout << (true && true || false && false) << endl; // true
}

Is this true because && has a higher precedence than || or because || is a short-circuit operator (in other words, does a short circuit operator disregard all subsequent expressions, or just the next expression)?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

谁的年少不轻狂 2024-10-06 23:00:52

&& 的优先级高于 ||

&& has a higher precedence than ||.

请你别敷衍 2024-10-06 23:00:52

Caladain 给出了正确的答案,但我想回应你对他的回答的评论之一:

如果短路||运算符发生并短路第二个 && 的执行表达式,这意味着 ||运算符在第二个 && 之前执行操作员。这意味着 && 是从左到右执行的。和|| (不是 && 优先级)。

我认为您遇到的部分问题是优先级并不完全意味着您认为的含义。确实, && 的优先级高于 ||,这正是您所看到的行为的原因。考虑普通算术运算符的情况:假设我们有 a * b + c * (d + e)。优先级告诉我们如何插入括号:首先将 * 括起来,然后将 + 括起来。这给了我们(a * b) + (c * (d + e));就您而言,我们有 (1 && 1) || (infiniteLoop() &&infiniteLoop())。然后,想象表达式变成。为此,请将每个运算符转换为一个节点,并将其两个参数作为子节点:

Expression trees.

评估此树的时间很短-电路出现了。在算术树中,您可以想象一种广度优先的自下而上的执行方式:首先计算DE = d + e,然后AB = a * b > 和CDE = c * DE,最终结果为AB + CDE。但请注意,您同样可以先评估AB,然后评估DECDE,最后得出最终结果;你无法分辨出其中的区别。但是,由于 ||&& 是短路的,因此它们必须使用这种最左优先求值。因此,为了评估 ||,我们首先评估 1 && 1..由于这是正确的,|| 会短路并忽略其右侧分支 - 即使如果它已经评估了它,它也必须评估 >infiniteLoop() &&首先是infiniteLoop()。

如果有帮助,您可以将树中的每个节点视为一个函数调用,它会生成以下表示形式 plus(times(a,b), times(c,plus(d,e))) 在第一种情况下,和 or(and(1,1), and(infiniteLoop(),infiniteLoop()) 在第二种情况下。短路意味着你必须充分评估每个左-hand 函数参数为 orand;如果为 true(对于 or)或 false< /code> (对于 ),然后忽略右侧参数。

您的评论假定我们首先评估具有最高优先级的所有内容,然后评估具有次高优先级的所有内容,依此类推,这对应于树的广度优先的自下而上的执行,但是,优先级告诉我们如何构建树,但是在简单的算术情况下是无关的。 ,正是如何评估树的精确规范


编辑1:在您的其他评论中,您说过。

您的算术示例需要在最终加法之前计算两个乘法,这不是定义优先级的原因吗?

是的,这就是优先级的定义——只不过它并不完全正确。这在C中当然是正确的,但请考虑一下您如何在头脑中计算(非C!)表达式0 * 5 ^ 7,其中5 ^ 7 = 57^ 的优先级高于 *。根据你的广度优先自下而上的规则,我们需要评估 05 ^ 7 才能找到结果。但你不会费心去评估 5 ^ 7;你只需说“好吧,由于 0 * x = 0 对于所有 x,这必须是 0”,然后跳过整个右侧-手分支。换句话说,在计算最终的乘法之前,我还没有充分评估两边;我已经短路了类似地,由于 false && _ == falsetrue || _ == true 对于任何_,我们可能不需要触摸右侧;这就是操作员短路的含义。 C 不会短路乘法(尽管有语言可以做到这一点),但它确实短路 &&||

正如短路 0 * 5 ^ 7 不会改变通常的 PEMDAS 优先规则一样,短路逻辑运算符也不会改变 && 的事实> 的优先级高于 ||。这只是一条捷径。由于我们必须首先选择运算符的某一侧进行计算,因此 C 承诺首先计算逻辑运算符的左侧;一旦完成此操作,就有一种明显(且有用)的方法来避免评估右侧的某些值,并且 C 承诺这样做。

您的规则(从下到上评估广度优先)也是明确定义的,并且语言可以选择这样做。然而,它的缺点是不允许短路,这是一种有用的行为。但是,如果树中的每个表达式都是明确定义的(没有循环)并且是纯粹的(没有修改变量、打印等),那么您就无法分辨出区别。只有在这些奇怪的情况下,“和”和“或”的数学定义没有涵盖这些情况,短路才可见。

另外,请注意,短路通过优先考虑最左边的表达式来工作这一事实并不具有任何根本性。人们可以定义一种语言Ɔ,其中⅋⅋代表and\\代表||,但其中< code>0 ⅋⅋infiniteLoop() 和 1 \\infiniteLoop() 将循环,而 infiniteLoop() ⅋⅋ 0infiniteLoop() \\ 1 分别为 false 和 true。这正好对应于选择首先评估右侧而不是左侧,然后以相同的方式进行简化。

简而言之:优先级告诉我们的是如何构建解析树。评估解析树的唯一合理的顺序是那些表现得好像我们在明确定义的纯值上广度优先自下而上评估它(正如您想要的那样) >。对于未定义或不纯的值,必须选择某些线性顺序。1一旦选择了线性顺序,运算符一侧的某些值可能会唯一地确定该运算符的结果整个表达式 (eg, 0 * _ == _ * 0 == 0, false && _ == _ && false == falsetrue || _ == _ || true == true)。因此,您可能无需完成对线性顺序之后发生的任何事情的评估就可以逃脱; C 承诺通过从左到右的方式对逻辑运算符 &&|| 执行此操作,而不是对其他任何操作执行此操作。然而,由于优先顺序,我们确实知道true ||真&& falsetrue 而不是 false:因为

  true || true && false
→ true || (true && false)
→ true || false
→ true

不是

  true || true && false
↛ (true || true) && false
→ true && false
→ false

1: 实际上,我们理论上也可以计算运算符的两边并行,但现在这并不重要,而且对于 C 来说当然没有意义。这产生了更灵活的语义,但存在副作用问题(它们什么时候发生?)。

Caladain has exactly the right answer, but I wanted to respond to one of your comments on his answer:

If short-circuiting the || operator occurs and short-circuits the execution of the second && expression, that means the || operator was executed BEFORE the second && operator. This implies left-to-right execution for && and || (not && precedence).

I think part of the problem you're having is that precedence doesn't quite mean what you think it means. It is true that && has higher precedence than ||, and this exactly accounts for the behavior you're seeing. Consider the case with ordinary arithmetic operators: suppose we have a * b + c * (d + e). What precedence tells us is how to insert parentheses: first around *, then around +. This gives us (a * b) + (c * (d + e)); in your case, we have (1 && 1) || (infiniteLoop() && infiniteLoop()). Then, imagine the expressions becoming trees. To do this, transform each operator into a node with its two arguments as children:

Expression trees.

Evaluating this tree is where short-circuiting comes in. In the arithmetic tree, you can imagine a breadth-first bottom-up execution style: first evaluate DE = d + e, then AB = a * b and CDE = c * DE, and the final result is AB + CDE. But note that you could equally well have evaluated AB first, then DE, CDE, and the final result; you can't tell the difference. However, since || and && are short-circuiting, they have to use this leftmost-first evaluation. Thus, to evaluate the ||, we first evaluate 1 && 1. Since this is true, || short-circuits and ignores its right-hand branch—even though, if it had evaluated it, it would have had to evaluate the infiniteLoop() && infiniteLoop() first.

If it helps, you can think of each node in the tree as a function call, which produces the following representation plus(times(a,b), times(c,plus(d,e))) in the first case, and or(and(1,1), and(infiniteLoop(),infiniteLoop()) in the second case. Short-circuiting means that you have to fully evaluate each left-hand function argument to or or and; if it's true (for or) or false (for and), then ignore the right-hand argument.

Your comment presupposes that we evaluate everything with highest precedence first, then everything with next-highest precedence, and so on and so forth, which corresponds to a breadth-first bottom-up execution of the tree. Instead, what happens is that precedence tells us how to build the tree. The rules for execution of the tree are irrelevant in the simple arithmetic case; short-circuiting, however, is precisely an exact specification of how to evaluate the tree.


Edit 1: In one of your other comments, you said

Your arithmetic example necessitates the two multiplications to be evaluated before the final addition, is that not what defines precedence?

Yes, this is what defines precedence—except it's not quite true. It's certainly exactly true in C, but consider how you would evaluate the (non-C!) expression 0 * 5 ^ 7 in your head, where 5 ^ 7 = 57 and ^ has higher precedence than *. According to your breadth-first bottom-up rule, we need to evaluate 0 and 5 ^ 7 before we can find the result. But you wouldn't bother to evaluate 5 ^ 7; you'd just say "well, since 0 * x = 0 for all x, this must be 0", and skip the whole right-hand branch. In other words, I haven't evaluated both sides fully before evaluating the final multiplication; I've short-circuited. Similarly, since false && _ == false and true || _ == true for any _, we may not need to touch the right-hand side; this is what it means for an operator to be short-circuiting. C doesn't short-circuit multiplication (although a language could do this), but it does short-circuit && and ||.

Just as short-circuiting 0 * 5 ^ 7 doesn't change the usual PEMDAS precedence rules, short-circuiting the logical operators doesn't change the fact that && has higher precedence than ||. It's simply a shortcut. Since we have to choose some side of the operator to evaluate first, C promises to evaluate the left-hand side of the logical operators first; once it's done this, there's an obvious (and useful) way to avoid evaluating the right-hand side for certain values, and C promises to do this.

Your rule—evaluate the expression breadth-first bottom-up—is also well-defined, and a language could choose to do this. However, it has the disadvantage of not permitting short-circuiting, which is a useful behavior. But if every expression in your tree is well-defined (no loops) and pure (no modifying variables, printing, etc.), then you can't tell the difference. It's only in these strange cases, which the mathematical definitions of "and" and "or" don't cover, that short-circuiting is even visible.

Also, note that there's nothing fundamental about the fact that short-circuiting works by prioritizing the leftmost expression. One could define a language Ɔ, where ⅋⅋ represents and and \\ represents ||, but where 0 ⅋⅋ infiniteLoop() and 1 \\ infiniteLoop() would loop, and infiniteLoop() ⅋⅋ 0 and infiniteLoop() \\ 1 would be false and true, respectively. This just corresponds to choosing to evaluate the right-hand side first instead of the left-hand side, and then simplifying in the same way.

In a nutshell: what precedence tells us is how to build the parse tree. The only sensible orders for evaluating the parse tree are those that behave as if we evaluate it breadth-first bottom-up (as you want to do) on well-defined pure values. For undefined or impure values, some linear order must be chosen.1 Once a linear order is chosen, certain values for one side of an operator may uniquely determine the result of the whole expression (e.g., 0 * _ == _ * 0 == 0, false && _ == _ && false == false, or true || _ == _ || true == true). Because of this, you may be able to get away without completing the evaluation of whatever comes afterwards in the linear order; C promises to do this for the logical operators && and || by evaluating them in a left-to-right fashion, and not to do it for anything else. However, thanks to precedence, we do know that true || true && false is true and not false: because

  true || true && false
→ true || (true && false)
→ true || false
→ true

instead of

  true || true && false
↛ (true || true) && false
→ true && false
→ false

1: Actually, we could also theoretically evaluate both sides of an operator in parallel, but that's not important right now, and certainly wouldn't make sense for C. This gives rise to more flexible semantics, but one which has problems with side-effects (when do they happen?).

场罚期间 2024-10-06 23:00:52

(true && true || false && false) 用 && 进行评估具有较高的优先级。

TRUE && TRUE = True

FALSE && FALSE = False

True || False = True

更新:

1&&1||infiniteLoop()&&infiniteLoop()

为什么这在 C++ 中会产生 true ?

像以前一样,让我们​​把它分开。 &&优先级高于||和布尔语句在C++中短路。

1 && 1 = True.

当 bool 值转换为整数值时,则

false -> 0
true -> 1

表达式计算此 (true) && (true) 语句,它会短路 ||,从而防止无限循环运行。还有更多的编译器 Juju 正在运行,因此这是对情况的简单视图,对于本示例来说已经足够了。

在非短路环境中,该表达式将永远挂起,因为 OR 的两侧都将被“评估”,而右侧将挂起。

如果您对优先级感到困惑,这就是您原始帖子中的评估方式,如果 ||比 && 具有更高的优先级:

1st.) True || False = True
2nd.) True && 1st = True
3rd.) 2nd && false = false
Expression = False;

我不记得它是从右到左还是从左到右,但无论哪种方式结果都是相同的。在你的第二篇文章中,如果||具有更高的优先级:

1st.) 1||InfLoop();  Hang forever, but assuming it didn't
2nd.) 1 && 1st;
3rd.) 2nd && InfLoop(); Hang Forever

tl;dr: 它仍然是使 && 首先被求值的优先级,但编译器也会短路 OR。本质上,编译器将这样的操作顺序分组(简单视图,将干草叉放下:-P)

1st.) Is 1&&1 True?
2nd.) Evaluate if the Left side of the operation is true, 
      if so, skip the second test and return True,
      Otherwise return the value of the second test(this is the OR)
3rd.) Is Inf() && Inf() True? (this would hang forever since 
      you have an infinite loop)

更新#2:
“但是,此示例证明 && 不具有优先级,因为 || 在第二个 && 之前求值。这表明 && 和 || 具有相同的优先级,并且按从左到前的顺序求值正确的顺序。”

“如果 && 具有优先权,它将评估第一个 && (1),然后评估第二个 && (无限循环)并挂起程序。由于这种情况不会发生,因此 && 不会被评估在||之前。”

让我们详细介绍一下这些内容。

我们在这里谈论两件不同的事情。优先级决定了操作的顺序,短路是一种节省处理器周期的编译器/语言技巧。

让我们首先介绍一下优先级。优先级是“操作顺序”的简写,本质上,鉴于以下陈述:
1 + 2 * 3
应按什么顺序对操作进行分组以进行评估?

数学明确地将运算顺序定义为乘法的优先级高于加法。

1 + (2 * 3) = 1 + 2 * 3
2 * 3 is evaluated first, and then 1 is added to the result.
* has higher precedence than +, thus that operation is evaluated first.

现在,让我们过渡到布尔表达式: (&& = AND, || = OR)

true AND false OR true

C++ 给予 AND 的优先级高于 OR,因此

(true AND false) OR true
true AND false is evaluated first, and then 
      used as the left hand for the OR statement

,仅就优先级而言,(true && true || false && ; false) 将按以下顺序进行操作:

((true && true) || (false && false)) = (true && true || false && false)
1st Comparison.) true && true
2nd Comparison.) false && false
3rd Comparison.) Result of 1st comparison || Result of Second

到目前为止?现在让我们进入短路:
在 C++ 中,布尔语句就是所谓的“短路”。这意味着编译器将查看给定的语句并选择“最佳路径”进行评估。举个例子:

(true && true) || (false && false)
There is no need to evaluate the (false && false) if (true && true) 
equals true, since only one side of the OR statement needs to be true.
Thus, the compiler will Short Circuit the expression.  Here's the compiler's
Simplified logic:
1st.) Is (true && true) True?
2nd.) Evaluate if the Left side of the operation is true, 
      if so, skip the second test and return True,
      Otherwise return the value of the second test(this is the OR)
3rd.) Is (false && false) True? Return this value

正如您所看到的,如果 (true && true) 被评估为 TRUE,那么就不需要花费时钟周期来评估 (false && false) 是否为 true。

C++ 总是短路,但其他语言提供了所谓的“Eager”运算符的机制。

以编程语言 Ada 为例。在 Ada 中,“AND”和“OR”是“Eager”运算符……它们强制对所有内容进行评估。

在 Ada 中,(true AND true) OR (false AND false) 将在评估 OR 之前评估 (true AND true) 和 (false AND false)。 Ada 还使您能够使用 AND THEN 和 OR ELSE 进行短路,这将为您提供与 C++ 相同的行为。

我希望这能完全回答你的问题。如果没有,请告诉我:-)

更新 3:最后一次更新,如果您仍然遇到问题,我将继续通过电子邮件发送。

“如果发生短路 || 运算符并短路第二个 && 表达式的执行,则意味着 || 运算符在第二个 && 运算符之前执行。这意味着从左到右执行对于 && 和 || (不是 && 优先)。

让我们看一下这个例子:

(false && infLoop()) || (true && true) = true (Put a breakpoint in InfLoop and it won't get hit)
false && infLoop() || true && true = true  (Put a breakpoint in InfLoop and it won't get hit)
false || (false && true && infLoop()) || true = false (infLoop doesn't get hit)

如果你说的是真的,InfLoop 将在前两个中受到攻击。
您还会注意到第三个示例中也没有调用 InfLoop()。

现在,让我们看看:

(false || true && infLoop() || true);

Infloop 被调用!如果 OR 的优先级高于 &&,则编译器将评估:

(false || true) && (infLoop() || true) = true;
(false || true) =true
(infLoop() || true = true (infLoop isn't called)

但是 InfLoop 被调用!这就是为什么:

(false || true && infLoop() || true);
1st Comparison.) true && InfLoop() (InfLoop gets called)
2nd Comparison.) False || 1st Comp (will never get here)
3rd Comparison.) 2nd Comp || true; (will never get here)

Precendece 仅设置操作分组。在此,&&大于||。

true && false || true && true gets grouped as
(true && false) || (true && true);

编译器然后出现并确定应该以什么顺序执行评估,以便为它提供节省周期的最佳机会。

Consider: false && infLoop() || true && true
Precedence Grouping goes like this:
(false && infLoop()) || (true && true)
The compiler then looks at it, and decides it will order the execution in this order:
(true && true) THEN || THEN (false && InfLoop())

这是一个事实……我不知道还能如何证明这一点。优先级由语言语法规则决定。编译器的优化由每个编译器决定。有些编译器比其他编译器更好,但是所有人都可以根据自己认为合适的情况自由地重新排序分组比较,以便为其提供最快执行的“最佳”机会以最少的比较。

(true && true || false && false) is evaluated with && having higher precedence.

TRUE && TRUE = True

FALSE && FALSE = False

True || False = True

Update:

1&&1||infiniteLoop()&&infiniteLoop()

Why does this produce true in C++?

Like before, lets break it apart. && has higher precedence that || and boolean statements short circuit in C++.

1 && 1 = True.

When a bool value is converted to an integer value, then

false -> 0
true -> 1

The expression evaluates this (true) && (true) statement, which short circuits the ||, which prevents the infinite loops from running. There's a lot more compiler Juju going on, so this is a simplistic view of the situation which is adequate for this example.

In a NON-short circuited environment, That expression would hang forever because both sides of the OR would be "evaluated" and the right side would hang.

If you're confused about the precedence, this is how things would evaluate in your original post if || had higher precedence than &&:

1st.) True || False = True
2nd.) True && 1st = True
3rd.) 2nd && false = false
Expression = False;

I can't remember if it goes right to left, or left to right, but either way the result would be the same. In your second post, if || had higher precendence:

1st.) 1||InfLoop();  Hang forever, but assuming it didn't
2nd.) 1 && 1st;
3rd.) 2nd && InfLoop(); Hang Forever

tl;dr: It's still precedence that's making the &&'s be evaluated first, but the compiler short circuits the OR as well. In essence, the compiler groups the order of operations like this (SIMPLISTIC VIEW, put down the pitchforks :-P)

1st.) Is 1&&1 True?
2nd.) Evaluate if the Left side of the operation is true, 
      if so, skip the second test and return True,
      Otherwise return the value of the second test(this is the OR)
3rd.) Is Inf() && Inf() True? (this would hang forever since 
      you have an infinite loop)

Update #2:
"However, this example proves && DOES NOT have precedence, as the || is evaluated before the second &&. This shows that && and || have equal precedence and are evaluated in left-to-right order."

"If && had precedence it would evaluate the first && (1), then the second && (infinite loops) and hang the program. Since this does not happen, && is not evaluated before ||."

Let's cover these in detail.

We're talking about two distinct things here. Precedence, which determines the Order of Operations, and Short Circuiting, which is a compiler/language trick to save processor cycles.

Let's cover Precedence first. Precedence is short hand for "Order of Operations" In essence, given this statement:
1 + 2 * 3
in which order should the operations be grouped for evaluation?

Mathematics clearly defines the order of operations as giving multiplication higher precedence than addition.

1 + (2 * 3) = 1 + 2 * 3
2 * 3 is evaluated first, and then 1 is added to the result.
* has higher precedence than +, thus that operation is evaluated first.

Now, lets transition to boolean expressions: (&& = AND, || = OR)

true AND false OR true

C++ gives AND a higher precedence than OR, thus

(true AND false) OR true
true AND false is evaluated first, and then 
      used as the left hand for the OR statement

So, just on precedence, (true && true || false && false) will be operated on in this order:

((true && true) || (false && false)) = (true && true || false && false)
1st Comparison.) true && true
2nd Comparison.) false && false
3rd Comparison.) Result of 1st comparison || Result of Second

With me thus far? Now lets get into Short Circuiting:
In C++, Boolean statements are what's called "short circuited". This means that the compiler will look at a given statement a choose the "best path" for evaluation. Take this example:

(true && true) || (false && false)
There is no need to evaluate the (false && false) if (true && true) 
equals true, since only one side of the OR statement needs to be true.
Thus, the compiler will Short Circuit the expression.  Here's the compiler's
Simplified logic:
1st.) Is (true && true) True?
2nd.) Evaluate if the Left side of the operation is true, 
      if so, skip the second test and return True,
      Otherwise return the value of the second test(this is the OR)
3rd.) Is (false && false) True? Return this value

As you can see, if (true && true) is evaluated TRUE, then there isn't a need to spend the clock cycles evaluating if (false && false) is true.

C++ Always short Circuts, but other languages provide mechanisms for what are called "Eager" operators.

Take for instance the programming language Ada. In Ada, "AND" and "OR" are "Eager" Operators..they force everything to be evaluated.

In Ada (true AND true) OR (false AND false) would evaluate both (true AND true) and (false AND false) before evaluating the OR. Ada Also gives you the ability to short circuit with AND THEN and OR ELSE, which will give you the same behavior C++ does.

I hope that fully answers your question. If not, let me know :-)

Update 3: Last update, and then I'll continue on email if you're still having issues.

"If short-circuiting the || operator occurs and short-circuits the execution of the second && expression, that means the || operator was executed BEFORE the second && operator. This implies left-to-right execution for && and || (not && precedence)."

Let's look at then this example:

(false && infLoop()) || (true && true) = true (Put a breakpoint in InfLoop and it won't get hit)
false && infLoop() || true && true = true  (Put a breakpoint in InfLoop and it won't get hit)
false || (false && true && infLoop()) || true = false (infLoop doesn't get hit)

If what you were saying was true, InfLoop would get hit in the first two.
You'll also notice InfLoop() doesn't get called in the third example either.

Now, lets look at this:

(false || true && infLoop() || true);

Infloop gets called! If OR had higher precendence than &&, then the compiler would evaluate:

(false || true) && (infLoop() || true) = true;
(false || true) =true
(infLoop() || true = true (infLoop isn't called)

But InfLoop gets called! This is why:

(false || true && infLoop() || true);
1st Comparison.) true && InfLoop() (InfLoop gets called)
2nd Comparison.) False || 1st Comp (will never get here)
3rd Comparison.) 2nd Comp || true; (will never get here)

Precendece ONLY sets the grouping of operations. In this, && is greater than ||.

true && false || true && true gets grouped as
(true && false) || (true && true);

The Compiler Then comes along and determines what order it should execute the evaluation in to give it the best chance for saving cycles.

Consider: false && infLoop() || true && true
Precedence Grouping goes like this:
(false && infLoop()) || (true && true)
The compiler then looks at it, and decides it will order the execution in this order:
(true && true) THEN || THEN (false && InfLoop())

It's kindof a fact..and I don't know how else to demonstrate this. Precedence is determined by the language grammar rules. The Compiler's optimization is determined by each compiler..some are better than others, but All are free to reorder the grouped comparisons as it sees fit in order to give it the "best" chance for the fastest execution with the fewest comparisons.

她比我温柔 2024-10-06 23:00:52

有两个事实解释了这两个示例的行为。首先,&&的优先级高于||。其次,两个逻辑运算符都使用短路求值。

优先级经常与求值顺序混淆,但它是独立的。只要最终结果正确,表达式中的各个元素就可以按任何顺序求值。一般来说,对于某些运算符,这意味着左侧 (LHS) 的值可以在右侧 (RHS) 的值之前或之后进行计算,只要两者在应用运算符本身之前进行计算即可。

逻辑运算符有一个特殊的属性:在某些情况下,如果一侧的计算结果为特定值,则无论另一侧的值如何,运算符的值都是已知的。为了使此属性有用,C 语言(以及扩展后的所有类似 C 的语言)指定了逻辑运算符,以在 RHS 之前计算 LHS,并且进一步仅在需要其值时才计算 RHS。 > 了解运算符的结果。

因此,假设 TRUEFALSE 的通常定义,TRUE &&正确 ||假&& FALSE 从左侧开始计算。第一个 TRUE 不会强制第一个 && 的结果,因此会计算第二个 TRUE,然后表达式 <代码>TRUE && TRUE 被评估(为 TRUE)。现在,|| 知道它的 LHS。更好的是,它的 LHS 强制了解 || 的结果,因此它跳过了对其整个 RHS 的评估。

完全相同的评估顺序适用于第二种情况。由于 || 的 RHS 不相关,因此不会对其求值,也不会调用 infiniteLoop()

此行为是设计使然,并且很有用。例如,您可以编写 p && p->next 知道表达式永远不会尝试取消引用 NULL 指针。

Two facts explain the behavior of both examples. First, the precedence of && is higher than ||. Second, both logical operators use short-circuit evaluation.

Precedence is often confounded with order of evaluation, but it is independent. An expression may have its individual elements evaluated in any order, as long as the final result is correct. In general for some operator, that means that the value on the left (LHS) may be evaluated either before or after the value on the right (RHS), as long as both are evaluated before the operator itself is applied.

The logical operators have a special property: in certain cases if one side evaluates to a specific value, then the operator's value is known regardless of the value on the other side. To make this property useful, the C language (and by extension every C-like language) has specified the logical operators to evaluate the LHS before the RHS, and further to only evaluate the RHS if its value is required to know the result of the operator.

So, assuming the usual definitions of TRUE and FALSE, TRUE && TRUE || FALSE && FALSE is evaluated starting at the left. The first TRUE doesn't force the result of the first &&, so the second TRUE is evaluated, and then the expression TRUE && TRUE is evaluated (to TRUE). Now, the || knows its LHS. Even better, its LHS has forced the result of the || to be known, so it skips evaluation of its entire RHS.

The exact same order of evaluation applies in the second case. Since the RHS of the || is irrelevant, it isn't evaluated and neither call to infiniteLoop() is made.

This behavior is by design, and is useful. For instance, you can write p && p->next knowing that the expression will never attempt to dereference a NULL pointer.

小红帽 2024-10-06 23:00:52

&& 确实有一个 更高优先级

&& does indeed have a higher precedence.

痴情换悲伤 2024-10-06 23:00:52

“如果发生短路 || 运算符并短路第二个 && 表达式的执行,则意味着 || 运算符在第二个 && 运算符之前执行。这意味着从左到右- && 和 || 的正确执行(不是 && 优先)。”

不完全是。

(xx && yy || zz && qq)

将这样评估:

  1. 检查第一个运算符。
  2. 评估 xx && yy
  3. 检查下一个运算符。
  4. 如果下一个运算符是 ||,并且语句的第一部分为 true,则跳过其余部分。
  5. 否则,检查 || 之后的下一个运算符,并对其求值:zz && qq
  6. 最后,评估||

根据我的理解,C++ 的设计是为了在开始计算之前读取内容。毕竟,在我们的示例中,它不知道我们在 || 之后进行了第二次 && 检查,直到它读入它,这意味着它必须在到达第二个 && 之前读取 ||。因此,如果第一部分的计算结果为 true,则它不会执行 || 之后的部分,但如果第一部分的计算结果为 false,则它将执行第一部分,读入||,查找并评估第二部分,并使用第二部分的结果来确定最终结果。

"If short-circuiting the || operator occurs and short-circuits the execution of the second && expression, that means the || operator was executed BEFORE the second && operator. This implies left-to-right execution for && and || (not && precedence)."

Not quite.

(xx && yy || zz && qq)

Would be evaluated like this:

  1. Check first operator.
  2. Evaluate xx && yy
  3. Check next operator.
  4. If next operator is ||, and the first part of the statement is true, skip the rest.
  5. Otherwise, check for next operator after ||, and evaluate it: zz && qq
  6. Finally, evaluate ||.

From my understanding, C++ is designed so that it reads things in before it begins evaluating. After all, in our example, it doesn't know that we have a second && check after the || until it reads it in, meaning it has to read in the || before it gets to the second &&. As such, if the first part evaluated to true, it won't do the part after the ||, but if the first part evaluates to false, then it'll do the first part, read in the ||, find and evaluate the second part, and use the second part's result to determine the final result.

留一抹残留的笑 2024-10-06 23:00:52

由于 and/or/true/false 与 */+/1/0 非常相似(它们在数学上是等价的),因此以下内容也是正确的:

1 * 1 + 0 * 0 == 1

并且相当容易记住......

所以是的,它与优先短路。如果将布尔操作映射到相应的整数操作,则布尔操作的优先级相当容易。

Since and/or/true/false is very similar to */+/1/0 (they are mathematically equivalent-ish), the following is also true:

1 * 1 + 0 * 0 == 1

and rather easy to remember...

So yes, it has to do with precedence and short-circuit. Precendence of boolean ops is rather easy if you map it to their corresponding integer ops.

深白境迁sunset 2024-10-06 23:00:52

关于您的编辑:infiniteLoop() 不会被评估,因为 true || (无论如何)总是正确的。使用 true | (无论如何)如果应该执行任何事情。

regarding your edit: infiniteLoop() won't be evaluated because true || (whatever) is always true. Use true | (whatever) if whatever should be executed.

无风消散 2024-10-06 23:00:52

关于 true &&真实 ||无限循环() &&例如,由于结合了两个特征,因此两个无限循环调用都不会被评估:&&优先于 || 和 ||当左侧为真时短路。

如果&&和||具有相同的优先级,评估必须像这样进行:

((( true && true ) || infiniteLoop ) && infiniteLoop )
(( true || infiniteLoop ) && infiniteLoop )
=> first call to infiniteLoop is short-circuited
(true && infiniteLoop) => second call to infiniteLoop would have to be evaluated

但是由于 && 的优先级,评估实际上是这样的

(( true && true ) || ( infiniteLoop && infiniteLoop ))
( true || ( infiniteLoop && infiniteLoop ))
=> the entire ( infiniteLoop && infiniteLoop ) expression is short circuited
( true )

about the true && true || infiniteLoop() && infiniteLoop() example, neither of the infinite loop calls are being evaluated because of the two characteristics combined: && has precedence over ||, and || short-circuits when the left side is true.

if && and || had the same precedence, the evaluation would have to go like this:

((( true && true ) || infiniteLoop ) && infiniteLoop )
(( true || infiniteLoop ) && infiniteLoop )
=> first call to infiniteLoop is short-circuited
(true && infiniteLoop) => second call to infiniteLoop would have to be evaluated

but because of &&'s precedence, the evaluation actually goes

(( true && true ) || ( infiniteLoop && infiniteLoop ))
( true || ( infiniteLoop && infiniteLoop ))
=> the entire ( infiniteLoop && infiniteLoop ) expression is short circuited
( true )
蔚蓝源自深海 2024-10-06 23:00:52

对于Andrew的最新代码,

#include <iostream>
using namespace std;

bool infiniteLoop () {
    while (true);
    return false;
}

int main() {
    cout << (true && true || infiniteLoop() && infiniteLoop()) << endl; // true
}

短路求值意味着对infiniteLoop的调用保证不会被执行。

然而,它以一种反常的方式很有趣,因为 C++0x 草案使无限循环什么都不做未定义的行为。这项新规则通常被认为是非常不可取和愚蠢的,甚至是彻头彻尾的危险,但它却悄悄地进入了选秀。部分是出于对线程场景的考虑,一篇论文的作者认为这会简化一些非常不相关的规则。

因此,使用处于 C++0x 一致性“前沿”的编译器,程序可能会终止,并产生一些结果,即使它执行了对infiniteLoop的调用!当然,使用这样的编译器,它也可能会产生可怕的现象,鼻守护进程......

令人高兴的是,如上所述,短路评估意味着保证不会执行调用。

干杯&呵呵,

Regarding Andrew's latest code,

#include <iostream>
using namespace std;

bool infiniteLoop () {
    while (true);
    return false;
}

int main() {
    cout << (true && true || infiniteLoop() && infiniteLoop()) << endl; // true
}

Short-circuit evaluation means that the calls to infiniteLoop are guaranteed to not be executed.

However, it's interesting in a perverse sort of way because the C++0x draft makes the infinite-loop-that-does-nothing Undefined Behavior. That new rule is generally regarded as very undesirable and stupid, even downright dangerous, but it sort of sneaked into the draft. Partially from considerations of threading scenarios, where the author of one paper thought it would simplify the rules for something-or-other pretty irrelevant.

So, with a compiler that's on the "leading edge" of C++0x-conformance the program could terminate, with some result, even if it executed a call to infiniteLoop ! Of course, with such a compiler it could also produce that dreaded phenomenon, nasal daemons...

Happily, as mentioned, short-circuit evaluation means that the calls are guaranteed to not be executed.

Cheers & hth.,

成熟稳重的好男人 2024-10-06 23:00:52

简单的答案是&&优先级高于||。另外,代码不会被执行,因为它不需要知道布尔表达式的结果。是的,这是编译器优化。

The simple answer is && takes higher precedence than ||. In addition, the code is not executed because it doesn't need to be to know the result of the boolean expression. Yes, it is a compiler optimization.

决绝 2024-10-06 23:00:52

这是一个顺序说明:

  (true && true || false && false)
= ((true && true) || (false && false))  // because && is higher precedence than ||, 
                                        //   like 1 + 2 * 3 = 7  (* higher than +)
= ((true) || (false))
= true

但还要注意,如果是

(true || ( ... ))

,则不会计算右侧,因此不会调用那里的任何函数,并且表达式将仅返回 true

This is a sequential illustration:

  (true && true || false && false)
= ((true && true) || (false && false))  // because && is higher precedence than ||, 
                                        //   like 1 + 2 * 3 = 7  (* higher than +)
= ((true) || (false))
= true

but also note that if it is

(true || ( ... ))

then the right hand side is not evaluated, so any function there is not called, and the expression will just return true.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文