关于评价顺序和比较的几个问题

some questions about evaluation order and comparison

本文关键字:比较 几个问题 顺序 于评价 评价      更新时间:2023-10-16

在一次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不加,特别是在它前面有一个++ ?我想知道执行这一行的顺序,我不能在调试模式下这样做。

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

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

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

当我添加一个浮点转换到5.2它工作....

3)

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

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

问题1:

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

问题2:

浮点运算很棘手——准确地表示一个数字通常是不可能的,而真正使用的数字只是一个非常接近的近似值——接近到看起来可以工作,但如果你检查每一个小的地方,你会发现数字并不是它们看起来的样子。默认情况下,数字被视为double。一个double和一个float,虽然它们看起来一样,但并不相同。这里已经讨论过了:float和float literal比较时的奇怪输出。我强烈建议大家阅读其中一篇链接文章,这篇:《每个计算机科学家都应该知道的浮点算术》

问题3:

你是对的,返回值是0(第一次!),但是你认为该指令被分成两部分,其中第一部分是return,这应该导致第二部分(增量)被跳过。不,不是这样的。在任何情况下都执行增量。后增量的工作原理是这样的:创建一个副本,增加原始副本,返回副本。不管叫后增量的是什么,都会看到原始值,但是增量不会发生。并且由于xstatic,因此该值将被保留,因此下次调用n()函数时,x的初始值将为1。然后是2 3,等等

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

问题1的答案:

几件事情:

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

  2. &&||操作符都短路——如果表达式的值可以从左侧操作数确定,则不计算右侧操作数;

      表达式a || bb中的
    • 如果a非零则不求值;
    • 表达式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. 正因为如此,你不应该使用==来比较浮点值;通常,你会取两个值之间的差,并确保它小于某个值(记住,值取决于大小)。

问题3的答案:

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


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

  1. C和c++操作短路逻辑。

考虑以下内容:

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

myBool将尽快计算。在本例中,它的值为true,因为||中的左参数为true。它立即停止计算。&&也是如此。这就是为什么习惯地在布尔运算符的左侧添加更有可能失败的情况。

  • 为false,因为浮点数不能完美地用二进制表示。
  • 在这种情况下,它的值实际上是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;
    }
    
    下面是上面程序的输出。比较浮点数和双精度数在计算中是一件棘手的事情。做这件事要小心。
  • 因为它是静态的,它将在返回之前递增,并且每次调用函数时保持其静态值。
  • 我不会深入讨论这个问题,因为它在静态变量生命周期的问题中已经很好地概述了。本质上,static与程序一样存在,而不考虑可见性