Operatory bitowe i shift (odwołanie w C#)

Operatory bitowe i przesunięcia obejmują jednoargumentowe uzupełnianie bitowe, binarne przesunięcie w lewo i w prawo, niepodpisane przesunięcie w prawo oraz binarne operatory logiczne AND, OR oraz wyłączne operatory OR. Te operandy przyjmują operandy całkowitych typów liczbowych lub typu char .

Te operatory są definiowane dla inttypów , uint, longi ulong . Gdy oba operandy mają inne typy całkowite (sbyte, byte, short, ushortlub char), ich wartości są konwertowane na int typ, który jest również typem wyniku operacji. Gdy operandy mają różne typy całkowite, ich wartości są konwertowane na najbliższy typ całkowity. Aby uzyskać więcej informacji, zobacz sekcję Promocje liczbowe specyfikacji języka C#. Operatory złożone (takie jak >>=) nie konwertują argumentów na int lub mają typ wyniku jako int.

Operatory &, |i ^ są również zdefiniowane dla operandów bool typu. Aby uzyskać więcej informacji, zobacz Operatory logiczne logiczne.

Operacje bitowe i przesunięcia nigdy nie powodują przepełnienia i generują te same wyniki w kontekstach zaewidencjonowanych i niezaznaczonej .

Operator uzupełniania bitowego ~

Operator ~ tworzy bitowe uzupełnienie operandu przez odwrócenie każdego bitu:

uint a = 0b_0000_1111_0000_1111_0000_1111_0000_1100;
uint b = ~a;
Console.WriteLine(Convert.ToString(b, toBase: 2));
// Output:
// 11110000111100001111000011110011

Możesz również użyć symbolu ~ , aby zadeklarować finalizatory. Aby uzyskać więcej informacji, zobacz Finalizatory.

Operator przesunięcia w lewo <<

Operator << przesuwa lewy operand po lewej stronie przez liczbę bitów zdefiniowanych przez operand po prawej stronie. Aby uzyskać informacje na temat sposobu, w jaki operand po prawej stronie definiuje liczbę zmian, zobacz sekcję Shift count of the shift operators (Liczba shift operatorów przesunięcia).

Operacja przesunięcia w lewo odrzuca bity o wysokiej kolejności, które znajdują się poza zakresem typu wyniku i ustawia puste pozycje bitów o niskiej kolejności na zero, jak pokazano w poniższym przykładzie:

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

Ponieważ operatory przesunięcia są zdefiniowane tylko dla inttypów , , longuinti ulong , wynik operacji zawsze zawiera co najmniej 32 bity. Jeśli operand po lewej stronie ma inny typ całkowity (sbyte, byte, , shortushortlub char), jego wartość jest konwertowana na int typ, jak pokazano w poniższym przykładzie:

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

Operator przesunięcia w prawo >>

Operator >> przesuwa lewy operand w prawo przez liczbę bitów zdefiniowanych przez operand po prawej stronie. Aby uzyskać informacje na temat sposobu, w jaki operand po prawej stronie definiuje liczbę zmian, zobacz sekcję Shift count of the shift operators (Liczba shift operatorów przesunięcia).

Operacja przesunięcia po prawej stronie odrzuca bity o niskiej kolejności, jak pokazano w poniższym przykładzie:

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

Puste pozycje bitów o wysokiej kolejności są ustawiane na podstawie typu operandu po lewej stronie w następujący sposób:

  • Jeśli operand po lewej stronie ma typ int lub long, operator przesunięcia prawego wykonuje przesunięcie arytmetyczne : wartość najbardziej znaczącego bitu (bit znaku) operandu po lewej stronie jest propagowana do pustych pozycji bitów o wysokiej kolejności. Oznacza to, że puste pozycje bitów o wysokiej kolejności są ustawione na zero, jeśli operand po lewej stronie nie jest ujemny i ustawiony na jeden, jeśli jest ujemny.

    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
    
  • Jeśli operand po lewej stronie ma typ uint lub ulong, operator przesunięcia w prawo wykonuje zmianę logiczną : puste pozycje bitów o wysokiej kolejności są zawsze ustawione na 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
    

Uwaga

Użyj operatora niepodpisanego przesunięcia prawego, aby wykonać przesunięcie logiczne na operandach podpisanych typów liczb całkowitych. Jest to preferowane rzutowanie operandu po lewej stronie do typu niepodpisanego, a następnie rzutowanie wyniku operacji przesunięcia z powrotem do typu podpisanego.

Operator niepodpisanego przesunięcia w prawo >>>

Dostępny w języku C# 11 lub nowszym >>> operator przesuwa lewy operand w prawo przez liczbę bitów zdefiniowanych przez operand po prawej stronie. Aby uzyskać informacje na temat sposobu, w jaki operand po prawej stronie definiuje liczbę zmian, zobacz sekcję Shift count of the shift operators (Liczba shift operatorów przesunięcia).

Operator >>> zawsze wykonuje zmianę logiczną . Oznacza to, że puste pozycje bitów o wysokiej kolejności są zawsze ustawione na zero, niezależnie od typu operandu po lewej stronie. Operator >> wykonuje przesunięcie arytmetyczne (czyli wartość najbardziej znaczącego bitu jest propagowana do pustych pozycji bitów o wysokiej kolejności), jeśli operand po lewej stronie jest typu ze znakiem. W poniższym przykładzie pokazano różnicę między operatorami >> i >>> dla ujemnego operandu po lewej stronie:

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

Operator logiczny AND i

Operator & oblicza bitową wartość logiczną AND swoich operandów całkowitych:

uint a = 0b_1111_1000;
uint b = 0b_1001_1101;
uint c = a & b;
Console.WriteLine(Convert.ToString(c, toBase: 2));
// Output:
// 10011000

W przypadku bool operandów & operator oblicza logiczne AND jego operandów. Operator jednoargumentowy & jest operatorem address-of.

Logiczny wyłączny operator OR ^

Operator ^ oblicza bitowo logiczne wyłączne OR, znane również jako bitowe logiczne XOR, jego operandów całkowitych:

uint a = 0b_1111_1000;
uint b = 0b_0001_1100;
uint c = a ^ b;
Console.WriteLine(Convert.ToString(c, toBase: 2));
// Output:
// 11100100

W przypadku bool operandów ^ operator oblicza logiczny wyłączny OR jego operandów.

Operator logiczny OR |

Operator | oblicza bitową wartość logiczną OR swoich operandów całkowitych:

uint a = 0b_1010_0000;
uint b = 0b_1001_0001;
uint c = a | b;
Console.WriteLine(Convert.ToString(c, toBase: 2));
// Output:
// 10110001

W przypadku bool operandów | operator oblicza logiczne OR jego operandów.

Przypisanie złożone

Dla operatora opbinarnego wyrażenie przypisania złożonego formularza

x op= y

jest równoważny

x = x op y

z wyjątkiem tego, że x jest obliczany tylko raz.

W poniższym przykładzie pokazano użycie przypisania złożonego z operatorami bitowym i 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}");

Ze względu na promocje liczbowe wynik op operacji może nie być niejawnie konwertowany na typ Tx. W takim przypadku, jeśli op jest wstępnie zdefiniowanym operatorem, a wynik operacji jest jawnie konwertowany na typ Tx, wyrażenie przypisania złożonego formularza x op= y jest równoważne x = (T)(x op y), z wyjątkiem tego, że x jest obliczany tylko raz. W poniższym przykładzie pokazano, że zachowanie:

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

Kolejność wykonywania działań

Następujące kolejności kolejności bitowych i przesunięć operatorów rozpoczynających się od najwyższego pierwszeństwa do najniższego:

  • Operator uzupełniania bitowego ~
  • Operatory <<shift , >>i >>>
  • Operator logiczny AND &
  • Logiczny wyłączny operator OR ^
  • Operator logiczny OR |

Użyj nawiasów, (), aby zmienić kolejność oceny narzuconej przez pierwszeństwo operatora:

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}");

Aby uzyskać pełną listę operatorów języka C# uporządkowanych według poziomu pierwszeństwa, zobacz sekcję Pierwszeństwo operatora w artykule Operatory języka C#.

Liczba zmian operatorów przesunięcia

Dla wbudowanych operatorów <<przesunięcia , >>i >>>, typ operandu po prawej stronie musi być int lub typ, który ma wstępnie zdefiniowaną niejawną konwersję liczbową na int.

x << countW przypadku wyrażeń , x >> counti x >>> count rzeczywista liczba zmian zależy od typu w x następujący sposób:

  • Jeśli typ to xint lub uint, liczba zmian jest definiowana przez pięć bitów o niskiej kolejności operandu po prawej stronie. Oznacza to, że liczba zmian jest obliczana z count & 0x1F (lub count & 0b_1_1111).

  • Jeśli typ to xlong lub ulong, liczba zmian jest definiowana przez sześć bitów o niskiej kolejności operandu po prawej stronie. Oznacza to, że liczba zmian jest obliczana z count & 0x3F (lub count & 0b_11_1111).

W poniższym przykładzie pokazano, że zachowanie:

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

Uwaga

Jak pokazano w poprzednim przykładzie, wynik operacji przesunięcia może być inny niż zero, nawet jeśli wartość operandu po prawej stronie jest większa niż liczba bitów w operandie po lewej stronie.

Operatory logiczne wyliczania

Operatory , , i ^ są również obsługiwane przez dowolny typ wyliczenia. |&~ W przypadku operandów tego samego typu wyliczenia operacja logiczna jest wykonywana na odpowiednich wartościach bazowego typu całkowitego. Na przykład w przypadku dowolnego x typu T i y wyliczenia z typem x & yUbazowym wyrażenie generuje ten sam wynik co (T)((U)x & (U)y) wyrażenie.

Zazwyczaj używasz operatorów logicznych bitowych z typem wyliczenia zdefiniowanym za pomocą atrybutu Flags . Aby uzyskać więcej informacji, zobacz sekcję Typy wyliczenia jako flagi bitowe artykułu Typy wyliczenia.

Przeciążenie operatora

Typ zdefiniowany przez użytkownika może przeciążać operatory ~, , <<, >>&>>>, , |i .^ Gdy operator binarny jest przeciążony, odpowiedni operator przypisania złożonego jest również niejawnie przeciążony. Typ zdefiniowany przez użytkownika nie może jawnie przeciążyć operatora przypisania złożonego.

Jeśli typ T zdefiniowany przez użytkownika przeciąża <<operator , >>lub >>> , typ operandu po lewej stronie musi mieć wartość T. W języku C# 10 i starszych typ operandu po prawej stronie musi mieć intwartość ; począwszy od języka C# 11, typ operandu po prawej stronie przeciążonego operatora przesunięcia może być dowolny.

specyfikacja języka C#

Aby uzyskać więcej informacji, zobacz następujące sekcje specyfikacji języka C#:

Zobacz też