O que significa (x ^ 0x1)! = 0?

183

Me deparei com o seguinte trecho de código

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

O que x ^ 0x1significa isso ? Isso é alguma técnica padrão?

KodeWarrior
fonte
97
A ofuscação de código é bastante padrão.
precisa
47
Isso pode ser resolvido com a mesma abordagem de uma equação "normal": 0 != (x ^ 1)→ xou ambos os lados por 1 → (0 ^ 1) != (x ^ 1 ^ 1)→ simplificar →1 != x
Score_Under
4
Não vejo como if (1 != x)é difícil escrever.
Adriano Varoli Piazza
12
Vários comentários, primeiro de @Spook, apontam claramente que o typede xnão é fornecido - portanto, não sabemos que esse é um número inteiro nesse problema marcado com C ++. Claro, se este é C ou xé um número inteiro, a resposta é fácil, mas isso não é um dado e operator ^existe a possibilidade de sobrecarga .
chux - Restabelece Monica
11
Eu não vejo como isso fica tantos upvotes ...
laike9m

Respostas:

277

A operação XOR ( x ^ 0x1) inverte o bit 0. Portanto, a expressão significa efetivamente: se o bit 0 de x é 0 ou qualquer outro bit de x é 1, a expressão é verdadeira.

Por outro lado, a expressão é falsa se x == 1.

Portanto, o teste é o mesmo que:

if (x != 1)

e é, portanto, (sem dúvida) desnecessariamente ofuscado.

Paul R
fonte
39
Ei, você não conhece o contexto. Se x é algum tipo de sinalizador de bit, é IMO realmente mais claro escrevê-lo como está agora do que usar o operador! =.
Spook
40
@ Spook: não está apenas testando uma flag de bit único - está testando toda a largura de x. Se você quiser apenas testar um único bit, há expressões mais claras, como o uso de um AND bit a bit.
Paul R
115
Desnecessariamente ofuscado? Você não sabe que é nosso trabalho ofuscar código. Se escrevêssemos um código simples que alguém pudesse entender, por que, onde iria a santidade religiosa de nossas posições no mundo? De repente, seríamos trabalhadores comuns, como todo mundo. Ofuscação é inerentemente necessária.
19413 Thom
31
Na verdade, Spook está certo. O teste (x! = 1) não é equivalente. O código pode ser C ++ (e, em C ++, ^ pode ser um operador que faz qualquer coisa). Então você não conhece o contexto, o @Spook está certo.
xryl669
82
@TheThom уєт уσυ ωяιтє ιи ¢ ℓєαя тєχт
bobobobo
78
  • ^é o bit a bit XOR operação
  • 0x1está 1em notação hexadecimal
  • x ^ 0x1irá inverter o último bit de x(consulte a tabela verdade do XOR no link acima, se isso não estiver claro para você).

Portanto, a condição (0 != ( x ^ 0x1 ))será verdadeira se xfor maior que 1 ou se o último bit xfor 0. O que deixa apenas x == 1 como um valor no qual a condição será falsa. Então é equivalente a

if (x != 1)

PS Inferno de uma maneira de implementar uma condição tão simples, devo acrescentar. Não faça isso. E se você precisar escrever um código complicado, deixe um comentário . Eu imploro a você.

Girafa violeta
fonte
7
Não é x==0; 4 ^ 0x1é verdade, mas 4==0é obviamente falsa.
Fred Foo
4
"condição parece ser igual a if (x == 0)", não é igual a x != 1?
Andrew-Dufresne
6
A equivalência pressupõe que xé um tipo integral. Se é um floatou double, então acredito que a expressão renderia verdadeira para 1.0 <= x < 2.0. E se xfor do tipo definido pelo usuário, a expressão poderá retornar verdadeira se xfor um yugo, canguru, aniversário do famoso compositor ou qualquer número que compartilhe pelo menos três dígitos com o preço atual do chá em dólar na China.
Supercat 19/12
4
@supercat Não há operator^para float/ double.
macio
1
@ supercat: Quando é necessária uma conversão de ponto flutuante para inteiro, a conversão está implícita (não requer sintaxe de conversão). Mas nenhuma conversão é acionada pelos operadores bit a bit, eles simplesmente falham nos tipos de ponto flutuante.
Ben Voigt
49

Isso pode parecer uma explicação simplificada, mas se alguém quiser passar por isso lentamente, está abaixo:

^é um operador XOR bit a bit em c, c ++ e c #.

Um XOR bit a bit pega dois padrões de bit de igual comprimento e executa a operação OR exclusiva lógica em cada par de bits correspondentes.

OR exclusivo é uma operação lógica que gera true sempre que as duas entradas diferem (uma é verdadeira, a outra é falsa).

A tabela verdade de a xor b :

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

Então, vamos ilustrar a 0 == ( x ^ 0x1 )expressão no nível binário:

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

tão:

   0 == ( x ^ 0x1 )    =>    x == 1
   0 != ( x ^ 0x1 )    =>    x != 1
jwaliszko
fonte
34

É um operador exclusivo OR (XOR). Para entender como funciona, você pode executar este código simples

    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;

A saída será

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

Então essa expressão

0 != ( x ^ 0x1 )

será igual a verdade somente quando x! = 0x1.

Não muda x em si. Ele verifica apenas se x é igual a 0 ou 1. essa expressão pode ser alterada para

if ( x != 0x1 )
Vlad de Moscou
fonte
19

Ele verifica que xna verdade não é 0x1... xoring xcom 0x1resultará em 0 somente se xé 0x1... este é um velho truque usado principalmente em linguagem assembly

Ferenc Deak
fonte
Isso é mais rápido que != 1?
Fiddling Bits de
2
nos tempos antigos, ao fazer otimizações manuais de montagem (x86), se bem me lembro, a xorabordagem continha menos código de máquina e foi executada mais rapidamente do que a atribuição correspondente a 0... no entanto, esta pergunta contém uma xorcomparação AND, por isso acho que !=pode ser Mais rápido. Não tenho tanta certeza, no entanto, precisaria ver algum assembly gerado pelo compilador.
Ferenc Deak
10
@BitFiddlingCodeMonkey: Não. Se o XOR fosse mais rápido do que algum teste de igualdade no nível nativo, seu compilador emitiria XORs para testar a igualdade. Portanto, os XORs nunca são mais rápidos que os testes de igualdade na otimização de compiladores. A regra 101 de escrever código rápido é "não tente ajudar o compilador. Você acabará criando código ilegível que é mais lento na prática".
19413 Matt
@fritzone IIRC, os truques do XOR tinham mais a ver com salvar registros, salvar uma carga de registro porque o literal poderia ser codificado diretamente no OP em alguns casos, e algumas diferenças sutis nos sinalizadores de status (mas a maioria da minha montagem estava em 68k e DSP).
mpdonadio
1
@MPD: Normalmente, tem a ver com a limpeza de um registro (pelo menos no 386+). xor eax, eax define eax como zero em dois bytes, mas mov eax, 0 leva três ou seis bytes (dependendo da codificação) e leva um pouco mais de tempo para decodificar que a forma xor .
Matt
18

O ^operador é bit a bit xor. E 0x1é o número 1, escrito como uma constante hexadecimal.

Portanto, x ^ 0x1avalia um novo valor que é igual a x, mas com o bit menos significativo invertido.

O código nada mais faz do que comparar x com 1, de uma maneira muito complicada e obscura.

David Heffernan
fonte
11

O operador xor (exclusivo ou) é mais comumente usado para inverter um ou mais bits. A operação é perguntar se exatamente um dos bits é um, isso leva à seguinte tabela verdade (A e B são entradas, Y é saída):

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

Agora, o objetivo deste código parece ser verificar exatamente se o último bit é 1 e os outros são 0, isso é igual if ( x != 1 ). A razão para esse método obscuro pode ser que técnicas de manipulação de bits anteriores foram usadas e talvez sejam usadas em outros locais do programa.

Paulo
fonte
8

^é bit a bit xor operatorem c. No seu caso, x é xado com 1. por exemplo, xtem o valor 10, então 10d ^ 1d ===> 1010b ^ 0001b = 1011b, 1011b == 11da condição se torna verdadeira.

Chinna
fonte
Erro de digitação na sua resposta. 10 != 1010
perfil completo de Fiddling Bits
@BitFiddlingCodeMonkey: erro de digitação no seu comentário:10 (decimal) == 1010 (binary)
Paul R
6
@PaulR Como é que alguém sabe o que é decimal e o que é binário se você não coloca um bou algo lá?
Fiddling Bits
0b1010 não seria a maneira pitônica de fazer as coisas?
TankorSmash
8

O teste bit a bit parece ser uma ofuscação deliberada, mas se os dados subjacentes são dados corporativos de um sistema de mainframe IBM, pode ser que simplesmente o código tenha sido escrito para refletir a documentação original. Os formatos de dados da IBM remontam à década de 1960 e frequentemente codificam sinalizadores como bits únicos em uma palavra para economizar armazenamento. À medida que os formatos foram modificados, os bytes do sinalizador foram adicionados no final dos registros existentes para manter a compatibilidade com versões anteriores. A documentação para um registro SMF, por exemplo, pode mostrar o código da linguagem assembly para testar três bits individuais em três palavras diferentes em um único registro para decidir que os dados eram um arquivo de entrada. Eu sei muito menos sobre as informações internas do TCP / IP, mas você também pode encontrar sinalizadores de bits.

Indefinido
fonte
7

O operador ^ é o bit a bit-xor (consulte &, |). O resultado para um par de bits é,

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

Então a expressão,

( x ^ 0x1 )

inverte / vira o 0º bit de x (deixando outros bits inalterados).

Considere se x pode ter valores além de 0x0 e 0x1? Quando x é um campo de bit único, ele pode ter apenas valores 0x0 e 0x1, mas quando x é um int (char / short / long / etc), os bits além do bit0 podem afetar o resultado da expressão.

A expressão fornecida permite que os bits ao lado do bit0 afetem o resultado,

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

Que tem uma veracidade equivalente como essa expressão (mais simples),

if ( x ^ 0x1 )

Observe que essa expressão examinaria apenas o bit0,

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

Portanto, a expressão apresentada realmente combina duas verificações de expressão,

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

O autor pretendia verificar apenas o bit0 e pretendia usar essa expressão,

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

Ou o autor pretendia obter os valores para bit1-bitN e xor do bit0?

ChuckCottrill
fonte
7

Estou adicionando uma nova resposta porque ninguém realmente explicou como obter a resposta intuitivamente.

O inverso de +é -.
O inverso de ^é^ .

Como você resolve 0 != x - 1para x? Você + 1para os dois lados: 0 + 1 != x - 1 + 11 != x.
Como você resolve 0 != x ^ 1para x? Você ^ 1para os dois lados: 0 ^ 1 != x ^ 1 ^ 11 != x.

user541686
fonte
6

Eu acho que existem outros bits ou valores de campo de bits x, e isso pretende testar se apenas o bit de ordem inferior está definido. No contexto, eu acho que esse é o padrão e, portanto, a codificação deste e de algunsm (provavelmente mais caros de codificar) podem ser ignorados, porque ambos devem ser o valor padrão, inicializados em um construtor ou similar.

De alguma forma, o decodificador deve ser capaz de inferir que esses valores estão ausentes. Se eles estão no final de alguma estrutura, ela pode ser comunicada por um lengthvalor sempre presente.

Ed Staub
fonte
4

O XOR é útil na enumeração de sinalizador C #. Para remover o sinalizador único do valor enum, é necessário usar o operador xor (consulte aqui )

Exemplo:

[Flags]
enum FlagTest { None 0x0, Test1 0x1, Test2 0x2, Test3 0x4}

FlagTest test = FlagTest.Test2 | FlagTest.Test3;
Console.WriteLine(test); //Out: FlagTest.Test2 | FlagTest.Test3
test = test ^ FlagTest.Test2;
Console.WriteLine(test); //Out: FlagTest.Test3
Igrek.
fonte
Mas a pergunta é sobre C ++, C # não
theDmi
@ theDmi: MAS TAMBÉM sobre manipulação de bit e máscara de bit. A enumeração de bandeira em C # é definitivamente sobre bitmask.
Igrek.
Além disso, também pode ser útil em C ++. Veja: Pilha 1 e Pilha 2
Igrek.
Essa resposta parece não ter nada a ver com a pergunta original, exceto alguma discussão sobre o operador XOR bit a bit?
Paul R
4

Existem muitas respostas boas, mas eu gosto de pensar de uma maneira mais simples.

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

Em primeiro lugar. Uma declaração if só é falsa se o argumento for zero. Isso significa que comparar diferente de zero é inútil.

if ( a != 0 );
// Same as
if ( a );

Então isso nos deixa com:

if ( x ^ 0x1 );

Um XOR com um. O que um XOR faz é essencialmente detectar bits diferentes. Portanto, se todos os bits forem iguais, ele retornará 0. Como 0 é falso, o único momento em que retornará falso é se todos os bits forem iguais. Portanto, será falso se os argumentos forem os mesmos, verdadeiro se forem diferentes ... assim como o operador não é igual a .

if ( x != 0x1 );

De fato, a única diferença entre os dois é que !=retornará 0 ou 1, enquanto ^retornará qualquer número, mas a verdade do resultado será sempre a mesma. Uma maneira fácil de pensar sobre isso é.

(b != c) === !!(b ^ c) // for all b and c

A "simplificação" final está convertendo 0x1para decimal, que é 1. Portanto, sua declaração é equivalente a:

if ( x != 1 )
Kevin Cox
fonte
1

^ é um operador XOR bit a bit

Se x = 1

          00000001   (x)       (decimal 1)
          00000001   (0x1)     (decimal 1)
XOR       00000000   (0x0)     (decimal 0)

aqui 0 == (x ^ 0x1)

Se x = 0

          00000000   (x)       (decimal 0)
          00000001   (0x1)     (decimal 1)
XOR       00000001   (0x1)     (decimal 0)

aqui 0! = (x ^ 0x1)

A tabela verdade de a xor b:

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

O código significa simplesmente

akbar ali
fonte
6
É realmente necessário postar mais uma resposta duplicada?
Mysticial
2
você pode dizer outra resposta, mas não duplicar. desculpe se você se importa
akbar ali 22/12/13
1

A técnica padrão que pode estar sendo usada, aqui, é repetir um idioma como aparece no contexto circundante para maior clareza, em vez de ofuscá-lo, substituindo-o por um idioma que é aritmeticamente mais simples, mas sem sentido no contexto.

O código circundante pode fazer referências frequentes (x ^ 1)ou o teste pode estar perguntando "se o bit 0 era o contrário, essa máscara de bits estaria vazia?".

Dado que a condição faz com que algo seja encode()editado, pode ser que, no contexto, o estado padrão do bit 0 tenha sido invertido por outros fatores, e precisamos apenas codificar informações extras se algum dos bits se desviar do padrão (normalmente zero) )

Se você tira a expressão do contexto e pergunta o que ela faz, ignora a intenção subjacente. Você também pode examinar a saída do assembly do compilador e ver que ele simplesmente faz uma comparação direta de igualdade com 1.

sh1
fonte
0

Como vejo as respostas até agora, falta uma regra simples para lidar com XORs. Sem entrar em detalhes o que ^e 0xmédia (e if, e !=etc), a expressão 0 != (x^1)pode ser reformulado como segue usando o fato de que (a^a)==0:

0 != (x^1) <=> [xor left and right side by 1]
(0^1) != (x^1^1) <=>
1 != x
Serge Rogatch
fonte