关于评估顺序和比较的一些问题

some questions about evaluation order and comparison

在一次C/C++面试测试中,我发现了一些我没有正确回答的问题,我用Visual C++检查了结果,希望你能帮助我理解它们:

1)

int i=-3, j=2, k=0, m;
m = ++i && ++j || ++k; // k not incremented why ???
cout << i << " " << j << " " << k << " " << m; // -2 3 0 1 why it's not -2 3 1 1 !!!

=> 为什么 k 没有递增,尤其是它前面有一个 ++?我想知道执行这一行的顺序,我无法在调试模式下执行此操作。

能否请你给我一个规则来评估这样的表达式,其中有像 ++variable 或 variable++ 这样的东西谢谢

2) 为什么这个比较是错误的?

float a = 5.2;
if(a == 5.2) // false
{}

当我向 5.2 添加浮点转换时它起作用了....

3)

int n()
{
    static int x = 0;
    return x++;
}

=> 我认为我们总是 return 0 因为我认为编译器会将 "return x++" 翻译成:return x; x++;那么我们永远不会执行增量...

下次有多个问题时,请分开提问。

问题1的答案:

几件事:

  1. &&|| 运算符都强制从左到右求值1,并且都引入了 序列点;将计算左侧操作数,并在计算右侧操作数之前应用所有副作用;

  2. &&||运算符短路-如果表达式的值可以从左边确定-手边的操作数,那么右边的操作数将不会被计算;

    • 在表达式a || b中,如果a不为零,b将不会被计算;
    • 在表达式a && b中,如果a为零,b将不会被计算;
  3. &&的优先级高于||,所以a || b && c被解析为a || (b && c)

  4. x++ 评估为 x 的当前值,并且作为 副作用 递增 x;

  5. ++x 的计算结果为 x 的当前值加 1,并且作为 副作用 递增 x .

所以,表达式

++i && ++j || ++k

解析

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

评价如下:

  1. ++i被评估;结果是 -2 不为零,所以:
  2. ++j被评价;结果是 3,所以:
  3. -23 都非零,所以:
  4. ++i && ++j 的计算结果为 1,因此:
  5. ++k 根本没有被评估。

问题2的答案:

同样,几个问题:

  1. 大多数浮点值无法精确表示;它们存储为近似值(您不能将无限数量的值放入有限数量的位中);

  2. 浮点常量表达式5.2的类型是double,不是float;要使其成为 float,您将使用 f 后缀 - 5.2f;

  3. floatdouble 具有不同的表示形式(double 为指数和分数分配了更多位),因此它们将存储相同值的不同近似值,即为什么 == 比较不起作用。

  4. 正因为如此,你不应该使用==来比较浮点值;通常,您将取两个值之间的差值并确保它小于某个 epsilon 值(请记住 epsilon 值取决于幅度)。

问题3的答案:

您正在返回 表达式 x++ 的结果,但该表达式仍然具有递增 x 的副作用(副作用是在 return 语句结束之前应用)。


1。 C 中的大多数运算符 not 会强制执行特定的求值顺序 - 给定一个像 a + b * c 这样的表达式,abc 可以按任何顺序 求值 b * c 结果 必须先知道才能将其添加到 a 的结果中,但这并不意味着 bc 必须在 a.
之前 求值

  1. C and C++ operate on short-circuit logic.

考虑以下几点:

bool myBool = (1 || 0+6*4);

myBool 会尽快评估。在这种情况下,它将计算为真,因为 || 中的左参数为真。它立即停止评估。 && 也是如此。这就是为什么在布尔运算符的左侧添加更可能失败的情况是惯用的。

  1. It is false because floats cannot be represented perfectly by binary.

在这种情况下,它实际上会评估为 true。但是,请考虑以下示例:

#include <iostream>

int main() {
   double i = 0.1;
   double total = 0.0;
   for(int j = 0; j < 10000; j++) {
      total+=i;
   }

   // total = 0.1*10000 = 1000... right?

   std::cout << std::boolalpha << "total == 1000.0 ? " << (total == 1000.0) << std::endl;

   return 0;
}

Here是上面程序的输出。比较浮点数和双精度数在计算中是一件棘手的事情。小心点。

  1. Because it is static, it will be increment before return and remain its static value each time the function is called.

我不会过多地讨论这个,因为它在 this question about lifetime of static variables 中有很好的概述。本质上,无论可见性如何,static 的内容与程序的寿命一样长。

问题编号 1:

&& 运算符的优先级高于 ||,因此将首先对其求值。两项(++i++j)都有预增,所以先分别增加到-2和3,然后进行AND运算。在 C++ 中,0 是 false,其他都是 true。在这种情况下,两项都是 true(即非零),因此 && return 为 true。现在是测试true || ++k的时候了。在这里,优化开始了:因为 true || anything 总是 true,编译器甚至不测试表达式。也就是说,它不执行它。并且 k 不会递增。这就是为什么让 if 语句中的代码做某事是一个非常糟糕的习惯——你不能确定它会是 运行,具体取决于条件。如果您需要它到运行,确保它没有放在那里,否则它可以被优化掉。

问题编号 2:

浮点运算很棘手 - 通常不可能准确地表示一个数字,真正使用的数字只是一个非常接近的近似值 - 足够接近以至于它看起来可行,但如果你去检查每一个小的有点你注意到数字不是他们看起来的样子。默认情况下,数字被视为 double。一个double和一个float,虽然看起来一样,但其实不一样。这已在此处介绍:strange output in comparison of float with float literal. And I really recommend to read one of the linked articles, this one: "What Every Computer Scientist Should Know About Floating-Point Arithmetic"

问题编号 3:

returned 的值为 0(第一次!),你是对的,但你认为指令分为两部分,第一部分是 return,这应该会导致第二个(增量)被跳过。不,它不是那样工作的。在任何情况下都会执行增量。 Post 增量是这样工作的:制作一个副本,增加原件,return 副本。任何称为 post 增量的东西都会看到原始值,但增量 发生。而由于xstatic,这个值会被保留,这样下次调用函数n()时,x的初始值就会是1。然后2、3 等等。