RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 590020
Accepted
ampawd
ampawd
Asked:2020-11-12 05:34:51 +0000 UTC2020-11-12 05:34:51 +0000 UTC 2020-11-12 05:34:51 +0000 UTC

关于表达式评估的顺序

  • 772

我想了解在一般情况下哪些规则决定了表达式值的评估顺序。

假设有这样的代码

int readValue()
{
  int v;
  cin >> v;
  return v;
}

int main()
{
  cout << readValue() << ' ' << readValue() << '\n';
  return 0;
}

如您所知,移位运算符是从左到右计算的-但是当您输入1 2输出时2 1(Microsoft的编译器),这是什么原因?

这段代码是生成undefined behaviour还是只是输出顺序未定义?

如果你删除阅读代码——一切都是可以预见的

int readValue(int v)
{
  return v;
}

int main()
{
  cout << readValue(1) << ' ' << readValue(2) << '\n';
  return 0;
}

结论 -1 2

怎么了 ?

c++
  • 3 3 个回答
  • 10 Views

3 个回答

  • Voted
  1. AnT stands with Russia
    2020-11-12T06:11:33Z2020-11-12T06:11:33Z

    这些移位运算符是从左到右求值的说法是正确的,但它只告诉您连续运算符应用于<<其直接操作数的顺序。在这种情况下,运算符的直接操作数是<<该类型的一些临时中间值int

    cout << __tmp_int1 << ' ' << __tmp_int2 << '\n';
    

    在这种情况下,关于这个(而且只有这个)表达式,我们可以说它是从左到右计算的。也就是说,该值__tmp_int1将更早显示,而该值将更__tmp_int2晚显示。

    而至于这些中间值的准备顺序和时刻——不局限于上面的说法。也就是说,编译器可以做和

    int __tmp_int1 = ReadValue();
    int __tmp_int2 = ReadValue();
    

    反之亦然

    int __tmp_int2 = ReadValue();
    int __tmp_int1 = ReadValue();
    

    或者通常以某种不可预测的方式将训练__tmp_int1和__tmp_int2挑战交织在一起。<<最主要的是价值在需要的时候已经准备好了。没有更多的规定。

    • 6
  2. Best Answer
    Vlad from Moscow
    2020-11-12T05:44:07Z2020-11-12T05:44:07Z

    根据 C++ 标准,函数参数的求值顺序没有指定,这意味着编译器可以选择任何参数的求值顺序 From the C++ standard (1.9 Program execution)

    3 抽象机的某些其他方面和操作在本国际标准中被描述为未指定(例如,函数参数的评估顺序)。

    此优惠

      cout << readValue() << ' ' << readValue() << '\n';
    

    是一个函数 operator <<调用链。它对应于以下命名的函数调用链 operator <<

    operator <<( operator <<( std::cout.operator <<( readValue() ), ' ' )
                 .operator <<( readValue() ),  '\n' );
    

    MS VC++ 从右到左评估参数。另一个编译器可能会以不同的顺序评估参数,例如从左到右。

    编辑:我将提供一个基于运算符重载的额外说明性示例operator &&。

    考虑以下演示程序

    #include <iostream>
    
    struct A
    {
        int x;
        A( int x = 0 ) : x( x ) {}
    };
    
    bool operator &&( const A &a, int x )
    {
        return 0 < a.x && 0 < x; 
    }
    
    A f1()
    {
        std::cout << "f1()" << std::endl;
        return A( -1 );
    }
    
    int f2()
    { 
        std::cout << "f2()" << std::endl;
        return 1;
    }
    
    int main(void) 
    {
        std::cout << ( f1() && f2() ) << std::endl;
    
        return 0;
    }
    

    它的控制台输出如下

    f2()
    f1()
    0
    

    operator &&如果这是基本类型的内置运算符,则f2()如果表达式的值为 ,则不会对表达式f1()求值false。

    但是,由于这是一个用户重载的函数调用,编译器会执行以下操作。

    编译器首先尝试确定正在使用哪个重载函数。如果找不到,或者有歧义,编译器就会发出一条错误消息。如果找到这样的函数,它会用对相应用户函数的调用替换给定的条目。并基于此函数调用,构建生成的目标代码。由于未指定计算函数参数的顺序,因此输出显示编译器首先计算右参数,然后计算左参数。

    将此程序的输出与运算符operator &&处理基本类型(即使用内置运算符)的程序的输出进行比较operator &&。

    #include <iostream>
    
    int f1()
    {
        std::cout << "f1()" << std::endl;
        return  0;
    }
    
    int f2()
    { 
        std::cout << "f2()" << std::endl;
        return 1;
    }
    
    int main(void) 
    {
        std::cout << ( f1() && f2() ) << std::endl;
    
        return 0;
    }
    

    将程序输出到控制台

    f1()
    0
    

    如您所见,该函数f2将永远不会被调用,因为左操作数的值为false。

    这种差异是由于以下事实:在第一种情况下,编译器处理名称为 的用户定义函数 operator &&的调用,而在第二种情况下,它处理内置运算符 operator &&。

    • 5
  3. Andrey
    2020-12-09T17:10:21Z2020-12-09T17:10:21Z

    正如已经提到的,函数和运算符的参数的评估顺序没有由标准定义,但在绝对数量的情况下,出于实际原因,它是从右到左的:假设有一个代码

    void f(int, int);
    f(g(), h());
    

    h() 先求值比较方便,因为它的结果必须先入栈才能调用 f() 函数。好吧,举个例子

    int readValue(int v)
    {
      return v;
    }
    
    int main()
    {
      cout << readValue(1) << ' ' << readValue(2) << '\n';
      return 0;
    }
    

    一切也很简单。其实这里编译器很可能是在编译时计算了readValue(1)和readValue(2)的值,即 很可能在汇编代码中没有调用 readValue。实际上,这是在vs2015中生成的汇编代码:

        std::cout << readValue(1) << ' ' << readValue(2) << std::endl;
    01121000  mov         ecx,dword ptr [_imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A (0112203Ch)]  
    01121006  push        offset std::endl<char,std::char_traits<char> > (01121300h)  
    0112100B  push        2  
    0112100D  push        1  
    0112100F  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (01122038h)]  
    01121015  mov         ecx,eax  
    01121017  call        std::operator<<<std::char_traits<char> > (011210F0h)  
    0112101C  mov         ecx,eax  
    0112101E  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (01122038h)]  
    01121024  mov         ecx,eax  
    01121026  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (01122058h)]  
        return 0;
    0112102C  xor         eax,eax  
    }
    0112102E  ret 
    

    虽然这里的结果不依赖于调用的顺序,但如果不进行优化,结果是一样的。

    • 1

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    如何停止编写糟糕的代码?

    • 3 个回答
  • Marko Smith

    onCreateView 方法重构

    • 1 个回答
  • Marko Smith

    通用还是非通用

    • 2 个回答
  • Marko Smith

    如何访问 jQuery 中的列

    • 1 个回答
  • Marko Smith

    *.tga 文件的组重命名(3620 个)

    • 1 个回答
  • Marko Smith

    内存分配列表C#

    • 1 个回答
  • Marko Smith

    常规赛适度贪婪

    • 1 个回答
  • Marko Smith

    如何制作自己的自动完成/自动更正?

    • 1 个回答
  • Marko Smith

    选择斐波那契数列

    • 2 个回答
  • Marko Smith

    所有 API 版本中的通用权限代码

    • 2 个回答
  • Martin Hope
    jfs *(星号)和 ** 双星号在 Python 中是什么意思? 2020-11-23 05:07:40 +0000 UTC
  • Martin Hope
    hwak 哪个孩子调用了父母的静态方法?还是不可能完成的任务? 2020-11-18 16:30:55 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    user207618 Codegolf——组合选择算法的实现 2020-10-23 18:46:29 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    Arch ArrayList 与 LinkedList 的区别? 2020-09-20 02:42:49 +0000 UTC
  • Martin Hope
    iluxa1810 哪个更正确使用:if () 或 try-catch? 2020-08-23 18:56:13 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5