据我所知,在类C语言中,我可以使用not
运算符执行布尔值:(C)
int myBool = TRUE;
myBool = !myBool;
但我的问题是:这是如何在幕后实现的?我的猜测是使用跳转,但如果过度使用则效率低下:( Intel x86语法)
; assume eax holds boolean
test eax, eax
jnz short boolTrue
inc eax ; eax was 0, now 1
jmp short after
boolTrue: ; eax non-zero
xor eax, eax ; eax now 0
after:
如图所示,它需要5条指令,至少有一个跳转,一个按位and
(test
)。必须有一个更简单的方法来实现这一点,因为我已经看到代码库基于某些奇怪的原因而做了“双重注意”(if (!!fileHandle)
)。
所以(如上所述):编译器如何在x86上进行布尔!
?
答案 0 :(得分:4)
您可以使用SETZ
(设置(如果)为零(设置标志))来执行此操作,但不需要分支。
答案 1 :(得分:2)
有可能使用进位和无分支序列:
sub %eax, 1 ;; this produces carry only when %eax EQ zero
sbb %eax, %eax ;; 0 when not carry, -1 when carry
and %eax, 1 ;; just get the least significant bit (also neg %eax will do)
代表eax=!eax
和
sub %eax, 1 ;;
sbb %eax, %eax
add %eax, 1 ;; [-1, 0] + 1 == [0, 1]
代表eax = !!eax
。
我无法弄清楚是否有3个指令(或更少)序列使用不会浪费原始变量(或寄存器eax)的常见算术运算。
答案 2 :(得分:1)
在良好的编译器中,用于实现诸如NOT的运算符的方法通常不仅限于一个代码生成模式。编译器将考虑周围上下文的许多因素,例如编译时优化,紧接在NOT运算符之前的源和/或生成的代码,是否在条件上下文中使用NOT运算符(例如,在if语句中使用)或值上下文(用于赋值)。
当在if语句中使用时,NOT运算符更可能使用分支实现,因为if语句最有可能需要分支。在值上下文中使用时,NOT运算符更可能通过比较和捕获结果来实现(没有分支)。
当在较大的表达式中使用NOT运算符时,通常可以反转嵌套运算符,因此对于一种查看它的方法,NOT运算符根本不生成代码;这可能发生在a = ! (b > 0)
或if ( ! ( a && b ) )
等表达式中。即使使用if ( !a )
,您也可能会发现a实际上并未被忽略,而只是在if ( a )
中进行测试,但分支条件已反转。
在您给出的具体示例中,我希望编译器能够做出最好的努力(为生产而不是为调试生成代码),它会在编译时确定常量0
而不是{{1应该传递给myBool
。
简而言之,一个不错的编译器有许多可用的技术和许多标准来确定使用哪些。
答案 3 :(得分:0)
如果你有一个合适的布尔值,你可以使用XOR
在两个表示之间切换。请注意C
没有bool,它只将零解释为false,其他任何东西都为true。 C++
和C#
都有适当的布尔值,因此他们不需要使用非零检查,实际上至少g++
确实使用了XOR
方法。