(x ^ 0x1) != 0 的意思是什么?

182

我遇到了以下代码片段

if( 0 != ( x ^ 0x1 ) )
     encode( x, m );

x ^ 0x1 是什么意思?这是一种标准技术吗?


98
代码混淆是很常见的。 - raina77ow
几天前我也遇到了类似的情况 https://dev59.com/emIj5IYBdhLWcg3w8ZYF - user2286243
48
这个问题可以使用与“普通”方程相同的方法来解决:0 != (x ^ 1) → 将两边都异或 1 → (0 ^ 1) != (x ^ 1 ^ 1) → 简化 → 1 != x - Score_Under
4
“我不明白为什么写if (1 != x)很难。” - Adriano Varoli Piazza
12
@Spook的评论明显指出x的类型未给定 - 因此我们不知道在这个被标记为C++的问题中,它是否是整数。如果这是C语言或者x是一个整数,那么答案很容易得出,但这并不是必然的,因为有可能对operator ^进行重载。 - chux - Reinstate Monica
11
我不明白为什么这篇文章有这么多赞... - laike9m
17个回答

277

XOR操作(x ^ 0x1)反转位0。因此该表达式有效地意味着:如果x的第0位为0,或者x的任何其他位为1,则该表达式为真。

相反,如果x == 1,则表达式为假。

因此,这个测试与以下测试相同:

if (x != 1)

因此(可以说)它是不必要的混淆。


40
嘿,你不知道上下文。如果x是某种位标志,我认为现在写它比使用!=运算符更清晰。 - Spook
40
@Spook:这里不仅是测试单个比特标志——它涉及测试整个 x 的位宽。如果你只想测试单个比特,那么有更清晰的用法,比如使用按位与运算符。 - Paul R
115
不必要地混淆代码吗?你不知道混淆代码是我们的工作吗?如果我们编写了任何人都能理解的简单代码,那么我们在世界上的宗教神圣地位去哪了?我们会变得和其他人一样普通。混淆本质上是必要的。 - Thom
31
实际上,Spook是正确的。这个测试(x != 1)并不等同。这段代码可以是C++(在C++中,“^”可以是任意操作符)。因此,你不知道上下文,@Spook是正确的。 - xryl669
82
请用清晰的文字写下您的内容。 - bobobobo
显示剩余12条评论

79
  • ^是按位异或运算符
  • 0x1是十六进制表示中的1
  • x ^ 0x1将翻转x的最后一位(如果您不清楚,可以参考上面链接中的异或真值表)。

因此,条件(0 != ( x ^ 0x1 ))将在x大于1或x的最后一位为0时成立。这只留下了x==1作为条件不成立的值。因此,它等同于

if (x != 1)

顺便说一下,这种简单条件的实现方式真是够糟糕的。千万别这样做。如果你必须编写复杂的代码,请写注释。我请求你了。


7
不是 x==04 ^ 0x1 为真,但 4==0 显然是假的。 - Fred Foo
4
“condition seems to be equal to if (x == 0)”, doesn't it equal to x != 1? “条件似乎等同于 if (x == 0)”,难道它不等同于 x != 1 吗? - Andrew-Dufresne
6
等价性假设“x”是一个整数类型。如果它是一个浮点数或双精度数,那么我认为表达式对于“1.0 <= x < 2.0”会返回true。如果“x”是用户定义类型,则表达式可以在以下情况下返回true:如果“x”是一辆尤戈车、袋鼠、著名作曲家的生日或任何数字与当前以美元计价的中国茶叶价格共享至少三个数字。 - supercat
4
@supercatпјҢеҜ№дәҺfloat/doubleзұ»еһӢпјҢжІЎжңүoperator^иҝҗз®—з¬ҰгҖӮ - fluffy
1
@supercat:当需要将浮点数转换为整数时,转换是隐式的(不需要强制转换语法)。但是按位运算符不会触发任何转换,它们对于浮点类型只会简单地失败。 - Ben Voigt
显示剩余6条评论

49

这可能看起来是一个过于简化的解释,但如果有人想慢慢理解,以下是:

^ 是 c、c++ 和 c# 中的 位异或 运算符。

位异或操作取两个等长的二进制位模式,并对每一对相应的位执行逻辑异或操作。

异或是一种逻辑操作,当两个输入不同时输出 true(一个为 true,另一个为 false)。

a xor b真值表

a           b        a xor b
----------------------------
1           1           0
1           0           1
0           1           1
0           0           0

让我们从二进制层面解释表达式 0 == ( x ^ 0x1 ):

             what? xxxxxxxx (8 bits)
               xor 00000001 (hex 0x1 or 0x01, decimal 1)    
             gives 00000000
---------------------------
the only answer is 00000001

所以:
   0 == ( x ^ 0x1 )    =>    x == 1
   0 != ( x ^ 0x1 )    =>    x != 1

34

这是异或(XOR)运算符。要了解它的工作原理,您可以运行此简单代码

    std::cout << "0x0 ^ 0x0 = " << ( 0x0 ^ 0x0 ) << std::endl;
    std::cout << "0x0 ^ 0x1 = " << ( 0x0 ^ 0x1 ) << std::endl;
    std::cout << "0x1 ^ 0x0 = " << ( 0x1 ^ 0x0 ) << std::endl;
    std::cout << "0x1 ^ 0x1 = " << ( 0x1 ^ 0x1 ) << std::endl;

输出将会是

0x0 ^ 0x0 = 0
0x0 ^ 0x1 = 1
0x1 ^ 0x0 = 1
0x1 ^ 0x1 = 0

因此,这个表达式

0 != ( x ^ 0x1 )

只有当x != 0x1时才会等于true。

它不会改变x本身。它仅检查x是否等于0或1。该表达式可以更改为

if ( x != 0x1 )

19

它检查x实际上不是0x1...使用xorx0x1异或,只会在x0x1时结果为0...这是一种主要用于汇编语言的老技巧


这比 != 1 更快吗? - Fiddling Bits
2
在古代,当进行手动汇编优化(x86)时,如果我没记错的话,“xor”方法包含的机器代码较少,并且执行速度比相应的赋值为“0”的方法更快...然而,这个问题包含了一个“xor”和一个比较,所以我认为“!=”可能会更快。不过我不太确定,需要看一些编译器生成的汇编代码。 - Ferenc Deak
10
不。如果异或比某些本地等价性测试快,你的编译器将发出异或以测试等价性。因此,在优化编译器上,异或永远不会比等价性测试更快。编写快速代码的第101条规则是“不要尝试帮助编译器。你只会最终得到难以阅读的、实际上更慢的代码”。 - Matt
如果我没记错的话,XOR技巧更多地涉及到保存寄存器,因为在某些情况下可以直接在操作码中编码文字,以及一些状态标志的微妙差别(但我大部分汇编是在68k和DSP上进行的)。 - mpdonadio
1
@MPD: 通常是与清除寄存器有关(至少在386+上)。 xor eax,eax 在两个字节中将eax设置为零,而mov eax,0需要三个或六个字节(取决于编码),并且比xor形式需要稍长时间解码。 - Matt

18

^ 运算符是按位异或。而 0x1 是数字 1 的十六进制常量表示。

因此,x ^ 0x1 得到的值与 x 相同,但最低有效位取反。

这段代码只是以一种非常复杂和难以理解的方式将 x 与 1 进行比较。


11

xor(异或)运算符最常用于反转一个或多个位。该操作是询问其中一位是否恰好为1,这导致以下真值表(A和B是输入,Y是输出):

A    B    Y
0    0    0
0    1    1
1    0    1
1    1    0

现在这段代码的目的似乎是检查最后一位是否为1,其他位是否为0,这相当于 if ( x != 1 )。 这种模糊的方法之所以被使用可能是之前已经使用了位操作技术,并且可能在程序的其他地方也使用了。


8

^ 是在 c 语言中用于位运算的异或操作符。在你的代码中,x 被异或上了 1。例如,如果 x 的值是 10,那么 10d ^ 1d ===> 1010b ^ 0001b = 1011b, 1011b == 11d,所以条件成立。


你的回答中有错别字。10 != 1010 - Fiddling Bits
@BitFiddlingCodeMonkey:你的评论中有错别字:10(十进制)== 1010(二进制) - Paul R
6
如果您没有在数字后面加上 b 或其他标识,那么任何人怎么知道哪些是十进制数,哪些是二进制数呢? - Fiddling Bits
0b1010难道不是Pythonic的做法吗? - TankorSmash

8
位测试似乎是一种故意的混淆方式,但如果底层数据来自IBM主机系统的企业数据,则可能仅仅是为了反映原始文档而编写的代码。IBM数据格式可以追溯到1960年代,并经常将标志编码为单个字中的单个位以节省存储空间。随着格式的修改,在现有记录末尾添加了标记字节以保持向后兼容性。例如,SMF记录的文档可能显示汇编语言代码来测试单个记录中三个不同字中的三个单独的位,以决定数据是输入文件。我对TCP/IP内部了解较少,但您可能会在其中发现比特标志。

7

^ 操作符是按位异或运算(参见 &, |)。对于一对二进制位,其结果为

0 ^ 0 == 0
0 ^ 1 == 1
1 ^ 0 == 1
1 ^ 1 == 0

因此,这个表达式,
( x ^ 0x1 )

将x的第0位翻转/反转(其他位不变)。

请考虑x是否可以有除了0x0和0x1之外的值?当x是单个位域时,它只能具有0x0和0x1值,但当x是int(char / short / long /等)时,除了bit0之外的位可能会影响表达式的结果。

给定的表达式允许除了bit0之外的位影响结果,

if ( 0 != ( x ^ 0x1 ) )

这个(更简单)表达式的真实性相当于以下表达式:
if ( x ^ 0x1 )

请注意,此表达式仅检查bit0。
if( 0x1 & ( x ^ 0x1 ) )

因此,所呈现的表达式实际上是结合了两个表达式检查的结果,

if( ( x & ~0x1 )  //look at all bits besides bit0
||  ( x ^ 0x1 ) ) //combine with the xor expression for bit0

作者是否只想检查位0,并意图使用这个表达式:

if( 0x1 & ( x ^ 0x1 ) )

作者是否有意将bit1-bitN的值与bit0的异或混合在一起?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接