Bitenkénti és shift operátorok (C#-referencia)

A bitenkénti és a shift operátorok közé tartoznak a nem bitenkénti kiegészítések, a bináris bal és jobb eltolás, az aláíratlan jobb eltolás, valamint a bináris logikai ÉS, OR és kizárólagos VAGY operátorok. Ezek az operandusok az integrál numerikus típusok vagy a karaktertípus operandusait veszik fel.

Ezek az operátorok a int, uint, longés ulong típusokhoz vannak definiálva. Ha mindkét operandus más integrál típusú (sbyte, , , shortvagy charushort) típusú, byteakkor az értékek a int típusra lesznek konvertálva, ami szintén egy művelet eredménytípusa. Ha az operandusok különböző integráltípusok, az értékek a legközelebbi integrált típusra lesznek konvertálva. További információkért tekintse meg a C#-nyelv specifikációjának Numerikus promóciók szakaszát. Az összetett operátorok (például >>=) nem konvertálják az argumentumaikat int az eredménytípusra, vagy az eredmény típusa a következő int.

A &, |és ^ operátorok is definiálva vannak a bool típus operandusaihoz. További információ: Logikai operátorok.

A bitenkénti és a váltási műveletek soha nem okoznak túlcsordulást, és ugyanazokat az eredményeket eredményezik a ellenőrzött és a nem ellenőrzött környezetekben.

Bitenkénti komplementer operátor ~

Az ~ operátor az operandus bitenkénti kiegészítését állítja elő az egyes bitek megfordításával:

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

A szimbólummal ~ véglegesítőket is deklarálhat. További információ: Finalizers.

Bal shift operátor <<

Az << operátor a bal oldali operandusát balra tolja a jobb oldali operandus által meghatározott bitek számával. Arról, hogy a jobb oldali operandus hogyan határozza meg a műszakszámot, tekintse meg a shift operátorok szakaszának Shift count (Műszakok száma) szakaszát.

A bal oldali eltolásos művelet elveti az eredménytípus tartományán kívül eső nagy értékű biteket, és nullára állítja az alacsony sorrendű üres bitpozíciókat, ahogy az alábbi példa mutatja:

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

Mivel a shift operátorok csak a int, uint, longés ulong a típusok esetében vannak definiálva, a művelet eredménye mindig legalább 32 bitet tartalmaz. Ha a bal oldali operandus egy másik integráltípusú (sbyte, byte, , shortvagy charushort), akkor az értéke a típusra int lesz konvertálva, ahogyan az alábbi példa mutatja:

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

Jobb műszakos operátor >>

Az >> operátor a jobb oldali operandus által meghatározott bitek számával jobbra tolja el a bal oldali operandusát. Arról, hogy a jobb oldali operandus hogyan határozza meg a műszakszámot, tekintse meg a shift operátorok szakaszának Shift count (Műszakok száma) szakaszát.

A jobb oldali shift művelet elveti az alacsony sorrendű biteket, ahogy az alábbi példa is mutatja:

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

A magas sorrendű üres bitpozíciók a bal oldali operandus típusától függően vannak beállítva az alábbiak szerint:

  • Ha a bal oldali operandus típusa int vagy long, akkor a jobb műszak operátora aritmetikai eltolást hajt végre: a bal oldali operandus legjelentősebb bitjének (jelbitjének) értékét propagálja a rendszer a nagy sorrendű üres bitpozíciókba. Ez azt jelenti, hogy a magas sorrendű üres bitpozíciók nullára vannak állítva, ha a bal oldali operandus nem negatív, és negatív értékre van állítva.

    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
    
  • Ha a bal oldali operandus típusúuint, vagy ulongha a jobb oldali shift operátor logikai eltolást hajt végre: a nagy sorrendű üres bitpozíciók mindig nullára vannak állítva.

    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
    

Feljegyzés

Az aláíratlan jobb shift operátorral logikai műszakot hajthat végre aláírt egész szám típusú operandusokon. Ez előnyben részesíti a bal oldali operandus hozzárendelés nélküli típusra történő átírását, majd a shift művelet eredményét egy aláírt típusra.

Nem aláírt jobb műszakos operátor >>>

A C# 11-es és újabb verzióiban elérhető operátor a >>> jobb oldali operandus által meghatározott bitek számával jobbra tolja el a bal oldali operandusát. Arról, hogy a jobb oldali operandus hogyan határozza meg a műszakszámot, tekintse meg a shift operátorok szakaszának Shift count (Műszakok száma) szakaszát.

Az >>> operátor mindig logikai eltolódást hajt végre. Ez azt jelenti, hogy a magas sorrendű üres bitpozíciók mindig nullára vannak állítva, függetlenül a bal oldali operandus típusától. Az >> operátor aritmetikai eltolódást hajt végre (azaz a legjelentősebb bit értékét propagálja a rendszer a nagy sorrendű üres bitpozíciókba), ha a bal oldali operandus aláírt típusú. Az alábbi példa a negatív bal oldali operandus és >>> operátorok közötti >> különbséget mutatja be:

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

Logikai ÉS operátor >

Az & operátor kiszámítja a bitenkénti logikai ÉS annak integrál operandusait:

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

Az operandusok esetében bool az operátor kiszámítja & az operandusok logikai ÉS operandusait. Az unary & operátor az operátor címe.

Logikai kizárólagos VAGY operátor ^

Az ^ operátor kiszámítja az integrál operandusainak bitenkénti logikai kizárólagos vagy más néven bitenkénti logikai XOR-ját:

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

Az operandusok esetében bool az operátor kiszámítja ^ az operandusok logikai kizárólagosságát vagy operandusait.

Logikai VAGY operátor |

Az | operátor kiszámítja a bitenkénti logikai VAGY annak integrál operandusait:

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

Operandusok esetén bool az operátor kiszámítja | az operandusok logikai VAGY operandusait.

Összetett hozzárendelés

Bináris operátor opesetén az űrlap összetett hozzárendelési kifejezése

x op= y

egyenértékű a

x = x op y

kivéve, hogy csak x egyszer van kiértékelve.

Az alábbi példa az összetett hozzárendelés bitenkénti és shift operátorokkal való használatát mutatja be:

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

Numerikus előléptetések miatt előfordulhat, hogy a op művelet eredménye implicit módon nem konvertálható a típusra Tx. Ilyen esetben, ha op egy előre definiált operátor, és a művelet eredménye explicit módon átalakítható a típusraxT, az űrlap x op= y összetett hozzárendelési kifejezése egyenértékűx = (T)(x op y), kivéve, ha x csak egyszer lesz kiértékelve. Az alábbi példa ezt a viselkedést mutatja be:

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

Operátorok műveleti sorrendje

Az alábbi lista bitenkénti sorrendbe sorolja a operátorokat, és a legmagasabb prioritástól a legalacsonyabbig vált:

  • Bitenkénti komplementer operátor ~
  • Shift operátorok <<, >>és >>>
  • Logikai ÉS operátor &
  • Logikai kizárólagos VAGY operátor ^
  • Logikai VAGY operátor |

Zárójelek ()használatával módosítsa az operátorok elsőbbsége által előírt kiértékelési sorrendet:

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

A C#-operátorok elsőbbségi szint szerint rendezett teljes listájáért tekintse meg a C# operátorok cikkének Operátorok elsőbbsége című szakaszát.

A műszak operátorainak műszakszáma

A beépített shift operátorok <<>>>>>esetében a jobb oldali operandus típusának vagy olyan típusnak kell lennieint, amely előre definiált implicit numerikus átalakítássalintrendelkezik.

A x << count, x >> countés x >>> count kifejezések esetében a tényleges műszakszám az alábbi típustól x függ:

  • Ha a típus x vagy intuintaz, a műszakok számát a jobb oldali operandus alacsony sorrendű öt bitje határozza meg. Ez azt jelent, hogy a műszakok száma (vagy count & 0b_1_1111) alapján count & 0x1F lesz kiszámítva.

  • Ha a típus x vagy longulongaz, akkor a műszakok számát a jobb oldali operandus alacsonyrendű hat bitje határozza meg. Ez azt jelent, hogy a műszakok száma (vagy count & 0b_11_1111) alapján count & 0x3F lesz kiszámítva.

Az alábbi példa ezt a viselkedést mutatja be:

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

Feljegyzés

Ahogy az előző példa is mutatja, a műszakművelet eredménye nem lehet nulla akkor is, ha a jobb oldali operandus értéke nagyobb, mint a bal oldali operandus bitjeinek száma.

Logikai operátorok számbavétele

A ~, &, |és ^ operátorokat bármely enumerálási típus is támogatja. Az azonos enumerálási típusú operandusok esetében a rendszer logikai műveletet hajt végre az alapul szolgáló integráltípus megfelelő értékein. Például egy mögöttes típusú Uenumerálási típus T esetén yx a x & y kifejezés ugyanazt az eredményt adja, mint a (T)((U)x & (U)y) kifejezés.

Általában bitenkénti logikai operátorokat használ a Flags attribútummal definiált enumerálási típussal. További információkért tekintse meg az Enumerálási típusok az Enumerálástípusok cikk bitjelölők szakaszát.

Operátorok túlterhelése

A felhasználó által definiált típus túlterhelheti a ~, <<, >>, >>>, &, |és ^ operátorokat. Ha egy bináris operátor túlterhelt, a megfelelő összetett hozzárendelési operátor is implicit módon túlterhelt. A felhasználó által definiált típus nem tudja explicit módon túlterhelni az összetett hozzárendelési operátorokat.

Ha egy felhasználó által definiált típus T túlterheli a <<, >>vagy >>> operátort, a bal oldali operandus típusának kell lennie T. A C# 10-es és korábbi verzióiban a jobb oldali operandus típusának kell lennie int; a C# 11-től kezdődően a túlterhelt műszak operátorának jobb operandusának típusa bármelyik lehet.

C# nyelvspecifikáció

További információt a C# nyelvspecifikációjának alábbi szakaszaiban talál:

Lásd még