Bitweise und Schiebeoperatoren: C#-Referenz

Zu den bitweisen Operatoren und Schiebeoperatoren zählen das unäre bitweise Komplement, die binäre Links- und Rechtsverschiebung, die vorzeichenlose Rechtsverschiebung sowie die binären logischen AND-, OR- und XOR-Operatoren. Diese Operanden verwenden Operanden der integralen numerischen Typen oder des char-Typs.

Diese Operatoren werden für die Typen int, uint, long und ulong definiert. Wenn beide Operanden andere integrale Typen aufweisen (sbyte, byte, short, ushort oder char), werden ihre Werte in den Typ int konvertiert. Hierbei handelt es sich auch um den Ergebnistyp einer Operation. Wenn die Operanden abweichende integrale Typen aufweisen, werden ihre Werte in den enthaltenden integralen Typ konvertiert, der am besten geeignet ist. Weitere Informationen finden Sie im Abschnitt Numerische Heraufstufungen der Spezifikation für die Sprache C#. Zusammengesetzte arithmetischen Operatoren (z. B. >>=), konvertieren ihre Argumente nicht in int oder weisen den Ergebnistyp als int aus.

Die Operatoren &, | und ^ werden auch für Operanden des bool-Typs definiert. Weitere Informationen finden Sie unter Logische boolesche Operatoren.

Bitweise und Schiebeoperationen verursachen niemals Überläufe und führen sowohl in geprüften als auch in ungeprüften Kontexten zu identischen Ergebnissen.

Bitweiser Komplementoperator ~

Mit dem Operator ~ wird ein bitweises Komplement seines Operanden erzeugt, indem jedes Bit umgekehrt wird:

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

Sie können das Symbol ~ auch verwenden, um Finalizers zu deklarieren. Weitere Informationen finden Sie unter Finalizer.

Operator für Linksverschiebung <<

Mit dem Operator << wird der linke Operand um die Anzahl von Bits nach links verschoben, die durch den rechten Operanden angegeben wird. Informationen dazu, wie der rechte Operand die Bitanzahl für die Verschiebung definiert, finden Sie im Abschnitt Anzahl für die Verschiebung durch Schiebeoperatoren.

Bei der Operation zum Verschieben nach links werden die hohen Bits, die außerhalb des Bereichs des Ergebnistyps liegen, verworfen, und die niedrigen leeren Bitpositionen werden auf null festgelegt. Dies ist im folgenden Beispiel dargestellt:

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

Da die Verschiebeoperatoren nur für die Typen int, uint, long und ulong definiert werden, enthält das Ergebnis einer Operation immer mindestens 32 Bit. Wenn der linke Operand einen abweichenden integralen Typ aufweist (sbyte, byte, short, ushort oder char), wird sein Wert in den Typ int konvertiert. Dies ist im folgenden Beispiel dargestellt:

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 für Rechtsverschiebung >>

Mit dem Operator >> wird der linke Operand um die Anzahl von Bits nach rechts verschoben, die durch den rechten Operanden angegeben wird. Informationen dazu, wie der rechte Operand die Bitanzahl für die Verschiebung definiert, finden Sie im Abschnitt Anzahl für die Verschiebung durch Schiebeoperatoren.

Bei der Operation zum Verschieben nach rechts werden die niedrigen Bits verworfen. Dies ist im folgenden Beispiel dargestellt:

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

Die höheren leeren Bitpositionen werden basierend auf dem Typ des linken Operanden wie folgt festgelegt:

  • Wenn der linke Operand vom Typ int oder long ist, führt der Operator zur Rechtsverschiebung eine arithmetische Verschiebung durch: Der Wert des Bits mit dem höchsten Stellenwert (MSB, „most significant bit“) des linken Operanden wird auf die hohen leeren Bitpositionen übertragen. Die hohen leeren Bitpositionen werden daher auf 0 festgelegt, wenn der linke Operand nicht negativ ist, bzw. auf 1, wenn der linke Operand negativ ist.

    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
    
  • Wenn der linke Operand vom Typ uint oder ulong ist, führt der Operator zur Rechtsverschiebung eine logische Verschiebung durch: Die hohen leeren Bitpositionen werden immer auf 0 (null) festgelegt.

    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
    

Hinweis

Verwenden Sie den Operator für vorzeichenlose Rechtsverschiebung, um eine logische Verschiebung für Operanden von vorzeichenbehafteten Ganzzahltypen durchzuführen. Dies ist der Umwandlung eines linken Operanden in einen vorzeichenlosen Typ und der anschließenden Umwandlung des Ergebnisses einer Verschiebeoperation zurück in einen vorzeichenbehafteten Typ vorzuziehen.

Operator für vorzeichenlose Rechtsverschiebung (>>>)

Mit dem in C# 11 und höher verfügbaren Operator >>> wird der linke Operand um die Anzahl von Bits nach rechts verschoben, die durch den rechten Operanden angegeben wird. Informationen dazu, wie der rechte Operand die Bitanzahl für die Verschiebung definiert, finden Sie im Abschnitt Anzahl für die Verschiebung durch Schiebeoperatoren.

Der >>>-Operator führt immer eine logische Verschiebung durch. Das heißt, die höherwertigen leeren Bitpositionen werden unabhängig vom Typ des linken Operanden immer auf 0 festgelegt. Der >>-Operator führt eine arithmetische Verschiebung durch (d. h. der Wert des höchstwertigen Bits wird an die höherwertigen leeren Bitpositionen weitergegeben), wenn der linke Operand einen vorzeichenbehafteten Typ aufweist. Das folgende Beispiel veranschaulicht den Unterschied zwischen den Operatoren >> und >>> für einen negativen linken Operanden:

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

Logischer AND-Operator &

Mit dem Operator & wird „bitweises logisches UND“ für die ganzzahligen Operanden berechnet:

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

Für bool-Operanden berechnet der &-Operator das logische UND für die Operanden. Der unäre &-Operator ist der address-of-Operator.

Logischer exklusiver OR-Operator: ^

Mit dem Operator ^ wird „bitweises logisches exklusives ODER“, auch als „bitweises logisches XOR“ bezeichnet, seiner ganzzahligen Operanden berechnet:

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

Für bool-Operanden berechnet der ^-Operator das logische exklusive ODER für die Operanden.

Logischer OR-Operator: |

Mit dem Operator | wird „bitweises logisches ODER“ der ganzzahligen Operanden berechnet:

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

Für bool-Operanden berechnet der |-Operator das logische ODER für die Operanden.

Verbundzuweisung

Bei einem binären Operator op entspricht ein Verbundzuweisungsausdruck der Form

x op= y

für die folgende Syntax:

x = x op y

außer dass x nur einmal überprüft wird.

Im folgenden Beispiel wird die Verwendung von Verbundzuweisungen mit bitweisen und Schiebeoperatoren veranschaulicht:

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

Aufgrund von numerischen Höherstufungen kann das Ergebnis der Operation op ggf. nicht implizit in den Typ T von x konvertiert werden. In diesem Fall gilt Folgendes: Wenn op ein vordefinierter Operator ist und das Ergebnis der Operation explizit in den Typ T von x konvertiert werden kann, entspricht ein Verbundzuweisungsausdruck der Form x op= y dem Ausdruck x = (T)(x op y). Der einzige Unterschied ist, dass x nur einmal ausgewertet wird. Das folgende Beispiel veranschaulicht dieses Verhalten:

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

Operatorrangfolge

In der folgenden Liste sind die bitweisen und Schiebeoperatoren absteigend nach Rangfolge sortiert:

  • Bitweiser Komplementoperator ~
  • Schiebeoperatoren <<, >> und >>>
  • Logischer AND-Operator &
  • Logischer exklusiver OR-Operator ^
  • Logischer OR-Operator |

Verwenden Sie Klammern (), wenn Sie die Reihenfolge der Auswertung ändern möchten, die durch Operatorrangfolge festgelegt wird:

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

Die vollständige Liste der nach Rangfolgenebene sortierten C#-Operatoren finden Sie im Abschnitt Operatorrangfolge im Artikel C#-Operatoren.

Anzahl für die Verschiebung durch Schiebeoperatoren

Für die integrierten Schiebeoperatoren <<, >> und >>> muss der Typ des rechten Operanden int oder ein Typ sein, der über eine vordefinierte, implizite numerische Konvertierung nach int verfügt.

Für die Ausdrücke x << count, x >> count und x >>> count hängt die tatsächliche Anzahl der Verschiebungen wie folgt vom Typ von x ab:

  • Lautet der Typ von xint oder uint, wird die Verschiebungsanzahl durch die niedrigen fünf Bits des rechten Operanden definiert. Die Verschiebungsanzahl errechnet sich daher aus count & 0x1F (oder count & 0b_1_1111).

  • Lautet der Typ von xlong oder ulong, wird die Verschiebungsanzahl durch die niedrigen sechs Bits des rechten Operanden definiert. Die Verschiebungsanzahl errechnet sich daher aus count & 0x3F (oder count & 0b_11_1111).

Das folgende Beispiel veranschaulicht dieses Verhalten:

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

Hinweis

Wie im vorherigen Beispiel gezeigt wird, kann das Ergebnis eines Verschiebungsvorgangs nicht 0 (Null) sein, auch wenn der Wert des rechten Operanden größer ist als die Anzahl der Bits im linken Operanden.

Logische Enumerationsoperatoren

Die Operatoren ~, &, | und ^ werden auch von jedem Enumerationstyp unterstützt. Für Operanden mit dem gleichen Enumerationstyp wird ein logischer Vorgang für die entsprechenden Werte des zugrunde liegenden integralen Typs durchgeführt. Für alle x- und y-Elemente des Enumerationstyps T mit dem zugrunde liegenden Typ U führt der Ausdruck x & y zum gleichen Ergebnis wie der Ausdruck (T)((U)x & (U)y).

Normalerweise verwenden Sie bitweise logische Operatoren mit einem Enumerationstyp, der mit dem Flags-Attribut definiert wird. Weitere Informationen finden Sie im Abschnitt Enumerationstypen als Bitflags des Artikels Enumerationstypen.

Operatorüberladbarkeit

Ein benutzerdefinierter Typ kann die Operatoren ~, <<, >>, >>>, &, | und ^überladen. Wenn ein binärer Operator überladen ist, wird der zugehörige Verbundzuweisungsoperator implizit auch überladen. Ein benutzerdefinierter Typ kann einen Verbundzuweisungsoperator nicht explizit überladen.

Wenn ein benutzerdefinierter Typ T den Operator <<, >> oder >>> überlädt, muss der Typ des linken Operanden T sein. In C# 10 und früher muss der Typ des rechten Operanden int sein. Ab C# 11 kann der Typ des rechten Operanden eines überladenen Schiebeoperators ein beliebiger Typ sein.

C#-Sprachspezifikation

Weitere Informationen finden Sie in den folgenden Abschnitten der C#-Sprachspezifikation:

Weitere Informationen