在这个复杂的表达式中,运算符的优先级/求值顺序是什么,为什么结果是 -129?

What is the operator precedence/order of evaluation in this complex expression, and why is the result -129?

提问人:Yurii Kapusta 提问时间:9/24/2023 最后编辑:Jan SchultkeYurii Kapusta 更新时间:9/25/2023 访问量:133

问:

我不明白操作在 C++ 表达式中发生的顺序。

例如: 为什么我们到了-129?你能解释一下行动的顺序吗?

#include <stdio.h>

int main()
{
    char a = 60;
    unsigned c = 88;
    long d = 134;
    int e = -6;

    printf("Reuslt: %ld\n",!a++<sizeof(long double)&(c)|~d--+-e );
    
    return 0;
}

我尝试使用 int k1、k2、k3、...、kn(其中 k 代表 1 个动作)的动作来做到这一点,但我还不明白动作的顺序。

C++ 运算符优先级 执行顺序

评论

2赞 Ahmed AEK 9/24/2023
运算符优先级,如果在此页面中未指定,则为 UB。这将取决于编译器创建者
2赞 Pepijn Kramer 9/24/2023
你确定你有一个“C++”问题,你的代码看起来像“C”。在 C++ 中,在这种情况下的计算顺序没有特殊性。此外,出于调试目的,只需尝试将一行上的副作用数量限制为 1。(除非真的很容易推理这些副作用)。不知何故,在我看来,您只是想破坏编译器;)
2赞 BoP 9/24/2023
无论如何,一个好的经验法则是不要编写你自己不理解的代码。这是没有人需要理解的代码。
3赞 Eric Postpischil 9/24/2023
@BoP:这是每个C++程序员都需要理解的代码。一个基本的调试技能是根据语言语义弄清楚代码在形式上说了什么,而不是人们认为它说了什么。虽然编写清晰的代码对人类很有用,并且其含义与人类自然解释它的方式一致,但由于多个程序员的更改或其他原因而产生错误,并且错误的存在通常是由于人类的某些信念是不正确的。因此,我们必须学会分析代码的形式含义。做这样的练习对于培养这种技能很有用。
1赞 Pepijn Kramer 9/24/2023
@EricPostpischil我希望你的意思是任何人都应该能够说这是一团糟,让我们用更简单的术语写下我们的意思。有时答案真的是,如果你能用更简单的东西代替它,你就不需要理解一团糟。

答:

1赞 Lajos Arpad 9/24/2023 #1

我不会给你确切的答案,因为这是一个练习,最终是你需要解决它。但我会给你你需要的信息,以便解决这个问题。

优先 级:

  • !: 3
  • a++: 2
  • <: 9
  • sizeof(): 3
  • a&b: 11
  • |: 13
  • ~: 3
  • d--: 2
  • +: 6
  • -e: 3

来源: https://en.cppreference.com/w/cpp/language/operator_precedence

当我们谈论相同优先级的操作时,人们会期望顺序是从左到右的(除了特殊情况,例如分配,这里不存在,我们从哪里到右),顺序作的优先级覆盖。优先级编号越低,操作的优先级越高。

例如,将执行第一个,然后执行 ,因为优先级较高(较低的 prio 数表示前面的运算符)。因此,建议你拿笔和纸画出表达式树!a++++!++

这将使您了解这里究竟发生了什么以及以什么顺序发生。尝试在不查看表达式的实际结果的情况下弄清楚这一点,并将您获得的结果与稍后使用表达式测试的结果进行比较,而不会受到影响。如果结果匹配,那么很可能(但不是 100% 证明)您是正确的。如果结果不匹配,则说明有问题。

如果结果不匹配,则隔离表达式树的子树并分别评估它们,直到您获得导致不匹配的确切原因,然后您就会明白为什么您错了。

3赞 Ted Lyngmo 9/24/2023 #2

你有。。。

!a++ < sizeof(long double) & (c) | ~d-- + -e

...因此,让我们从 C++ 运算符优先级表中获取我们需要的内容,并添加括号以阐明如何解释表达式:

优先 算子 描述 关联性
2 a++ a-- 后缀/后缀递增和递减 从左到右
3 +a -a 一元正负 从右到左
! ~ 逻辑 NOT 和按位 NOT
sizeof 尺寸
6 a+b a-b 加法和减法 从左到右
9 < <= > >= 对于关系运算符,分别为 < 和 ≤ 以及 > 和 ≥ -"-
11 a&b 按位 AND -"-
13 | 按位 OR(包括 OR) -"-
  • 2. 对于和:a++d--
    !(a++) < sizeof(long double) & (c) | ~(d--) + -e
    
  • 3. 对于 、 和 (我在这里省略了括号):!(a++)sizeof~(d--)-esizeof(type)
    (!(a++)) < sizeof(long double) & (c) | (~(d--)) + (-e)
    
  • 6. 对于:(~(d--)) + (-e)
    (!(a++)) < sizeof(long double) & (c) | ((~(d--)) + (-e))
    
  • 9. 对于:(!(a++)) < sizeof(long double)
    ((!(a++)) < sizeof(long double)) & (c) | ((~(d--)) + (-e))
    
  • 11. 对于:((!(a++)) < sizeof(long double)) & (c)
    (((!(a++)) < sizeof(long double)) & (c)) | ((~(d--)) + (-e))
    
  • 13. 此时只剩下这些了,所以这些围绕着整个表达式:|
    ((((!(a++)) < sizeof(long double)) & (c)) | ((~(d--)) + (-e)))
    

还有带括号的表达式,可以更清楚地说明哪些子表达式联系在一起,您现在只需要从内括号中解开混乱并取出即可。

0赞 Olaf Dietsche 9/24/2023 #3

@TedLyngmo的回答开始

  • 由于 和 稍后不会在代码中使用,因此您可以忽略并删除 post 递增和递减运算符ad

    ((((!(a)) < sizeof(long double)) & (c)) | ((~(d)) + (-e)))
    
  • !a0

    ((((0) < sizeof(long double)) & (c)) | ((~(d)) + (-e)))
    
  • 0 < sizeof(long double)给出 true 或1

    (((1) & (c)) | ((~(d)) + (-e)))
    
  • c是偶数,因此结果为1 & c0

    ((0) | ((~(d)) + (-e)))
    
  • 按位保留任何内容,因此可能会被删除0 | anything

    (~(d)) + (-e)
    
  • +(-e)是基本的算术

    (~(d)) - e
    
  • 这些只是简单的操作,将表达式减少到最低限度。剩下的表达式是 just or ,由差值(或总和)和按位 not 组成。为此,您需要了解二进制算术和 Two 补码(维基百科)。(~d) - e(~134) + 6


  • 感谢 @user17732522 的评论:按位 or: 有一个转折点,另请参阅隐式转换 (cppreference)。0 | anything

    • 由于操作数具有类型 和 ,第一个操作数 () 将被转换为,结果如问题中所述。unsigned intsigned long0signed long
    • 如果操作数是 ,则第二个操作数 () 将被转换为 并且结果不同。unsigned intsigned int(~d) - eunsigned int

评论

0赞 user17732522 9/24/2023
您忽略了表达式的类型,这些类型很重要。如果例如 而不是 ,则结果值将不同。dintlong
0赞 Olaf Dietsche 9/24/2023
你是对的,我错过了这个。