Dela via


Bitvis och skiftoperatorer (C#-referens)

Bitvis- och skiftoperatorerna omfattar oanvänd bitvis komplement, binär vänster- och högerförskjutning, osignerat högerskift och binära logiska AND-, OR- och exklusiva OR-operatorer. Dessa operander tar operander av de integrerade numeriska typernaeller teckentypen .

Dessa operatorer definieras för typerna int, uint, longoch ulong . När båda operanderna är av andra integraltyper (sbyte, , shortbyte, ushorteller char), konverteras deras värden till int typen, vilket också är resultattypen för en åtgärd. När operander är av olika integraltyper konverteras deras värden till den närmaste innehåller integraltyp. Mer information finns i avsnittet Numeriska kampanjer i C#-språkspecifikationen. De sammansatta operatorerna (till exempel >>=) konverterar inte sina argument till int eller har resultattypen som int.

Operatorerna &, |och ^ definieras också för operander av typen bool . Mer information finns i Booleska logiska operatorer.

Bitvis och skiftåtgärder orsakar aldrig spill och ger samma resultat i markerade och omarkerade kontexter.

Bitvis komplementoperator ~

Operatorn ~ genererar ett bitvis komplement av sin operand genom att vända varje bit:

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

Du kan också använda symbolen ~ för att deklarera finalizers. Mer information finns i Finalizers.

Vänster skiftoperator <<

Operatorn << flyttar sin vänstra operande till vänster efter antalet bitar som definieras av dess högra operande. Information om hur den högra operanden definierar skiftantalet finns i avsnittet Skiftantal för skiftoperatorer .

Vänsterskiftsåtgärden tar bort de högordningsbitar som ligger utanför resultattypens intervall och anger tomma bitpositioner i låg ordning till noll, vilket visas i följande exempel:

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

Eftersom skiftoperatorerna endast definieras för typerna int, uint, longoch ulong innehåller resultatet av en åtgärd alltid minst 32 bitar. Om den vänstra operanden är av en annan integrerad typ (sbyte, , shortbyte, ushorteller char), konverteras dess värde till int typen, som följande exempel visar:

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

Högerskiftsoperator >>

Operatorn >> flyttar sin vänstra operande till höger med antalet bitar som definieras av dess högra operande. Information om hur den högra operanden definierar skiftantalet finns i avsnittet Skiftantal för skiftoperatorer .

Åtgärden right-shift tar bort lågordningsbitarna, vilket visas i följande exempel:

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

De tomma bitpositionerna i hög ordning anges baserat på typen av den vänstra operanden enligt följande:

  • Om den vänstra operanden är av typen int eller long, utför operatorn för höger skift ett aritmetiskt skift: värdet för den viktigaste biten (teckenbiten) för den vänstra operanden sprids till de tomma bitpositionerna i hög ordning. Det innebär att de tomma bitpositionerna i hög ordning är inställda på noll om den vänstra operanden inte är negativ och inställd på en om den är negativ.

    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
    
  • Om den vänstra operanden är av typen uint eller ulongutför operatorn right-shift ett logiskt skift: de tomma bitpositionerna i hög ordning är alltid inställda på noll.

    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
    

Kommentar

Använd den osignerade högerväxlingsoperatorn för att utföra ett logiskt skift på operander av signerade heltalstyper. Detta är att föredra för att casta en vänster operand till en osignerad typ och sedan gjuta resultatet av en skiftåtgärd tillbaka till en signerad typ.

Osignerad högerskiftsoperator >>>

Operatorn >>> är tillgänglig i C# 11 och senare och skiftar sin vänstra operande till höger efter antalet bitar som definieras av dess högra operand. Information om hur den högra operanden definierar skiftantalet finns i avsnittet Skiftantal för skiftoperatorer .

Operatorn >>> utför alltid ett logiskt skift. Det innebär att de tomma bitpositionerna i hög ordning alltid är inställda på noll, oavsett typ av vänster operand. Operatorn>>utför ett aritmetiskt skift (dvs. värdet för den viktigaste biten sprids till de tomma bitpositionerna i hög ordning) om den vänstra operanden är av en signerad typ. I följande exempel visas skillnaden mellan >> operatorerna och >>> för en negativ vänsteroperation:

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

Logisk AND-operator &

Operatorn & beräknar bitvis logiska OCH för dess integrerade operander:

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 operander beräknar operatorn & logiska OCH för sina operander. Den unary & operatorn är adress-of-operatorn.

Logisk exklusiv OR-operator ^

Operatorn ^ beräknar den bitvis logiska exklusiva OR, även kallad bitvis logisk XOR, för dess integrerade operander:

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 operander beräknar operatorn ^ den logiska exklusiva OR för sina operander.

Logisk OR-operator |

Operatorn | beräknar den bitvis logiska OR:en för dess integrerade operander:

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 operander beräknar operatorn | den logiska OR :en för dess operander.

Sammansatt tilldelning

För en binär operator op, ett sammansatt tilldelningsuttryck för formuläret

x op= y

motsvarar

x = x op y

förutom att x endast utvärderas en gång.

I följande exempel visas användningen av sammansatt tilldelning med bitvis operatorer och skiftoperatorer:

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

På grund av numeriska kampanjer kanske resultatet av op åtgärden inte implicit kan konverteras till typen Tx. I ett sådant fall, om op är en fördefinierad operator och resultatet av åtgärden uttryckligen kan konverteras till typen T av x, är ett sammansatt tilldelningsuttryck av formuläret x op= y likvärdigt med x = (T)(x op y), förutom att endast x utvärderas en gång. Följande exempel visar det beteendet:

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

Prioritet för operator

Följande lista beställer bitvis och skiftoperatorer från den högsta prioriteten till den lägsta:

  • Bitvis komplementoperator ~
  • Skiftoperatorer <<, >>och >>>
  • Logisk AND-operator &
  • Logisk exklusiv OR-operator ^
  • Logisk OR-operator |

Använd parenteser, (), för att ändra ordningen på utvärderingen som har införts av operatorprioriteten:

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

En fullständig lista över C#-operatorer ordnade efter prioritetsnivå finns i avsnittet Operatorprioriteten i artikeln C#-operatorer .

Skiftantal för skiftoperatorerna

För de inbyggda skiftoperatorerna <<, >>och >>>, måste typen av den högra operanden vara int eller en typ som har en fördefinierad implicit numerisk konvertering till int.

För uttrycken x << count, x >> countoch x >>> count beror det faktiska skiftantalet på typen av x på följande sätt:

  • Om typen av är eller definieras skiftantalet av fem bitar i den högra operanden med låg ordning.uintintx Det vill: skiftantalet beräknas från count & 0x1F (eller count & 0b_1_1111).

  • Om typen av x är long eller ulongdefinieras skiftantalet av den låga ordningen sex bitar av den högra operanden. Det vill: skiftantalet beräknas från count & 0x3F (eller count & 0b_11_1111).

Följande exempel visar det beteendet:

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

Kommentar

Som föregående exempel visar kan resultatet av en skiftåtgärd vara icke-noll även om värdet för den högra operanden är större än antalet bitar i den vänstra operanden.

Logiska uppräkningsoperatorer

Operatorerna , , och ^ stöds också av alla uppräkningstyper. |&~ För operander av samma uppräkningstyp utförs en logisk åtgärd på motsvarande värden för den underliggande integraltypen. För alla x och y av en uppräkningstyp T med en underliggande typ Ux & y ger uttrycket till exempel samma resultat som (T)((U)x & (U)y) uttrycket.

Du använder vanligtvis bitvis logiska operatorer med en uppräkningstyp som definieras med attributet Flaggor . Mer information finns i avsnittet Uppräkningstyper som bitflaggor i artikeln Uppräkningstyper .

Överlagring av operator

En användardefinierad typ kan överbelasta operatorerna ~, <<, >>, >>>, &, |och ^ . När en binär operator är överbelastad överbelastas även motsvarande sammansatta tilldelningsoperator implicit. En användardefinierad typ kan inte uttryckligen överbelasta en sammansatt tilldelningsoperator.

Om en användardefinierad typ T överbelastar operatorn <<, >>eller >>> måste typen av den vänstra operanden vara T. I C# 10 och tidigare måste typen av den högra operanden vara int. Från och med C# 11 kan typen av den högra operanden för en överbelastad skiftoperator vara valfri.

Språkspecifikation för C#

Mer information finns i följande avsnitt i C#-språkspecifikationen:

Se även