&&和 ||运营商

发布于 2024-12-09 10:48:53 字数 453 浏览 1 评论 0原文

我遇到了这段代码:

    int main()
    {
        int i=1,j=2,k=0,m=0;
        m = ++i || ++j && ++k;
        printf("%d %d %d %d %d",i,j,k,m);
    }

程序返回 2 2 0 1...。为什么?

&& 的优先级高于 ||,因此 ++j && ++k 应该首先被评估。因此我期望 j=3k=1。它将返回 true,因此 || 变为 true,因此不应评估 ++i。但它的作用却相反。

我希望其他人向我解释。

I came across this code:

    int main()
    {
        int i=1,j=2,k=0,m=0;
        m = ++i || ++j && ++k;
        printf("%d %d %d %d %d",i,j,k,m);
    }

The program returns 2 2 0 1.... Why?

&& has a higher priority than || so ++j && ++k should be evaluated first. Hence I would expect j=3 and k=1. It will return true hence || becomes true so ++i shouldn't be evaluated. But it works other way around.

I would like others to explain to me.

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

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

发布评论

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

评论(11

安静被遗忘 2024-12-16 10:48:53

具有更高的优先级并不意味着它首先被评估。这只是意味着它结合得更紧。在该示例中,该表达式相当于:++i || (++j && ++k)。首先计算的是 ++i 因为 || 从左到右计算。仅当计算结果为 false 时, ++j && ++k 被评估,因为 || 是短路的。

Having higher precedence does not mean it gets evaluated first. It just means it binds tighter. In that example, that expression is equivalent to: ++i || (++j && ++k). What gets evaluated first is ++i because || evaluates left to right. Only if that evaluates to false will ++j && ++k be evaluated because || is short-circuiting.

灼痛 2024-12-16 10:48:53

实际上 ++i 将首先被评估。仅当它为假时才会评估右侧(在您的情况下并非如此)。

事实上,“&& 具有更高的优先级”与优先级(其操作数与它的紧密程度)相关,而不是“谁的操作数首先被评估”。

因为 && 在表中确实位于 || 之上,所以该表达式将被解释如下:

m = ++i || (++j && ++k)

Actually ++i will be evaluated first. Only if it's false will the right side be evaluated (and in your case it's not).

The fact that "&& has higher priority" relates to precedence (how tightly its operands stick to it) not "whose operands get evaluated first".

Because && is indeed above || in the table, the expression will be interpreted like this:

m = ++i || (++j && ++k)
忆梦 2024-12-16 10:48:53

短路评估。如果 && 的左侧非零,则仅计算右侧。同样,只有当 || 的左侧为零时,才会计算右侧。

Short circuit evaluation. If, the left-side of the && is non-zero, only then will the right-hand side be evaluated. Likewise, only if the left-hand side of the || is zero, will the right-hand side be evaluated.

猥琐帝 2024-12-16 10:48:53

“较高的运算符优先级”与“首先评估”不同。当您使用短路运算符时,它们将从左到右进行计算。任何算术的结果都会受到运算符优先级的影响,但这不会改变短路的从左到右的顺序。

你的例子的复杂性是不做这类事情的一个很好的理由。即使您弄清楚了规则并确切地知道它将做什么,下一个来查看代码的程序员也可能不会。

"Higher operator precedence" is not the same as "evaluated first". When you use the short-circuiting operators, they are evaluated left-to-right. The results of any arithmetic will be affected by operator precedence, but that doesn't change the left-t0-right ordering of short circuiting.

The complexity of your example is a good reason for not doing this sort of thing. Even if you figure out the rules and know exactly what it will do, the next programmer to come along and look at the code probably won't.

耀眼的星火 2024-12-16 10:48:53

基本上,|| 的意思是,“如果您收到了真实的内容,则返回该内容,否则,返回之后发生的任何内容。”因此,唯一需要评估的是m = (++i != 0)。这意味着“增加 i,将 m 分配给与 0 相比的 i 值,中断”。

更具体地说,这是正在发生的事情:

i = 1;
i = i + 1;
if( i ) {
   m = 1;
}
else { // who cares, this will never happen.
   j = j + 1;
   if( j ) {
       k = k + 1;
       m = (k != 0); // 1
   }
   else {
       m = 0;
   }
}

Basically, || means, "if you have received something which is true, return that, otherwise, return whatever happens afterwards." So, the only thing which is evaluated there is m = (++i != 0). That means "increment i, assign m to the value of i compared to 0, break."

To be more specific, this is what is happening:

i = 1;
i = i + 1;
if( i ) {
   m = 1;
}
else { // who cares, this will never happen.
   j = j + 1;
   if( j ) {
       k = k + 1;
       m = (k != 0); // 1
   }
   else {
       m = 0;
   }
}
决绝 2024-12-16 10:48:53

在 C 语言中,您需要注意两个不同的问题:运算符优先级求值顺序

运算符优先级决定了哪个运算符首先计算其操作数,以及哪些操作数属于哪个运算符。例如,在表达式 a + b * c 中,运算符 * 的运算符优先级高于 +。因此,表达式将被计算为

a + (b * c)

C 语言中的所有运算符都具有确定性优先级,并且它们在任何编译器上都是相同的。

求值顺序决定首先求值的操作数。请注意,子表达式也是操作数。确定运算符优先级后应用计算顺序。如果上面的 a+b*c 示例具有从左到右的求值顺序,则操作数本身将按 a、b、c 的顺序求值。例如,如果操作数是函数调用,则函数 a()、b() 和 c() 将按该顺序执行。本例中的所有操作数都需要进行计算,因为它们都已被使用。如果编译器可以确定某些操作数不需要计算,则可以将它们优化掉,无论这些操作数是否包含副作用(例如函数调用)。

问题在于,操作数的求值顺序通常是未指定的行为,这意味着编译器可以自由地从左到右或从右到左求值,而我们无法知道或假设任何事情关于它。对于 C 中的大多数操作数都是如此,除了少数例外情况,其中评估顺序始终是确定性的。这些是运算符 || 的操作数&& ?: ,,其中求值顺序保证为从左到右。 (当使用正式的 C 语言语义时,人们说在左运算符和右运算符的求值之间存在一个序列点。)


因此对于具体示例 m = ++i || ++j && ++k

  • 一元前缀 ++ 运算符具有最高优先级,将运算符 i、j 和 k 绑定到它们。这个语法非常直观。
  • 二进制 &&运算符具有第二高优先级,将运算符 ++j++k 绑定到它。因此该表达式等价于 m = ++i || (++j && ++k)
  • 二进制||运算符具有第三高优先级,将运算符 i++(j++ && ++k)= 绑定到它。
  • 赋值运算符 = 具有最低优先级,绑定运算符 m++i || (++j && ++k) 到它。

此外,我们可以看到 ||和 &&运算符是保证计算顺序从左到右的运算符之一。换句话说,如果 || 的左操作数运算符被评估为 true,编译器不需要评估正确的操作数。在具体示例中,++i 始终为正数,因此编译器可以执行相当大的优化,有效地将表达式重新构造为 m = ++i;

如果i 在编译时未知,编译器将被迫计算整个表达式。然后表达式将按以下顺序求值:

  • &&优先级高于 ||,因此开始评估 &&操作员。
  • &&运算符保证操作数的求值顺序是从左到右,因此首先执行 ++j。
  • 如果 ++j 的结果为真(大于零),并且只有这样,才评估正确的运算符:执行 ++k。
  • 存储 ++j && 的结果++k 在临时变量中。我在这里将其称为j_and_k。如果 ++j 和 ++k 均为正数,则 j_and_k 将包含值 1(真),否则为 0(假)。
  • ||运算符保证操作数的求值顺序是从左到右,因此首先执行 ++i。
  • 如果 ++i 为假(零),并且只有在那时,才计算正确的运算符“j_and_k”。如果其中一个或两个均为正,则 || 的结果运算符为 1(真),否则为 0(假)。
  • 根据结果​​,m 被分配值 1 或 0。

In the C language, there are two different issues you need to be aware of: operator precedence and order of evaluation.

Operator precedence determines which operator that gets its operands evaluated first, and also which operands that belong to which operator. For example in the expression a + b * c, the operator * has higher operator precedence than +. Therefore the expression will be evaluated as

a + (b * c)

All operators in the C language have deterministic precedence and they are the same on any compiler.

Order of evaluation determines which operand that gets evaluated first. Note that a sub-expression is also an operand. Order of evaluation is applied after the operator precedence has been determined. If the above a+b*c example has left-to-right order of evaluation, then the operands themselves get evaluated in the order a, b, c. If the operands were for example function calls, then the functions a(), b() and c() would have been executed in that order. All operands in this example need to be evaluated since they are all used. If the compiler can determine that some operands need not be evaluated, it can optimize them away, regardless of whether those operands contain side-effects (such as function calls) or not.

The problem is that order of evaluation of operands is most often unspecified behaviour, meaning that the compiler is free to evaluate either left-to-right or right-to-left, and we cannot know or assume anything about it. This is true for most operands in C, save for a few exceptions where the order of evaluation is always deterministic. Those are the operands of the operators || && ?: ,, where the order of evaluation is guaranteed to be left-to-right. (When using formal C language semantics, one says that there is a sequence point between the evaluation of the left and the right operator.)


So for the specific example m = ++i || ++j && ++k.

  • The unary prefix ++ operators have the highest precedence, binding the operators i, j and k to them. This syntax is pretty intuitive.
  • The binary && operator has 2nd highest precedence, binding the operators ++j and ++k to it. So the expression is equivalent to m = ++i || (++j && ++k).
  • The binary || operator has 3rd highest precedence, binding the operators i++ and (j++ && ++k)= to it.
  • The assignment operator = has the lowest precedence, binding the operators m and ++i || (++j && ++k) to it.

Further, we can see that both the || and the && operators are among those operators where the order of evaluation is guaranteed to be left to right. In other words, if the left operand of the || operator is evaluated as true, the compiler does not need to evaluate the right operand. In the specific example, ++i is always positive, so the compiler can perform quite an optimization, effectively remaking the expression to m = ++i;

If the value of i wasn't known at compile time, the compiler would have been forced to evaluate the whole expression. Then the expression would have been evaluated in this order:

  • && has higher precedence than ||, so start evaluating the && operator.
  • && operator is guaranteed to have order of evaluation of operands left-to-right, so perform ++j first.
  • If the result of ++j is true (larger than zero), and only then, then evaluate the right operator: perform ++k.
  • Store the result of ++j && ++k in a temporary variable. I'll call it j_and_k here. If both ++j and ++k were positive, then j_and_k will contain value 1 (true), otherwise 0 (false).
  • || operator is guaranteed to have order of evaluation of operands left-to-right, so perform ++i first.
  • If ++i is false (zero), and only then, evaluate the right operator "j_and_k". If one or both of them are positive, the result of the || operator is 1 (true), otherwise it is 0 (false).
  • m gets assigned the value 1 or 0 depending on the result.
ぶ宁プ宁ぶ 2024-12-16 10:48:53

您会在这里看到逻辑运算符短路。如果 || 条件的第一部分为 true,则它永远不会计算表达式的其余部分(因为如果第一部分是指针非空检查,则您不希望取消引用第二部分中的指针(如果为空)。此外,由于它位于布尔结果表达式中,因此 ++i 的结果在分配给 之前会转换回 bool1 >米。

像躲避瘟疫一样避免这种代码,它只会给你带来短期和长期的调试噩梦。

You're seeing logical operator short-circuiting here. If the first part of an || condition is true, then it never evaluates the rest of the expression (because if the first part is a pointer-not-null check you wouldn't want to dereference the pointer in the second part if it's null). Further, since it's in a boolean-result expression the result of ++i is converted back to bool value 1 before being assigned into m.

Avoid this kind of code like the plague, it will only give you debugging nightmares in the short and long term.

一身软味 2024-12-16 10:48:53

快捷运算符将导致不必要的表达式组件不被计算。由于&&具有更高的优先级,如果您想允许||,则需要最后对其进行评估当 ++i 计算结果为 true 时,运算符能够简化整个表达式。由于这种情况,++i 是“=”之后计算的唯一变量。

Shortcut operators will cause the unnecessary expression components not to be evaluated. Since && has a higher precedence, it would need to be evaluated last if you want to allow the || operator to be able to shortcut the whole expression when ++i evaluates to true. Since this is the case, ++i is the only variable evaluated after the "=".

陌路终见情 2024-12-16 10:48:53

优先级和评估顺序是两个不同的事情。 ||&& 都从左到右计算其操作数;优先级不会改变这一点。

给定表达式 a ||乙&& c, a 将首先被评估。如果结果为 0,则 b && c 将被评估。

Precedence and order of evaluation are two different things. Both || and && evaluate their operands left-to-right; precedence doesn't change that.

Given the expression a || b && c, a will be evaluated first. If the result is 0, then b && c will be evaluated.

指尖上得阳光 2024-12-16 10:48:53

比较运算符(|| 和 &&)的顺序更为重要。
这就是为什么您最好首先进行最重要的测试。

The order of comparison operators (|| and &&) is more important.
That's why you'd better placed your most important test first.

热血少△年 2024-12-16 10:48:53

因为 ||和&& 短路,因此指定序列点

注意:这最初也被标记为 C++,并且作为重载运算符,您会得到略有不同的答案||或&&不要仅使内置短路

because || and && short circuit and therefore specify sequence points

Note: this was originally tagged C++ as well, and you get a slightly different answer there as overloaded operators || or && do not short circuit just inbuilt ones

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