Opérations au niveau des bits en C - Bitwise operations in C
Dans le langage de programmation C , les opérations peuvent être effectuées au niveau du bit à l' aide d' opérateurs au niveau du bit .
Les opérations au niveau du bit sont contrastées par les opérations au niveau de l' octet qui caractérisent les contreparties logiques des opérateurs au niveau du bit, les opérateurs ET, OU, NON. Au lieu de s'exécuter sur des bits individuels, les opérateurs de niveau octet s'exécutent sur des chaînes de huit bits (appelées octets) à la fois. La raison en est qu'un octet est normalement la plus petite unité de mémoire adressable (c'est-à-dire des données avec une adresse mémoire unique ).
Cela s'applique également aux opérateurs au niveau du bit, ce qui signifie que même s'ils n'opèrent que sur un bit à la fois, ils ne peuvent accepter rien de plus petit qu'un octet comme entrée.
Tous ces opérateurs sont également disponibles en C ++ , et beaucoup de C-famille des langues.
Opérateurs au niveau du bit
C fournit six opérateurs pour la manipulation de bits .
symbole | Opérateur |
---|---|
& |
ET au niveau du bit |
| |
OU inclusif au niveau du bit |
^ |
XOR au niveau du bit (OU exclusif) |
<< |
décalage à gauche |
>> |
décalage à droite |
~ |
PAS au niveau du bit (complément à un) (unaire) |
ET au niveau du bit &
un peu | peu b |
a & b (a ET b)
|
---|---|---|
0 | 0 | 0 |
0 | 1 | 0 |
1 | 0 | 0 |
1 | 1 | 1 |
L'opérateur ET au niveau du bit est une seule esperluette : &
. C'est juste une représentation de AND qui fait son travail sur les bits des opérandes plutôt que sur la valeur de vérité des opérandes. L'ET binaire au niveau du bit effectue une conjonction logique (indiquée dans le tableau ci-dessus) des bits à chaque position d'un nombre sous sa forme binaire.
Par exemple, en travaillant avec un octet (le type char) :
11001000 & 10111000 -------- = 10001000
Le bit le plus significatif du premier nombre est 1 et celui du deuxième nombre est également 1, donc le bit le plus significatif du résultat est 1 ; dans le deuxième bit le plus significatif, le bit du deuxième nombre est zéro, nous avons donc le résultat 0.
OU au niveau du bit |
un peu | peu b | un | b (a OU b) |
---|---|---|
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 1 |
Semblable à ET au niveau du bit, le OU au niveau du bit effectue une disjonction logique au niveau du bit. Son résultat est un 1 si l'un des bits est à 1 et zéro uniquement lorsque les deux bits sont à 0. Son symbole est |
ce qu'on peut appeler un tube.
11001000
| 10111000
--------
= 11111000
XOR au niveau du bit ^
un peu | peu b |
a ^ b (a XOR b)
|
---|---|---|
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 0 |
Le XOR au niveau du bit (exclusif ou) effectue une disjonction exclusive , ce qui équivaut à ajouter deux bits et à rejeter le report. Le résultat n'est nul que lorsque nous avons deux zéros ou deux uns. XOR peut être utilisé pour basculer les bits entre 1 et 0. Ainsi, i = i ^ 1
lorsqu'il est utilisé dans une boucle, ses valeurs basculent entre 1 et 0.
11001000
^ 10111000
--------
= 01110000
Opérateurs de quart
Il existe deux opérateurs de décalage au niveau du bit. Elles sont
- Décalage à droite (
>>
) - Décalage à gauche (
<<
)
Décalage à droite >>
Le symbole de l'opérateur de décalage à droite est >>
. Pour son fonctionnement, il nécessite deux opérandes . Il décale chaque bit de son opérande gauche vers la droite. Le nombre suivant l'opérateur décide du nombre de places où les bits sont décalés (c'est-à-dire l'opérande de droite). Ainsi en faisant ch >> 3
tous les bits seront décalés vers la droite de trois places et ainsi de suite.
Cependant, notez qu'une valeur d'opérande de décalage qui est soit un nombre négatif, soit supérieure ou égale au nombre total de bits dans cette valeur entraîne un comportement indéfini . Par exemple, lors du décalage d'un entier non signé de 32 bits, une quantité de décalage de 32 ou plus serait indéfinie.
Exemple:
- Si la variable
ch
contient le modèle binaire11100101
, alorsch >> 1
produira le résultat01110010
, etch >> 2
produira00111001
.
Ici, des espaces blancs sont générés simultanément à gauche lorsque les bits sont décalés vers la droite. Lorsqu'elle est effectuée sur un type non signé ou une valeur non négative dans un type signé, l'opération effectuée est un décalage logique , provoquant le remplissage des blancs par 0
s (zéros). Lorsqu'il est effectué sur une valeur négative dans un type signé, le résultat est techniquement défini par l'implémentation (dépendant du compilateur), mais la plupart des compilateurs effectueront un décalage arithmétique , ce qui remplira le blanc avec le bit de signe défini de l'opérande gauche.
Le décalage vers la droite peut être utilisé pour diviser un motif de bits par 2, comme indiqué :
i = 14; // Bit pattern 00001110
j = i >> 1; // here we have the bit pattern shifted by 1 thus we get 00000111 = 7 which is 14/2
Utilisation de l'opérateur à droite
L'utilisation typique d'un opérateur de décalage à droite en C peut être vue à partir du code suivant.
Exemple:
#include <stdio.h>
void showbits( unsigned int x )
{
int i=0;
for (i = (sizeof(int) * 8) - 1; i >= 0; i--)
{
putchar(x & (1u << i) ? '1' : '0');
}
printf("\n");
}
int main( void )
{
int j = 5225;
printf("%d in binary \t\t ", j);
/* assume we have a function that prints a binary string when given
a decimal integer
*/
showbits(j);
/* the loop for right shift operation */
for (int m = 0; m <= 5; m++)
{
int n = j >> m;
printf("%d right shift %d gives ", j, m);
showbits(n);
}
return 0;
}
La sortie du programme ci-dessus sera
5225 in binary 00000000000000000001010001101001
5225 right shift 0 gives 00000000000000000001010001101001
5225 right shift 1 gives 00000000000000000000101000110100
5225 right shift 2 gives 00000000000000000000010100011010
5225 right shift 3 gives 00000000000000000000001010001101
5225 right shift 4 gives 00000000000000000000000101000110
5225 right shift 5 gives 00000000000000000000000010100011
Décalage à gauche <<
Le symbole de l'opérateur de décalage à gauche est <<
. Il décale chaque bit de son opérande de gauche vers la gauche du nombre de positions indiqué par l'opérande de droite. Il fonctionne à l'opposé de celui de l'opérateur à droite. Ainsi, en faisant ch << 1
dans l'exemple ci-dessus, nous avons 11001010
. Les espaces vides générés sont remplis par des zéros comme ci-dessus.
Cependant, notez qu'une valeur d'opérande de décalage qui est soit un nombre négatif, soit supérieure ou égale au nombre total de bits dans cette valeur entraîne un comportement indéfini . Ceci est défini dans la norme ISO 9899:2011 6.5.7 Opérateurs de décalage au niveau du bit . Par exemple, lors du décalage d'un entier non signé de 32 bits, une quantité de décalage de 32 ou plus serait indéfinie.
Le décalage à gauche peut être utilisé pour multiplier un nombre entier par des puissances de 2 comme dans
int i = 7; // Decimal 7 is Binary (2^2) + (2^1) + (2^0) = 0000 0111
int j = 3; // Decimal 3 is Binary (2^1) + (2^0) = 0000 0011
k = (i << j); // Left shift operation multiplies the value by 2 to the power of j in decimal
// Equivalent to adding j zeros to the binary representation of i
// 56 = 7 * 2^3
// 0011 1000 = 0000 0111 << 0000 0011
Exemple : un programme d'addition simple
Le programme suivant ajoute deux opérandes en utilisant AND, XOR et shift gauche (<<).
#include <stdio.h>
int main( void )
{
unsigned int x = 3, y = 1, sum, carry;
sum = x ^ y; // x XOR y
carry = x & y; // x AND y
while (carry != 0)
{
carry = carry << 1; // left shift the carry
x = sum; // initialize x as sum
y = carry; // initialize y as carry
sum = x ^ y; // sum is calculated
carry = x & y; /* carry is calculated, the loop condition is
evaluated and the process is repeated until
carry is equal to 0.
*/
}
printf("%u\n", sum); // the program will print 4
return 0;
}
Opérateurs d'affectation au niveau du bit
C fournit un opérateur d'affectation composé pour chaque opération arithmétique binaire et au niveau du bit (c'est-à-dire chaque opération qui accepte deux opérandes). Chacun des opérateurs d'affectation au niveau du bit composé effectue l'opération binaire appropriée et stocke le résultat dans l'opérande de gauche.
Les opérateurs d'affectation au niveau du bit sont les suivants :
symbole | Opérateur |
---|---|
&= |
affectation ET au niveau du bit |
|= |
affectation OU inclusive au niveau du bit |
^= |
affectation OU exclusif au niveau du bit |
<<= |
affectation à gauche |
>>= |
affectation à droite |
Équivalents logiques
Quatre des opérateurs au niveau du bit ont des opérateurs logiques équivalents. Ils sont équivalents en ce qu'ils ont les mêmes tables de vérité. Cependant, les opérateurs logiques traitent chaque opérande comme n'ayant qu'une seule valeur, vraie ou fausse, plutôt que de traiter chaque bit d'un opérande comme une valeur indépendante. Les opérateurs logiques considèrent que zéro est faux et toute valeur différente de zéro est vraie. Une autre différence est que les opérateurs logiques effectuent une évaluation de court-circuit .
Le tableau ci-dessous correspond aux opérateurs équivalents et montre a et b comme opérandes des opérateurs.
Au niveau du bit | Logique |
---|---|
a & b |
a && b
|
a | b |
a || b
|
a ^ b |
a != b
|
~a |
!a
|
!=
a la même table de vérité que ^
mais contrairement aux vrais opérateurs logiques, en soi !=
n'est pas à proprement parler un opérateur logique. En effet, un opérateur logique doit traiter de la même manière toute valeur différente de zéro. Pour être utilisé comme un opérateur logique, il !=
faut d'abord normaliser les opérandes. Une logique non appliquée aux deux opérandes ne changera pas la table de vérité qui en résulte mais garantira que toutes les valeurs non nulles sont converties en la même valeur avant la comparaison. Cela fonctionne car !
un zéro donne toujours un un et !
toute valeur différente de zéro donne toujours un zéro.
Exemple:
/* Equivalent bitwise and logical operator tests */
#include <stdio.h>
void testOperator(char* name, unsigned char was, unsigned char expected);
int main( void )
{
// -- Bitwise operators -- //
//Truth tables packed in bits
const unsigned char operand1 = 0x0A; //0000 1010
const unsigned char operand2 = 0x0C; //0000 1100
const unsigned char expectedAnd = 0x08; //0000 1000
const unsigned char expectedOr = 0x0E; //0000 1110
const unsigned char expectedXor = 0x06; //0000 0110
const unsigned char operand3 = 0x01; //0000 0001
const unsigned char expectedNot = 0xFE; //1111 1110
testOperator("Bitwise AND", operand1 & operand2, expectedAnd);
testOperator("Bitwise OR", operand1 | operand2, expectedOr);
testOperator("Bitwise XOR", operand1 ^ operand2, expectedXor);
testOperator("Bitwise NOT", ~operand3, expectedNot);
printf("\n");
// -- Logical operators -- //
const unsigned char F = 0x00; //Zero
const unsigned char T = 0x01; //Any nonzero value
// Truth tables packed in arrays
const unsigned char operandArray1[4] = {T, F, T, F};
const unsigned char operandArray2[4] = {T, T, F, F};
const unsigned char expectedArrayAnd[4] = {T, F, F, F};
const unsigned char expectedArrayOr[4] = {T, T, T, F};
const unsigned char expectedArrayXor[4] = {F, T, T, F};
const unsigned char operandArray3[2] = {F, T};
const unsigned char expectedArrayNot[2] = {T, F};
int i;
for (i = 0; i < 4; i++)
{
testOperator("Logical AND", operandArray1[i] && operandArray2[i], expectedArrayAnd[i]);
}
printf("\n");
for (i = 0; i < 4; i++)
{
testOperator("Logical OR", operandArray1[i] || operandArray2[i], expectedArrayOr[i]);
}
printf("\n");
for (i = 0; i < 4; i++)
{
//Needs ! on operand's in case nonzero values are different
testOperator("Logical XOR", !operandArray1[i] != !operandArray2[i], expectedArrayXor[i]);
}
printf("\n");
for (i = 0; i < 2; i++)
{
testOperator("Logical NOT", !operandArray3[i], expectedArrayNot[i]);
}
printf("\n");
return 0;
}
void testOperator( char* name, unsigned char was, unsigned char expected )
{
char* result = (was == expected) ? "passed" : "failed";
printf("%s %s test, was: %X expected: %X \n", name, result, was, expected);
}
La sortie du programme ci-dessus sera
Bitwise AND passed, was: 8 expected: 8
Bitwise OR passed, was: E expected: E
Bitwise XOR passed, was: 6 expected: 6
Bitwise NOT passed, was: FE expected: FE
Logical AND passed, was: 1 expected: 1
Logical AND passed, was: 0 expected: 0
Logical AND passed, was: 0 expected: 0
Logical AND passed, was: 0 expected: 0
Logical OR passed, was: 1 expected: 1
Logical OR passed, was: 1 expected: 1
Logical OR passed, was: 1 expected: 1
Logical OR passed, was: 0 expected: 0
Logical XOR passed, was: 0 expected: 0
Logical XOR passed, was: 1 expected: 1
Logical XOR passed, was: 1 expected: 1
Logical XOR passed, was: 0 expected: 0
Logical NOT passed, was: 1 expected: 1
Logical NOT passed, was: 0 expected: 0
Voir également
- Manipulation de bits
- Fonctionnement au niveau du bit
- Trouver le premier ensemble
- Opérateurs en C et C++
- Bitboard
- Algèbre booléenne (logique)
- Algorithme d'échange XOR
- Liste chaînée XOR