哪一个会执行得更快,if(flag == 0)或if(0 == flag)?

mr_*_*air 111 c c++

面试问题:哪一个会执行得更快,if (flag==0)或者if (0==flag)?为什么?

Mat*_* M. 235

我还没有看到任何正确答案(并且已经有一些警告):Nawaz确实指出了用户定义的陷阱.而且我很遗憾我匆匆忙忙对"最愚蠢的问题"投了反对意见,因为似乎有很多人没有把它弄好,它为编译器优化提供了很好的讨论空间:)

答案是:

什么是flag类型?

flag实际上是用户定义类型的情况下.然后它取决于operator==选择的过载.当然,它们似乎很愚蠢,它们不会是对称的,但它肯定是允许的,而且我已经看到了其他的滥用行为.

如果flag是内置的,那么两者都应该采用相同的速度.

维基百科的文章x86,我敢打赌一Jxx对指令if语句:也许JNZ(跳转如果不是零)或某些等效.

我怀疑编译器错过了这样一个明显的优化,即使关闭优化也是如此.这是Peephole Optimization的设计类型.

编辑: Sprang再次,所以让我们添加一些程序集(LLVM 2.7 IR)

int regular(int c) {
  if (c == 0) { return 0; }
  return 1;
}

int yoda(int c) {
  if (0 == c) { return 0; }
  return 1;
}

define i32 @regular(i32 %c) nounwind readnone {
entry:
  %not. = icmp ne i32 %c, 0                       ; <i1> [#uses=1]
  %.0 = zext i1 %not. to i32                      ; <i32> [#uses=1]
  ret i32 %.0
}

define i32 @yoda(i32 %c) nounwind readnone {
entry:
  %not. = icmp ne i32 %c, 0                       ; <i1> [#uses=1]
  %.0 = zext i1 %not. to i32                      ; <i32> [#uses=1]
  ret i32 %.0
}
Run Code Online (Sandbox Code Playgroud)

即使一个人不知道如何阅读IR,我认为这是自我解释的.

  • 好!你的答案可能会把"最愚蠢的问题"变成"狡猾/最卑鄙的人"."让我们为候选人挖一个洞,看看他是否落入其中......":)我想我们都会自动认为`flag`必须是整数或布尔值.OTOH,有一个名为`flag`的用户定义类型的变量本身是非常错误的,恕我直言 (7认同)
  • @Matthieu:你说过*我还没有看到任何正确答案*..但我的是正确的,我想:P (4认同)
  • @mr_eclair:内置类型是一种内置于该语言中的类型(如隐含的名称).也就是说,即使没有单个`#include`指令也可以使用它.为简单起见,它通常等于`int`,`char`,`bool`等.所有其他类型都被称为用户定义,因为它们是一些用户声明它们的结果:`typedef`,`enum`,`struct`,`class`.例如,`std :: string`是用户定义的,即使你当然没有自己定义:) (2认同)

Naw*_*waz 56

您的版本没有任何区别.

我假设type标志不是用户定义的类型,而是一些内置类型.Enum是个例外!.您可以将枚举视为内置的.事实上,它的价值是内置类型之一!

如果它是用户定义的类型(除外enum),那么答案完全取决于你如何重载运算符==.请注意,您必须==通过定义两个函数来重载,每个函数对应一个函数!

  • 如果现代编译器错过了如此明显的优化,我会非常惊讶. (15认同)
  • 这可能是提出这个问题的唯一可能原因,恕我直言 (8认同)
  • @Nawaz:没有下来投票,但你的答案实际上是错误的,它仍然有很多赞成票是可怕的.对于记录,将整数与0进行比较是**单个汇编指令**,与否定完全相同.事实上,如果编译器有点愚蠢,那么这甚至可能比否定更快**(尽管不太可能). (8认同)
  • @Nawaz:说它可以,将来,或通常会更快,这仍然是错误的.*如果*存在差异,则"与零比较"版本将更快,因为否定实际上转换为两个操作:"否定操作数;检查结果是否为非零".当然,在实践中,编译器会对其进行优化以产生与简单的"与零比较"版本相同的代码,但优化将应用于否定版本,以使其赶上,而不是相反.康拉德是对的. (6认同)
  • 据我所知 !不是一个按位操作 (3认同)

小智 56

与GCC 4.1.2相同的amd64代码:

        .loc 1 4 0  # int f = argc;
        movl    -20(%rbp), %eax
        movl    %eax, -4(%rbp)
        .loc 1 6 0 # if( f == 0 ) {
        cmpl    $0, -4(%rbp)
        jne     .L2
        .loc 1 7 0 # return 0;
        movl    $0, -36(%rbp)
        jmp     .L4
        .loc 1 8 0 # }
 .L2:
        .loc 1 10 0 # if( 0 == f ) {
        cmpl    $0, -4(%rbp)
        jne     .L5
        .loc 1 11 0 # return 1;
        movl    $1, -36(%rbp)
        jmp     .L4
        .loc 1 12 0 # }
 .L5:
        .loc 1 14 0 # return 2;
        movl    $2, -36(%rbp)
 .L4:
        movl    -36(%rbp), %eax
        .loc 1 15 0 # }
        leave
        ret
Run Code Online (Sandbox Code Playgroud)

  • +1加倍努力证明编译器优化是相同的. (18认同)

Lin*_*een 27

绝对没有区别.

通过参考消除任务/比较拼写错误,您可能会在回答该面试问题时获得积分:

if (flag = 0)  // typo here
   {
   // code never executes
   }

if (0 = flag) // typo and syntactic error -> compiler complains
   {
   // ...
   }
Run Code Online (Sandbox Code Playgroud)

虽然这是真的,例如C编译器在前一个(flag = 0)的情况下发出警告,但在PHP,Perl或Javascript或者中没有这样的警告<insert language here>.

  • @David:Downvoters应该解释自己,因为这个网站不是秘密流行选票,匿名投票等.这个网站是关于学习的.如果某人通过低估来表示反应是不正确的,那么如果他们不解释原因,那么他们的知识就是自私自利.他们愿意在他们正确的时候获得所有的荣誉,但是当其他人错误时他们不愿意分享知识. (26认同)
  • 我根本没有投票,但是它的价值是什么:为什么人们在投票时解释自己是如此重要?投票是匿名设计.我完全反对downvoters应该总是发表评论的想法,因为我个人并不想因为我留下一个指出问题的评论而被假定为downvoter.也许这位贬低者认为大多数答案与速度问题无关?也许他认为这鼓励了他不赞同的编码风格?也许他是一个鸡巴,并希望自己的答案得到最高评价? (7认同)
  • 无论出于什么原因,人们都应该随意投票.声誉方面,这几乎总是一件好事,因为它经常引起其他人的支持,以应对不应有的下注,而事实上,一个单一的upvote将抵消五个不应当的downvotes. (3认同)

Jon*_*Jon 16

速度方面绝对没有区别.为什么会这样?

  • 如果flag是用户定义的类型,其中运算符的非对称重载==() (8认同)
  • 如果编译器完全被延迟.这是唯一的原因. (7认同)
  • 假设处理器有一个"test if if 0"指令,`x == 0`可能会使用它,但`0 == x`可能会使用正常比较.我确实说过必须要迟钝. (2认同)

ds2*_*680 12

当flag是用户定义的类型时,存在差异

struct sInt
{
    sInt( int i ) : wrappedInt(i)
    {
        std::cout << "ctor called" << std::endl;
    }

    operator int()
    {
        std::cout << "operator int()" << std::endl;
        return wrappedInt;
    }

    bool operator==(int nComp)
    {
        std::cout << "bool operator==(int nComp)" << std::endl;
        return (nComp == wrappedInt);
    }

    int wrappedInt;
};

int 
_tmain(int argc, _TCHAR* argv[])
{
    sInt s(0);

    //in this case this will probably be faster
    if ( 0 == s )
    {
        std::cout << "equal" << std::endl;
    }

    if ( s == 0 )
    {
        std::cout << "equal" << std::endl;
    }
}
Run Code Online (Sandbox Code Playgroud)

在第一种情况下(0 == s),调用转换运算符,然后将返回的结果与0进行比较.在第二种情况下,调用==运算符.

  • 提及转换运算符可能与运算符相关的+1 ==. (3认同)

Elz*_*ugi 11

如有疑问,请将其标记并了解真相.

  • 什么是基准测试错误?有时这种做法比理论更能告诉你 (2认同)

Mat*_*lia 7

它们在速度方面应该完全相同.

但请注意,有些人习惯在等式比较中将常量放在左边(所谓的"Yoda条件"),以避免在编写=(赋值运算符)而不是==(等式比较运算符)时可能出现的所有错误; 因为分配给文字会触发编译错误,所以避免了这种错误.

if(flag=0) // <--- typo: = instead of ==; flag is now set to 0
{
    // this is never executed
}

if(0=flag) // <--- compiler error, cannot assign value to literal
{

}
Run Code Online (Sandbox Code Playgroud)

另一方面,大多数人发现"Yoda条件"看起来很奇怪而且很烦人,特别是因为他们预防的错误类别也可以通过使用适当的编译器警告来发现.

if(flag=0) // <--- warning: assignment in conditional expression
{

}
Run Code Online (Sandbox Code Playgroud)


dar*_*ioo 5

正如其他人所说,没有区别.

0必须进行评估.flag必须进行评估.这个过程需要相同的时间,无论它们放在哪一侧.

正确的答案是:它们的速度都是一样的.

即使是表达式if(flag==0)if(0==flag)有相同数量的字符!如果其中一个被编写为if(flag== 0),那么编译器将有一个额外的空间来解析,因此你有理由指出编译时间.

但由于没有这样的东西,所以绝对没有理由为什么一个人应该比其他人更快.如果有原因,那么编译器正在做一些非常非常奇怪的事情来生成代码......


dav*_*vka 5

好吧,我同意所有人在对OP的评论中所说的,为了运动起见:

如果编译器不够聪明(实际上你不应该使用它)或者禁用优化,x == 0可以编译为本机汇编jump if zero指令,同时0 == x可能是更通用(且代价高昂)的数值比较.

不过,我不想为一个以这些方式思考的老板工作......


Fan*_*c23 5

哪一个快速取决于您使用的==版本.这是一个使用2个可能的==实现的片段,并且根据您是否选择调用x == 0或0 == x选择其中一个.

如果您只是使用POD,那么在速度方面这无关紧要.

#include <iostream>
using namespace std;

class x { 
  public:
  bool operator==(int x) { cout << "hello\n"; return 0; }
  friend bool operator==(int x, const x& a) { cout << "world\n"; return 0; } 
};

int main()
{ 
   x x1;
   //int m = 0;
   int k = (x1 == 0);
   int j = (0 == x1);
}
Run Code Online (Sandbox Code Playgroud)