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

时间:2015-09-16 21:17:07

标签: c++ boolean-expression

在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 !!!

=&GT;为什么k不增加特别是在它之前有一个++?我想知道执行这种行的顺序,我无法在调试模式下执行此操作。

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

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

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

当我向5.2添加浮动渲染时,它可以工作....

3)

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

=&GT;我以为我们总是返回0,因为我认为编译器会将“return x ++”翻译为:return x; X ++;那么我们永远不会执行增量...

3 个答案:

答案 0 :(得分:4)

问题1:

&&运算符优先于||,因此将首先对其进行评估。两个术语(++i++j)都有预先递增,因此首先它们分别增加到-2和3,然后它们是AND。在C ++中,0是false,其他一切都是true。在这种情况下,两个术语都是true(即非零),因此&&返回true。现在是时候测试true || ++k了。在这里,优化开始了:因为true || anything总是true,编译器甚至不测试表达式。也就是说,它不会执行它。并且k不会递增。这就是为什么在if语句中执行某些操作时使用代码是一个非常糟糕的习惯 - 根据条件的不同,您无法确定它是否会运行。如果需要运行它,请确保它没有放在那里或者它可以被优化掉。

问题2:

浮点算术很棘手 - 通常不可能代表一个数字,真正使用的数字只是一个非常接近的近似值 - 足够接近看起来它的工作原理,但是如果你去检查每一小块你注意数字不是它们的样子。默认情况下,数字被视为doubledoublefloat虽然看起来一样,却不一样。这已经在这里讨论过:strange output in comparison of float with float literal。我真的建议您阅读其中一篇链接文章,其中一篇:"What Every Computer Scientist Should Know About Floating-Point Arithmetic"

问题3:

你是对的,返回的值是0(第一次!),但是你认为该指令分为两部分,第一部分是return,这应该导致第二部分(要跳过的增量)。不,它不会那样工作。在任何情况下都执行增量。帖子增量的工作方式如下:制作副本,增加原件,返回副本。无论什么称为后增量都会看到原始值,但增量会发生。由于xstatic,因此将保留该值,以便下次调用函数n()时,x的初始值将为1.然后2,3等等。

答案 1 :(得分:3)

下次有多个问题时,请单独询问。

回答问题1:

有几件事:

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

  2. &&||运算符短路 - 如果表达式的值可以从左侧操作数确定,那么右侧-hand side operand将不会被评估;

      表达式a || b中的
    • ,如果b非零,则不会评估a;
    • 表达式a && b中的
    • ,如果b为零,则不会评估a;
  3. &&的优先级高于||,因此a || b && c被解析为a || (b && c);

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

  5. ++x评估x的当前值加1,副作用增加x

  6. 所以,表达式

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

    解析

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

    评估如下:

      评估
    1. ++i;结果是-2,它不是零,所以:
    2. 评估
    3. ++j;结果是3,所以:
    4. -23都不为零,因此:
    5. ++i && ++j评估为1,所以:
    6. ++k根本没有评估。
    7. 回答问题2:

      同样,有几个问题:

      1. 大多数浮点值无法正确表示 ;它们存储为近似值(您无法将无限数量的值拟合为有限数量的位);

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

      3. floatdouble具有不同的表示形式(double将更多位用于指数和分数),因此它们将为相同的值存储不同的近似值,这就是{{{比较没有效果。

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

      5. 回答问题3:

        您将返回表达式 ==的结果,但该表达式仍然具有递增x++的副作用(并且副作用在结束前应用) x声明。

        <小时/> 1。 C中的大多数运算符强制执行特定的评估顺序 - 给定return这样的表达式,每个a + b * cab可以按任何顺序评估。必须先知道c结果,然后才能将其添加到b * c的结果中,但这并不代表a或{在b之前,{1}}必须评估

答案 2 :(得分:1)

  
      
  1. C和C ++使用短路逻辑。
  2.   

请考虑以下事项:

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

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

  
      
  1. 这是假的,因为浮点数不能完全用二进制表示。
  2.   

在这种情况下,它实际上会评估为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. 因为它是静态的,所以它会在返回之前递增,并在每次调用函数时保持其静态值。
  2.   

我不会太过深入,因为它在this question about lifetime of static variables中得到了很好的概括。基本上,只要程序static存在,无论可见性如何,都会存在。