Bitvisa operationer i C

I programmeringsspråket C kan operationer utföras på bitnivå med hjälp av bitvisa operatorer .

Bitvisa operationer kontrasteras av bytenivåoperationer som kännetecknar de bitvisa operatorernas logiska motsvarigheter, AND, OR, NOT-operatorerna. Istället för att utföra på enskilda bitar, utför byte-nivåoperatorer på strängar om åtta bitar (känd som bytes) åt gången. Anledningen till detta är att en byte normalt är den minsta enheten av adresserbart minne (dvs data med en unik minnesadress ) .

Detta gäller också för bitvisa operatorer, vilket innebär att även om de bara arbetar på en bit åt gången kan de inte acceptera något mindre än en byte som sin indata.

Alla dessa operatorer är också tillgängliga i C++ och många C-familjespråk .

Bitvisa operatörer

C tillhandahåller sex operatorer för bitmanipulation .

Symbol Operatör
& bitvis OCH
| bitvis inklusive ELLER
^ bitvis XOR (exklusiv ELLER)
< vänster Shift
>> höger skift
~ bitvis INTE (ens komplement) (unär)

Bitvis OCH &

lite a bit b a & b (a OCH b)
0 0 0
0 1 0
1 0 0
1 1 1

Den bitvisa AND-operatorn är ett enda et-tecken: & . Det är bara en representation av OCH som gör sitt arbete på bitarna av operanderna snarare än sanningsvärdet för operanderna. Bitvis binär AND utför logisk konjunktion (visas i tabellen ovan) av bitarna i varje position av ett tal i dess binära form.

Till exempel, arbeta med en byte (char-typen):

11001000 & 10111000 -------- = 10001000

Den mest signifikanta biten av det första talet är 1 och den för det andra talet är också 1, så den mest signifikanta biten av resultatet är 1; i den näst mest signifikanta biten är biten i det andra talet noll, så vi har resultatet som 0.

Bitvis ELLER |

lite a bit b en | b (a ELLER b)
0 0 0
0 1 1
1 0 1
1 1 1

I likhet med bitvis AND utför bitvis OR logisk disjunktion på bitnivå. Resultatet är en 1 om någon av bitarna är 1 och noll endast när båda bitarna är 0. Dess symbol är | som kan kallas ett rör.

11001000 | 10111000 -------- = 11111000

Bitvis XOR ^

lite a bit b a ^ b (a XOR b)
0 0 0
0 1 1
1 0 1
1 1 0

Den bitvisa XOR (exklusiv eller) utför en exklusiv disjunktion , vilket motsvarar att lägga till två bitar och kassera överföringen. Resultatet är noll bara när vi har två nollor eller två ettor. XOR kan användas för att växla bitarna mellan 1 och 0. Således växlar i = i ^ 1 när den används i en loop dess värden mellan 1 och 0.

11001000 ^ 10111000 -------- = 01110000

Skiftoperatörer

Det finns två bitvisa skiftoperatorer. Dom är

  • Högerväxling ( >> )
  • Vänsterskift ( << )

Högerväxling >>

Symbolen för högerväxlingsoperatör är >> . För dess funktion krävs två operander . Den skiftar varje bit i sin vänstra operand till höger. Siffran efter operatören bestämmer antalet platser som bitarna skiftas (dvs. den högra operanden). Genom att göra ch >> 3 kommer alltså alla bitar att flyttas åt höger med tre platser och så vidare.

Observera dock att ett skiftoperandvärde som antingen är ett negativt tal eller är större än eller lika med det totala antalet bitar i detta värde resulterar i odefinierat beteende . Till exempel, när man skiftar ett 32-bitars heltal utan tecken, skulle en skiftmängd på 32 eller högre vara odefinierad.

Exempel:

Om variabeln ch innehåller bitmönstret 11100101 kommer ch >> 1 att ge resultatet 01110010 och ch >> 2 kommer att ge 00111001 .

0 Här genereras tomma utrymmen samtidigt till vänster när bitarna skiftas åt höger. När den utförs på en osignerad typ eller ett icke-negativt värde i en signerad typ, är operationen som utförs en logisk förskjutning , vilket gör att tomrummen fylls med s (nollor). När det utförs på ett negativt värde i en teckentyp är resultatet tekniskt implementeringsdefinierat (kompilatorberoende), men de flesta kompilatorer kommer att utföra ett aritmetiskt skift , vilket gör att tomrummet fylls med inställningsteckenbiten i den vänstra operanden.

Högerskiftning kan användas för att dividera ett bitmönster med 2 enligt bilden:

   
      i  =  14  ;  // Bitmönster 00001110  j  =  i  >>  1  ;  // här har vi bitmönstret förskjutet med 1 så vi får 00000111 = 7 vilket är 14/2 

Användning av högerskiftsförare

Typisk användning av en högerväxlingsoperatör i C kan ses av följande kod.

Exempel:

 
    
     

     0
              0 
    
               
    
    


   

       
     
    

    
        0    
    
             
          
        
    
     0
 #include  <stdio.h>  void  showbits  (  unsigned  int  x  )  {  int  i  =  ;  för  (  i  =  (  storleken på  (  int  )  *  8  )  -1  ;  i  >=  ;  i  -  )  {  putchar  (  x  &  (  1u  <<  i  )  ?  '1'  :  '0'  )  ;  }  printf  (  "  \n  "  );  }  int  main  (  void  )  {  int  j  =  5225  ;  printf  (  "%d i binär  \t\t  "  ,  j  );  showbits  (  j  );  /* slingan för högerskiftningsoperation */  for  (  int  m  =  ;  m  <=  5  ;  m  ++  )  {  int  n  =  j  >>  m  ;  printf  (  "%d högerskifte %d ger"  ,  j  ,  m  );  showbits  (  n  );  }  returnera  ;  } 

Resultatet av programmet ovan kommer att vara

5225 i binärt 00000000000000000001010001101001 5225 höger skift 0 ger 0000000000000000000001010001101001 5225 höger skift 1 ger 0000000000000000000000 10100 5225 högerväxling 2 ger 00000000000000000000010100011010 5225 högerväxling 3 ger 0000000000000000000000001010001101 52000 0 ger 00000 höger 00101000110 5225 högerväxling 5 ger 0000000000000000000000000010100011

Vänster skift <<

Symbolen för vänsterväxlingsoperatör är << . Den skiftar varje bit i sin vänstra operand till vänster med antalet positioner som anges av den högra operanden. Den fungerar motsatsen till högerväxlingsföraren. Genom att göra ch << 1 i exemplet ovan ( 11100101 ) har vi alltså 11001010 . Tomma utrymmen som genereras fylls upp med nollor enligt ovan.

Observera dock att ett skiftoperandvärde som antingen är ett negativt tal eller är större än eller lika med det totala antalet bitar i detta värde resulterar i odefinierat beteende . Detta definieras i standarden i ISO 9899:2011 6.5.7 Bitvisa skiftoperatorer . Till exempel, när man skiftar ett 32-bitars heltal utan tecken, skulle en skiftmängd på 32 eller högre vara odefinierad.

Vänsterskift kan användas för att multiplicera ett heltal med 2 potenser som i

       
       
     
              
              
               int  i  =  7  ;  // Decimal 7 är Binär (2^2) + (2^1) + (2^0) = 0000 0111  int  j  =  3  ;  // Decimal 3 är Binär (2^1) + (2^0) = 0000 0011  k  =  (  i  <<  j  );  // Vänsterskiftsoperation multiplicerar värdet med 2 till potensen av j i decimal  // Motsvarar att addera j nollor till den binära representationen av i  // 56 = 7 * 2^3  // 0011 1000 = 0000 0111 << 0000 0011 

Exempel: ett enkelt tilläggsprogram

Följande program lägger till två operander med hjälp av AND, XOR och left shift (<<).

 

   

             
         
         
       0
    
             
           
           
             
             



    
      
     0
 #include  <stdio.h>  int  main  (  void  )  {  unsigned  int  x  =  3  ,  y  =  1  ,  summa  ,  carry  ;  summa  =  x  ^  y  ;  // x XOR y  carry  =  x  &  y  ;  // x OCH y  while  (  bära  !=  )  {  bära  =  bära  <<  1  ;  // vänster skift bära  x  =  summa  ;  // initialisera x som summa  y  =  bära  ;  // initiera y som  bärsumma  =  x  ^  y  ;  // summan beräknas  carry  =  x  &  y  ;  /* carry beräknas, loopvillkoret utvärderas  och processen upprepas tills  carry är lika med 0.  */  }  printf  (  "%u  \n  "  ,  summa  );  // programmet kommer att skriva ut 4  returer  ;  } 

Bitvisa tilldelningsoperatorer

C tillhandahåller en sammansatt tilldelningsoperator för varje binär aritmetisk och bitvis operation. Varje operatör accepterar en vänster operand och en höger operand, utför lämplig binär operation på båda och lagrar resultatet i den vänstra operanden.

De bitvisa tilldelningsoperatorerna är följande.

Symbol Operatör
&= bitvis OCH tilldelning
|= bitvis inkluderande ELLER tilldelning
^= bitvis exklusiv ELLER tilldelning
<<= vänster skiftuppdrag
>>= högerskiftsuppdrag

Logiska motsvarigheter

Fyra av de bitvisa operatorerna har ekvivalenta logiska operatorer. De är likvärdiga genom att de har samma sanningstabeller. Men logiska operatorer behandlar varje operand som att den bara har ett värde, antingen sant eller falskt, snarare än att behandla varje bit i en operand som ett oberoende värde. Logiska operatorer anser att noll är falskt och alla värden som inte är noll är sanna. En annan skillnad är att logiska operatörer utför kortslutningsutvärdering .

Tabellen nedan matchar motsvarande operatorer och visar a och b som operander för operatorerna.

Bitvis Logisk
a & b a && b
en | b en || b
a ^ b a != b
~a !a

!= har samma sanningstabell som ^ men till skillnad från de sanna logiska operatorerna är != i sig själv strikt sett inte en logisk operator. Detta beror på att en logisk operator måste behandla alla värden som inte är noll lika. För att användas som en logisk operator != krävs att operander normaliseras först. En logik som inte tillämpas på båda operanderna kommer inte att ändra sanningstabellen som resulterar men kommer att säkerställa att alla värden som inte är noll konverteras till samma värde före jämförelse. Detta fungerar eftersom ! på en nolla resulterar alltid i en etta och ! på alla icke-nollvärden resulterar alltid i en noll.

Exempel:


 

        

   

   

   
            
            
         
          
         
	
            
         

       
       
       
     	
   

   

         
         

   

              
              
           
            
           
	
            
         

    
      0    
   
          
   
   

      0    
   
          
   
   

      0    
   
      
          
   
   

      0    
   
        
   
   

    0


          

            
           
 /* Motsvarande bitvisa och logiska operatortester */  #include  <stdio.h>  void  testOperator  (  char  *  name  ,  unsigned  char  was  ,  unsigned  char  waited  );  int  main  (  void  )  {  // -- Bitvisa operatorer -- //  //Sanningstabeller packade i bitar  const  unsigned  char  operand1  =  0x0A  ;  //0000 1010  const  unsigned  char  operand2  =  0x0C  ;  //0000 1100  const  unsigned  char  förväntadAnd  =  0x08  ;  //0000 1000  const  unsigned  char  förväntadEller  =  0x0E  ;  //0000 1110  const  unsigned  char  förväntadXor  =  0x06  ;  //0000 0110  const  unsigned  char  operand3  =  0x01  ;  //0000 0001  const  unsigned  char  förväntadNot  =  0xFE  ;  //1111 1110  testOperator  (  "Bitwise AND"  ,  operand1  &  operand2  ,  expectedAnd  );  testOperator  (  "Bitvis ELLER"  ,  operand1  |  operand2  ,  förväntadEller  );  testOperator  (  "Bitwise XOR"  ,  operand1  ^  operand2  ,  expectedXor  );  testOperator  (  "Bitwise NOT"  ,  ~  operand3  ,  expectedNot  );  printf  (  "  \n  "  );  // -- Logiska operatorer -- //  const  unsigned  char  F  =  0x00  ;  //Noll  konst  unsigned  char  T  =  0x01  ;  //Alla värden som inte är noll  // Sanningstabeller packade i arrayer  består av  unsigned  char  operandArray1  [  4  ]  =  {  T  ,  F  ,  T  ,  F  };  const  unsigned  char  operandArray2  [  4  ]  =  {  T  ,  T  ,  F  ,  F  };  const  unsigned  char  expectArrayAnd  [  4  ]  =  {  T  ,  F  ,  F  ,  F  };  const  unsigned  char  expectedArrayOr  [  4  ]  =  {  T  ,  T  ,  T  ,  F  };  const  unsigned  char  expectArrayXor  [  4  ]  =  {  F  ,  T  ,  T  ,  F  };  const  unsigned  char  operandArray3  [  2  ]  =  {  F  ,  T  };  const  unsigned  char  expectedArrayNot  [  2  ]  =  {  T  ,  F  };  int  i  ;  för  (  i  =  ;  i  <  4  ;  i  ++  )  {  testOperator  (  "Logical AND"  ,  operandArray1  [  i  ]  &&  operandArray2  [  i  ],  förväntadArrayAnd  [  i  ]);  }  printf  (  "  \n  "  );  för  (  i  =  ;  i  <  4  ;  i  ++  )  {  testOperator  (  "Logical OR"  ,  operandArray1  [  i  ]  ||  operandArray2  [  i  ],  förväntadArrayEller  [  i  ]);  }  printf  (  "  \n  "  );  for  (  i  =  ;  i  <  4  ;  i  ++  )  {  //Behöver! på operander om värden som inte är noll är olika   testOperator  (  "Logical XOR"  ,  !  operandArray1  [  i  ]  !=  !  operandArray2  [  i  ],  expectedArrayXor  [  i  ]);  }  printf  (  "  \n  "  );  for  (  i  =  ;  i  <  2  ;  i  ++  )  {  testOperator  (  "Logical NOT"  ,  !  operandArray3  [  i  ],  förväntadArrayNot  [  i  ]);  }  printf  (  "  \n  "  );  återvända  ;  }  void  testOperator  (  char  *  name  ,  unsigned  char  was  ,  unsigned  char  förväntad  )  {  char  *  result  =  (  var  ==  förväntat  )  ?  "godkänd"  :  "underkänd"  ;  printf  (  "%s %s, var: %X förväntat: %X  \n  "  ,  namn  ,  resultat  ,  var  ,  förväntat  );  } 

Resultatet av programmet ovan kommer att vara

Bitvis OCH godkänd, var: 8 förväntad: 8 Bitvis OR godkänd, var: E förväntad: E Bitvis XOR godkänd, var: 6 förväntad: 6 Bitvis INTE godkänd, var: FE förväntad: FE Logisk OCH godkänd, var: 1 förväntad: 1 Logisk OCH godkänd, var: 0 förväntad: 0 Logisk OCH godkänd, var: 0 förväntad: 0 Logisk OCH godkänd, var: 0 förväntad: 0 Logisk OR godkänd, var: 1 förväntad: 1 Logisk OR godkänd, var: 1 förväntad: 1 Logisk OR godkänd, var: 1 förväntad: 1 Logisk OR godkänd, var: 0 förväntad: 0 Logisk XOR godkänd, var: 0 förväntad: 0 Logisk XOR godkänd, var: 1 förväntad: 1 Logisk XOR godkänd, var: 1 förväntad: 1 Logisk XOR godkänd, var: 0 förväntad: 0 Logisk INTE godkänd, var: 1 förväntad: 1 Logisk INTE godkänd, var: 0 förväntad: 0

Se även

externa länkar