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.
- Unärer Operator
~
(bitweises Komplement) - Binäre Operatoren:
<<
(Linksverschiebung),>>
(Rechtsverschiebung) und>>>
(vorzeichenlose Rechtsverschiebung) - Binäre Operatoren
&
(logisches UND),|
(logisches ODER) und^
(logisches exklusives ODER)
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
oderlong
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
oderulong
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 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
x
int
oderuint
, wird die Verschiebungsanzahl durch die niedrigen fünf Bits des rechten Operanden definiert. Die Verschiebungsanzahl errechnet sich daher auscount & 0x1F
(odercount & 0b_1_1111
).Lautet der Typ von
x
long
oderulong
, wird die Verschiebungsanzahl durch die niedrigen sechs Bits des rechten Operanden definiert. Die Verschiebungsanzahl errechnet sich daher auscount & 0x3F
(odercount & 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:
- Bitweiser Komplementoperator
- Shift operators (Schiebeoperatoren)
- Logical operators (Logische Operatoren)
- Verbundzuweisung
- Numerische Heraufstufungen
- C# 11: Gelockerte Anforderungen für Schiebeoperatoren
- C# 11: Operator für logische Rechtsverschiebung