强制使用bitwise而不是boolean和

时间:2015-10-19 04:44:47

标签: c++ bitwise-operators logical-operators

在C ++中,bool保证为0或1

C++ (§4.5/4):
An rvalue of type bool can be converted to an rvalue of type int, with 
false becoming zero and true becoming one.

考虑以下函数以及g ++ 5.2使用-O3

生成的内容
int foo(bool a, bool b)
{
    if(a&b) return 3;
    else return 5;
}


0000000000000000 <_Z3foobb>:
   0:   40 84 ff                test   %dil,%dil
   3:   74 13                   je     18 <_Z3foobb+0x18>
   5:   40 84 f6                test   %sil,%sil
   8:   b8 03 00 00 00          mov    $0x3,%eax
   d:   74 09                   je     18 <_Z3foobb+0x18>
   f:   f3 c3                   repz retq 
   11:  0f 1f 80 00 00 00 00    nopl   0x0(%rax)
   18:  b8 05 00 00 00          mov    $0x5,%eax
   1d:  c3                      retq   

如上所示,它正在生成两条测试指令,表明它仍然将if视为if(a&amp;&amp; b)而不是按位和。

即使我首先将两个bool显式转换为两个字符,它仍会生成与上面相同的输出。

因为我知道两个操作数a和b只能有0/1作为值,所以有一些方法可以让gcc只生成一条测试指令。如果函数有两个整数而不是两个整数,那么这确实是它的作用。

3 个答案:

答案 0 :(得分:1)

使用&,某些编译器已经生成了不同的asm而没有跳转:

clang 3.6(rc2):

foo(bool, bool):                               # @foo(bool, bool)
    testb   %sil, %dil
    sete    %al
    movzbl  %al, %eax
    leal    3(%rax,%rax), %eax
    retq

一个hack,在你的情况下是使用*,它具有相同的true-table for true / false

int foo(bool a, bool b)
{
    if (a * b) return 3;
    else return 5;
}

产生:

foo(bool, bool):
    movzbl  %sil, %esi  # b, b
    movzbl  %dil, %edi  # a, tmp70
    imull   %esi, %edi  # b, tmp70
    cmpl    $1, %edi    #, tmp70
    sbbl    %eax, %eax  # D.1960
    andl    $2, %eax    #, D.1960
    addl    $3, %eax    #, D.1960
    ret

Demo

答案 1 :(得分:0)

它正在做一个按位-AND。这是&所做的定义,编译器正确地实现了按位AND的可观察行为。

二进制&的操作数经历整数提升,因此ab会转换为int。此转换的定义是:

  • true已转换为1
  • false已转换为0

执行按位-AND,这是一个详尽的案例列表:

  • 1 & 1 == 1
  • 1 & 0 == 0
  • 0 & 1 == 0
  • 0 & 0 == 0

正如您所看到的,这是一个布尔AND的案例列表。因此,仅通过查看装配输出就无法区分操作。

要确认这一点,here is an example显示g ++为if (a&b)生成与if (a&&b)完全相同的程序集。

请记住,C ++是根据抽象机器定义的,编译器的输出可以是任何东西,只要它产生与抽象机器相同的可观察行为。

您似乎反对与bool转换为int相关的代码部分。您的编译器显然已实现bool,因此任何非零位模式都被视为true

因此,需要额外的步骤。例如,如果其中一个bool具有位模式test %dil, %sil而另一个具有000...0010,则执行000....0001会产生错误的结果。

正如user657267在注释中指出的那样,编译器决定在一个参数上测试非零,然后在另一个参数上测试非零,这很容易证明它满足定义的行为的要求&以及如上所述的整数促销。

你无能为力。您可以提交一个g ++补丁,以便它只为000...0001 bool生成位模式true,因此它可以在这里使用test %dil, %sil,但我怀疑它会被接受。

可能可能编写读取ab表示的代码,并在最低字节上执行&。然而,这将是危险的;编译器可能已决定发送一些比您预期的其他位模式,因为它知道那些是true可接受的位模式。

另一种解决方案可能是将您的函数更改为接受unsigned int参数而不是bool。当然,这会引入新bug的可能性,例如:如果您使用1ull << 40调用该函数,则会将1ull << 40截断为0,因此与传入false的行为相同。你不能吃你的蛋糕并拥有它;在这种情况下,每次调用函数时都必须非常小心。 (也许为int编写一个包装类会有所帮助!)

答案 2 :(得分:-1)

我觉得这可能不是你正在寻找的东西但通常算术和只有被测试的对象是int类型才能被解除,所以一个简单的演员可以解决这个问题,但我会假设一个演员可能会生成您不想要的额外不需要的指令。

int foo(bool a, bool b)
{ 
    if((int)a&(int)b) return 3;
    else return 5;
}