在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只生成一条测试指令。如果函数有两个整数而不是两个整数,那么这确实是它的作用。
答案 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
答案 1 :(得分:0)
它正在做一个按位-AND。这是&
所做的定义,编译器正确地实现了按位AND的可观察行为。
二进制&
的操作数经历整数提升,因此a
和b
会转换为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
,但我怀疑它会被接受。
可能可能编写读取a
和b
表示的代码,并在最低字节上执行&
。然而,这将是危险的;编译器可能已决定发送一些比您预期的其他位模式,因为它知道那些是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;
}