Operadores bitwise e shift (referência C#)
Os operadores bitwise e shift incluem complemento bitwise unário, deslocamento binário para a esquerda e para a direita, deslocamento para a direita não assinado e os operadores lógicos binários E, OR e OR exclusivos. Esses operandos usam operandos dos tipos numéricos integrais ou do tipo char.
- Operador unário
~
(complemento bitwise) - Operadores binários
<<
(deslocamento para a esquerda),>>
(deslocamento para a direita) e>>>
(deslocamento para a direita não assinado) - Operadores binários
&
(AND lógico),|
(OR lógico) e^
(OR lógico exclusivo)
Esses operadores são definidos para os int
tipos , uint
, long
, e ulong
. Quando ambos os operandos são de outros tipos integrais (, , , ushort
, ou char
), seus valores são convertidos para o int
tipo, que também é o tipo de resultado de uma short
operação. byte
sbyte
Quando os operandos são de diferentes tipos integrais, seus valores são convertidos para o tipo integral contendo mais próximo. Para obter mais informações, consulte a seção Promoções numéricas da especificação da linguagem C#. Os operadores compostos (como >>=
) não convertem seus argumentos em int
ou têm o tipo de resultado como int
.
Os &
operadores , |
, e ^
também são definidos para operandos do bool
tipo. Para obter mais informações, consulte Operadores lógicos booleanos.
As operações bitwise e shift nunca causam estouro e produzem os mesmos resultados em contextos verificados e não verificados .
Operador de complemento Bitwise ~
O ~
operador produz um complemento bit a bit de seu operando invertendo cada bit:
uint a = 0b_0000_1111_0000_1111_0000_1111_0000_1100;
uint b = ~a;
Console.WriteLine(Convert.ToString(b, toBase: 2));
// Output:
// 11110000111100001111000011110011
Você também pode usar o ~
símbolo para declarar finalizadores. Para obter mais informações, consulte Finalizadores.
Operador de turno à esquerda <<
O <<
operador desloca seu operando esquerdo para a esquerda pelo número de bits definido pelo operando direito. Para obter informações sobre como o operando direito define a contagem de turnos, consulte a seção Contagem de turnos dos operadores de turno.
A operação de deslocamento para a esquerda descarta os bits de ordem alta que estão fora do intervalo do tipo de resultado e define as posições de bits vazios de ordem baixa como zero, como mostra o exemplo a seguir:
uint x = 0b_1100_1001_0000_0000_0000_0000_0001_0001;
Console.WriteLine($"Before: {Convert.ToString(x, toBase: 2)}");
uint y = x << 4;
Console.WriteLine($"After: {Convert.ToString(y, toBase: 2)}");
// Output:
// Before: 11001001000000000000000000010001
// After: 10010000000000000000000100010000
Como os operadores de turno são definidos apenas para os int
tipos , uint
, long
, e , o ulong
resultado de uma operação sempre contém pelo menos 32 bits. Se o operando esquerdo for de outro tipo integral (, , , ushort
, ou char
), seu valor será convertido para o int
tipo, como mostra o exemplo a short
seguir: byte
sbyte
byte a = 0b_1111_0001;
var b = a << 8;
Console.WriteLine(b.GetType());
Console.WriteLine($"Shifted byte: {Convert.ToString(b, toBase: 2)}");
// Output:
// System.Int32
// Shifted byte: 1111000100000000
Operador de turno à direita >>
O >>
operador desloca seu operando esquerdo para a direita pelo número de bits definido pelo operando direito. Para obter informações sobre como o operando direito define a contagem de turnos, consulte a seção Contagem de turnos dos operadores de turno.
A operação de deslocamento para a direita descarta os bits de ordem baixa, como mostra o exemplo a seguir:
uint x = 0b_1001;
Console.WriteLine($"Before: {Convert.ToString(x, toBase: 2), 4}");
uint y = x >> 2;
Console.WriteLine($"After: {Convert.ToString(y, toBase: 2).PadLeft(4, '0'), 4}");
// Output:
// Before: 1001
// After: 0010
As posições de bit vazio de ordem alta são definidas com base no tipo do operando esquerdo da seguinte maneira:
Se o operando esquerdo for do tipo
int
oulong
, o operador de deslocamento para a direita executa um deslocamento aritmético : o valor do bit mais significativo (o bit de sinal) do operando esquerdo é propagado para as posições de bit vazio de ordem alta. Ou seja, as posições de bit vazio de ordem alta são definidas como zero se o operando esquerdo não for negativo e definidas como uma se for negativa.int a = int.MinValue; Console.WriteLine($"Before: {Convert.ToString(a, toBase: 2)}"); int b = a >> 3; Console.WriteLine($"After: {Convert.ToString(b, toBase: 2)}"); // Output: // Before: 10000000000000000000000000000000 // After: 11110000000000000000000000000000
Se o operando esquerdo for do tipo
uint
ouulong
, o operador de deslocamento para a direita executa um deslocamento lógico : as posições de bit vazio de ordem alta são sempre definidas como zero.uint c = 0b_1000_0000_0000_0000_0000_0000_0000_0000; Console.WriteLine($"Before: {Convert.ToString(c, toBase: 2), 32}"); uint d = c >> 3; Console.WriteLine($"After: {Convert.ToString(d, toBase: 2).PadLeft(32, '0'), 32}"); // Output: // Before: 10000000000000000000000000000000 // After: 00010000000000000000000000000000
Nota
Use o operador de deslocamento à direita não assinado para executar um deslocamento lógico em operandos de tipos inteiros assinados. Isso é preferível a transmitir um operando esquerdo para um tipo não assinado e, em seguida, converter o resultado de uma operação de deslocamento de volta para um tipo assinado.
Operador de turno à direita não assinado >>>
Disponível em C# 11 e posterior, o >>>
operador desloca seu operando esquerdo para a direita pelo número de bits definido por seu operando direito. Para obter informações sobre como o operando direito define a contagem de turnos, consulte a seção Contagem de turnos dos operadores de turno.
O >>>
operador sempre executa um deslocamento lógico . Ou seja, as posições de bit vazio de ordem alta são sempre definidas como zero, independentemente do tipo de operando esquerdo. O >>
operador executa um deslocamento aritmético (ou seja, o valor do bit mais significativo é propagado para as posições de bit vazio de ordem alta) se o operando esquerdo for de um tipo assinado. O exemplo a seguir demonstra a diferença entre >>
e >>>
operadores para um operando esquerdo negativo:
int x = -8;
Console.WriteLine($"Before: {x,11}, hex: {x,8:x}, binary: {Convert.ToString(x, toBase: 2), 32}");
int y = x >> 2;
Console.WriteLine($"After >>: {y,11}, hex: {y,8:x}, binary: {Convert.ToString(y, toBase: 2), 32}");
int z = x >>> 2;
Console.WriteLine($"After >>>: {z,11}, hex: {z,8:x}, binary: {Convert.ToString(z, toBase: 2).PadLeft(32, '0'), 32}");
// Output:
// Before: -8, hex: fffffff8, binary: 11111111111111111111111111111000
// After >>: -2, hex: fffffffe, binary: 11111111111111111111111111111110
// After >>>: 1073741822, hex: 3ffffffe, binary: 00111111111111111111111111111110
Operador lógico AND &
O &
operador calcula a lógica bit a bit E de seus operandos integrais:
uint a = 0b_1111_1000;
uint b = 0b_1001_1101;
uint c = a & b;
Console.WriteLine(Convert.ToString(c, toBase: 2));
// Output:
// 10011000
Para bool
operandos, o operador calcula &
o And lógico de seus operandos. O operador unário &
é o endereço do operador.
Operador OR exclusivo lógico ^
O ^
operador calcula o OR lógico exclusivo bitwise, também conhecido como XOR lógico bitwise, de seus operandos integrais:
uint a = 0b_1111_1000;
uint b = 0b_0001_1100;
uint c = a ^ b;
Console.WriteLine(Convert.ToString(c, toBase: 2));
// Output:
// 11100100
Para bool
operandos, o operador calcula ^
o OR lógico exclusivo de seus operandos.
Operador lógico OR |
O |
operador calcula o OR lógico bit a bit de seus operandos integrais:
uint a = 0b_1010_0000;
uint b = 0b_1001_0001;
uint c = a | b;
Console.WriteLine(Convert.ToString(c, toBase: 2));
// Output:
// 10110001
Para bool
operandos, o operador calcula |
o OR lógico de seus operandos.
Atribuição composta
Para um operador op
binário , uma expressão de atribuição composta do formulário
x op= y
é equivalente a
x = x op y
só que só é x
avaliado uma vez.
O exemplo a seguir demonstra o uso da atribuição composta com operadores bitwise e shift:
uint INITIAL_VALUE = 0b_1111_1000;
uint a = INITIAL_VALUE;
a &= 0b_1001_1101;
Display(a); // output: 10011000
a = INITIAL_VALUE;
a |= 0b_0011_0001;
Display(a); // output: 11111001
a = INITIAL_VALUE;
a ^= 0b_1000_0000;
Display(a); // output: 01111000
a = INITIAL_VALUE;
a <<= 2;
Display(a); // output: 1111100000
a = INITIAL_VALUE;
a >>= 4;
Display(a); // output: 00001111
a = INITIAL_VALUE;
a >>>= 4;
Display(a); // output: 00001111
void Display(uint x) => Console.WriteLine($"{Convert.ToString(x, toBase: 2).PadLeft(8, '0'), 8}");
Devido a promoções numéricas, o op
resultado da operação pode não ser implicitamente conversível para o tipo T
de x
. Nesse caso, se op
for um operador predefinido e o resultado da operação for explicitamente conversível para o tipo T
de , uma expressão de x
atribuição composta do formulário x op= y
é equivalente a x = (T)(x op y)
, exceto que x
é avaliada apenas uma vez. O exemplo a seguir demonstra esse comportamento:
byte x = 0b_1111_0001;
int b = x << 8;
Console.WriteLine($"{Convert.ToString(b, toBase: 2)}"); // output: 1111000100000000
x <<= 8;
Console.WriteLine(x); // output: 0
Precedência dos operadores
A lista a seguir ordena os operadores bit a bit e desloca a partir da precedência mais alta para a mais baixa:
- Operador de complemento Bitwise
~
- Operadores de turnos
<<
,>>
e>>>
- Operador lógico E
&
- Operador lógico exclusivo das RUP
^
- Operador lógico OR
|
Use parênteses, ()
, para alterar a ordem de avaliação imposta pela precedência do operador:
uint a = 0b_1101;
uint b = 0b_1001;
uint c = 0b_1010;
uint d1 = a | b & c;
Display(d1); // output: 1101
uint d2 = (a | b) & c;
Display(d2); // output: 1000
void Display(uint x) => Console.WriteLine($"{Convert.ToString(x, toBase: 2), 4}");
Para obter a lista completa de operadores C# ordenados por nível de precedência, consulte a seção Precedência do operador do artigo Operadores C#.
Contagem de turnos dos operadores de turno
Para o x << count
, x >> count
e x >>> count
expressões, a contagem real de turnos depende do tipo da x
seguinte forma:
Se o tipo de é
int
ouuint
, a contagem dex
turnos é definida pelos cinco bits de ordem baixa do operando direito. Ou seja, a contagem de turnos é calculada a partir decount & 0x1F
(oucount & 0b_1_1111
).Se o tipo de é
long
ouulong
, a contagem dex
turnos é definida pelos seis bits de ordem baixa do operando direito. Ou seja, a contagem de turnos é calculada a partir decount & 0x3F
(oucount & 0b_11_1111
).
O exemplo a seguir demonstra esse comportamento:
int count1 = 0b_0000_0001;
int count2 = 0b_1110_0001;
int a = 0b_0001;
Console.WriteLine($"{a} << {count1} is {a << count1}; {a} << {count2} is {a << count2}");
// Output:
// 1 << 1 is 2; 1 << 225 is 2
int b = 0b_0100;
Console.WriteLine($"{b} >> {count1} is {b >> count1}; {b} >> {count2} is {b >> count2}");
// Output:
// 4 >> 1 is 2; 4 >> 225 is 2
int count = -31;
int c = 0b_0001;
Console.WriteLine($"{c} << {count} is {c << count}");
// Output:
// 1 << -31 is 2
Nota
Como mostra o exemplo anterior, o resultado de uma operação de deslocamento pode ser diferente de zero mesmo se o valor do operando direito for maior do que o número de bits no operando esquerdo.
Operadores lógicos de enumeração
Os ~
operadores , &
, |
e ^
também são suportados por qualquer tipo de enumeração . Para operandos do mesmo tipo de enumeração, uma operação lógica é executada nos valores correspondentes do tipo integral subjacente. Por exemplo, para qualquer x
e y
de um tipo T
de enumeração com um tipo U
subjacente , a x & y
expressão produz o mesmo resultado que a (T)((U)x & (U)y)
expressão.
Normalmente, você usa operadores lógicos bit a bit com um tipo de enumeração definido com o atributo Flags . Para obter mais informações, consulte a seção Tipos de enumeração como sinalizadores de bits do artigo Tipos de enumeração.
Capacidade de sobrecarga do operador
Um tipo definido pelo usuário pode sobrecarregar os ~
operadores , <<
, , >>
>>>
, |
&
, , e ^
. Quando um operador binário está sobrecarregado, o operador de atribuição composto correspondente também é implicitamente sobrecarregado. Um tipo definido pelo usuário não pode sobrecarregar explicitamente um operador de atribuição composta.
Se um tipo T
definido pelo usuário sobrecarregar o <<
, >>
ou >>>
operador, o tipo do operando esquerdo deve ser T
. Em C# 10 e anteriores, o tipo do operando direito deve ser int
; começando com C# 11, o tipo do operando direito de um operador de turno sobrecarregado pode ser qualquer.
Especificação da linguagem C#
Para obter mais informações, consulte as seguintes seções da especificação da linguagem C#:
- Operador de complemento Bitwise
- Operadores de turnos
- Operadores lógicos
- Atribuição composta
- Promoções numéricas
- C# 11 - Requisitos de mudança relaxados
- C# 11 - Operador lógico de deslocamento para a direita