Tutoriels - Opérateurs binaires et Bit Manipulations en C et C


Par Alex Allain

En général, en tant que programmeur, vous n'avez pas besoin de vous préoccuper au sujet des opérations au niveau du bit. Vous êtes libre de penser en octets, ou ints et doubles, ou même les types de données de niveau supérieur composé d'une combinaison de ceux-ci. Mais il y a des moments où vous souhaitez être en mesure d'aller au niveau d'un bit individuel. chiffrement ou exclusif est un exemple lorsque vous avez besoin des opérations binaires.







Enfin, vous pouvez utiliser des opérations de bits pour accélérer votre programme ou effectuer des tours soignées. (Ce n'est pas toujours la meilleure chose à faire.)

Penser à Bits

L'opérateur leftshift est l'équivalent de déplacer tous les bits d'un nombre un nombre de places à gauche: Par exemple, considérons le nombre 8 écrit en binaire 00001000. Si nous voulions passer à 2 places à gauche, nous avions aboutir à 00100000; tout est déplacé aux deux endroits, et des zéros à gauche sont ajoutés comme rembourrage. Ceci est le numéro 32 - en fait, à gauche déplacement est équivalent à la multiplication par une puissance de deux. Notez que dans cet exemple, nous utilisons des entiers, qui sont soit 2 ou 4 octets, et que l'opération soit appliquée à la séquence complète de 16 ou 32 bits.

Mais ce qui se passe si on passe un certain nombre comme 128 et nous ne le stocker dans un seul octet: 10000000? Eh bien, 128 * 2 = 256, et nous ne pouvons pas stocker même un nombre important dans un octet, donc il ne devrait pas être surprenant que le résultat est 00000000.

Il ne devrait pas vous surprendre qu'il ya un opérateur décalage vers la droite correspondante: >> (d'autant plus que je l'ai mentionné plus tôt). Notez qu'un déplacement vers la droite au niveau du bit sera l'équivalent de la division entière par 2.

Pourquoi est-il la division entière? Considérons le nombre 5, en binaire, 00000101. 5/2 est de 2,5, mais si vous effectuer une division de nombre entier, 5/2 est 2. Lorsque vous effectuer un décalage vers la droite par une: (unsigned int) 5 >> 1, on finit avec 00000010, comme une droite obtient décalée de l'extrémité; c'est la représentation du nombre 2. Notez que cela ne vaut que pour les entiers non signés; sinon, nous ne sommes pas garanti que les bits de remplissage seront tous 0s.

En général, en utilisant les opérateurs de décalage à gauche et à droite se traduira par un code nettement plus rapide que le calcul et la multiplication par une puissance de deux. Les opérateurs de décalage seront également utiles plus tard, quand nous regardons comment manipuler des bits individuels.

Pour l'instant, regardons quelques-uns des autres opérateurs binaires pour voir ce qu'ils peuvent faire pour nous.

ET bitwise

L'opérateur est au niveau du bit d'un seul esperluette: -. Un moyen mnémotechnique pratique est que la petite version du booléens AND, -, travaille sur des morceaux plus petits (bits au lieu d'octets, caractères, entiers, etc.). En substance, un binaire et prend simplement la logique des bits dans chaque position d'un nombre sous forme binaire.

Par exemple, travailler avec un octet (le type de char): Le bit le plus significatif du premier nombre est 0, donc nous savons que le bit le plus significatif du résultat doit être 0; dans le second bit le plus significatif, le bit de deuxième chiffre est égal à zéro, nous avons donc le même résultat. Le seul moment où les deux bits sont 1, qui est le seul moment où le résultat est 1, est le cinquième bit depuis le côté gauche. Par conséquent,

OU bitwise

OU fonctionne Bitwise presque exactement de la même manière que au niveau du bit. La seule différence est que seul l'un des deux bits doit être 1 pour le bit de cette position dans le résultat à 1. (Si les deux bits sont 1, le résultat aura également un 1 dans cette position.) Le symbole est un tuyau: |. Encore une fois, ce qui est similaire à booléen opérateur logique, qui est ||. et par conséquent Jetons un coup d'oeil à un exemple quand vous pouvez utiliser seulement ces quatre opérateurs de faire quelque chose potentiellement utile. Disons que vous vouliez garder une trace de certains attributs booléens quelque chose - par exemple, vous pourriez avoir huit voitures et que vous voulez garder une trace de qui sont en cours d'utilisation (!). Assignons chacune des voitures un nombre de 0 à 7.

Cette procédure fonctionne pour trouver le bit à la nième position. La seule chose à faire est de créer un numéro avec seulement un peu dans la position correcte sous tension. Ce ne sont que des puissances de deux, donc une approche peut-être faire quelque chose comme: Bien que cette fonction fonctionne, il peut être source de confusion. Il obscurcit le fait que ce que nous voulons faire est de passer un peu plus d'un certain nombre d'endroits, de sorte que nous avons un certain nombre comme 00100000 - deux zéros, un, et quelques zéros. (L'on pourrait aussi être le premier ou le dernier - 10000000 ou 00000001.)

Nous pouvons utiliser un leftshift bitwise pour ce faire, et ce sera beaucoup plus rapide à démarrer. Si nous commençons par le numéro 1, nous sommes assurés d'avoir un seul bit, et nous savons qu'il est à l'extrême droite. Nous allons garder à l'esprit que la voiture 0 aura ses données stockées dans le bit le plus à droite, et la voiture 7 sera le plus à gauche. Notez que le passage par zéro lieux est une opération juridique - nous allons revenir le même numéro que nous avons commencé avec.

Tout ce que nous pouvons faire maintenant vérifier si une voiture est en cours d'utilisation; nous ne pouvons pas mettre réellement le bit en usage. Il y a deux cas à considérer: indiquer une voiture est en cours d'utilisation, et la suppression d'une voiture de l'utilisation. Dans un cas, nous devons tourner un peu, et dans l'autre, mettez un peu hors tension.







Abordons le problème de tourner le bit sur. Qu'est-ce que cela suggère que nous devrions faire? Si nous avons un ensemble à zéro bit, la seule façon que nous connaissons en ce moment pour le mettre à 1 est de faire une opération de bits OR. Idéalement, si nous effectuons une opération de bits OU avec un seul bit à 1 (le reste est 0), alors nous n'affectera le reste du nombre parce que tout ORED zéro reste le même (1 ou 0 est 1, et 0 OR 0 est 0).

Encore une fois, nous devons déplacer un peu dans la position correcte: Qu'est-ce que cela fait? Prenons le cas de réglage du bit à 1 à droite: nous avons un certain nombre 0XXXXXXX | 10000000; le résultat, 1XXXXXXX. Le décalage est le même que précédemment; la seule différence est l'opérateur et que nous stockons le résultat.

Définition d'une voiture pour ne plus être utilisé est un peu plus compliqué. Pour cela, nous aurons besoin d'un autre opérateur.

Le Bitwise Complement

L'opérateur de complément au niveau du bit, le tilde,

flips chaque bit. Une bonne façon de se rappeler est que le tilde est parfois appelé tripoter, et le complément de chaque bit twiddles au niveau du bit: si vous avez un 1, il est 0, et si vous avez un 0, il est un 1.

Cela se révèle être un excellent moyen de trouver la plus grande valeur possible pour un nombre non signé: 0, bien sûr, est: 00000000 tous 0s 00000000. Une fois que nous Twiddle 0, nous obtenons tous les 1: 11111111 11111111. Comme max est un entier non signé , nous n'avons pas à vous soucier de bits de signe ou complément à deux. Nous savons que tous les 1 est le plus grand nombre possible.

et. ne peuvent pas être utilisés de façon interchangeable. Lorsque vous prenez le NON logique d'un nombre non nul, vous obtenez 0 (FAUX). Toutefois, lorsque vous tripoter un nombre non nul, la seule fois que vous aurez 0 est quand chaque bit est activé. (Ce principe de non-équivalence est vrai pour au niveau du bit aussi, sauf si vous savez que vous utilisez strictement les numéros 1 et 0. Pour un OU binaire, d'être certain que ce serait équivalent, vous aurez besoin pour vous assurer que le sous-jacent représentation de 0 est tous les zéros à l'utiliser de manière interchangeable. Mais ne le font pas! Ça va rendre votre code plus difficile à comprendre.)

Maintenant que nous avons un moyen de retournement de bits, nous pouvons commencer à penser à la façon de désactiver un seul bit. Nous savons que nous voulons laisser les autres bits non affecté, mais que si nous avons un 1 dans la position donnée, nous voulons que ce soit un 0. Prenez le temps de réfléchir à la façon de le faire avant de lire plus loin.

Nous devons trouver une séquence d'opérations qui laisse 1 et de 0 dans la position non-cible affectée; avant, nous avons utilisé un opérateur OR, mais on peut aussi utiliser un niveau du bit. 1 et 1 est 1 et 0 et 1 est 0. Maintenant, pour éteindre un peu, nous avons juste besoin et avec 0: 1 et 0 est 0. Donc, si nous voulons indiquer que la voiture 2 n'est plus utilisé , nous voulons prendre le ET de XXXXX1XX bitwise avec 11.111.011.

Comment pouvons-nous obtenir ce numéro? C'est là la possibilité de prendre le complément d'un nombre est très pratique: nous savons déjà comment transformer un peu sur. Si nous nous tournons un peu et prendre le complément du nombre, nous obtenons chaque bit à l'exception que peu: Maintenant que nous avons, nous pouvons simplement prendre le bitwise et cela avec le champ actuel des voitures, et le seul bit nous « le changement ll est l'un des car_num qui nous intéresse. Vous pourriez penser à vous-même, mais ce genre est de maladroit. Nous avons vraiment besoin de savoir si une voiture est en cours d'utilisation ou non (si le bit est activé ou désactivé) avant que nous puissions savoir quelle fonction à appeler. Bien que ce n'est pas nécessairement une mauvaise chose, cela signifie que nous avons besoin de savoir un peu plus sur ce qui se passe. Il y a un moyen plus facile, mais nous devons d'abord le dernier opérateur au niveau du bit: exclusif ou.

Bitwise exclusif (XOR)

Il n'y a pas de contrepartie de l'opérateur booléen à ou exclusif au niveau du bit, mais il y a une explication simple. Le ou exclusif opération prend deux entrées et renvoie 1 si l'un ou l'autre des entrées est un 1, mais pas si les deux sont. Autrement dit, si les deux entrées sont une ou les deux entrées sont 0, il renvoie la valeur 0. Bitwise ou-exclusif, à l'exploitant d'un caret, ^, effectue l'opération OU exclusif sur chaque paire de bits. Ou exclusif est généralement abrégé XOR.

Par exemple, si vous avez deux nombres représentés en binaire 10101010 et 01110010 prenant ensuite les résultats XOR au niveau du bit en 11011000. Il est plus facile de voir cela si les bits sont correctement alignés: Vous pouvez penser XOR de la manière suivante: vous avez des bit, 1 ou 0, que nous appellerons A. Lorsque vous prenez un XOR 0, vous obtenez toujours un retour: si A est 1, vous obtenez 1, et si A est 0, vous obtenez 0. autre Par contre, lorsque vous prenez un XOR 1, vous feuilletez A. Si A est 0, vous obtenez 1; si A est 1, vous obtenez 0.

Ainsi, vous pouvez penser à l'opération XOR comme une sorte de tripoter sélective: si vous appliquez XOR à deux chiffres, dont un est tous les 1, vous obtenez l'équivalent d'un tripoter.

De plus, si vous appliquez l'opération XOR deux fois - que vous avez un peu, A, et un autre bit B et vous définissez C égale à A XOR B, puis prendre C XOR B: vous obtenez A XOR B XOR B, essentiellement soit flips tous les bits de a deux reprises, ou flips jamais le bit, alors que vous venez de retournons A. (vous pouvez aussi penser à B XOR B en annulant.) en guise d'exercice, vous pouvez penser à un moyen d'utiliser pour échange de deux variables entières sans une variable temporaire? (Une fois que vous l'avez deviné, vérifier la solution.)

Comment est-ce que nous aider? Eh bien, rappelez-vous le premier principe: XOR un peu avec 0 dans le même bit. Donc, ce que nous aimerions vraiment pouvoir faire est d'appeler une fonction qui retourne le bit de la voiture qui nous intéresse - peu importe si elle est en cours de mise sous tension ou hors tension - et laisse le reste des bits inchangés.

Cela semble beaucoup comme la ce que nous avons fait dans le passé; en fait, nous avons seulement besoin de faire un changement à notre fonction de tourner un peu. Au lieu d'utiliser un opérateur OR, nous utilisons un XOR au niveau du bit. Cela laisse tout inchangé, mais retourne le bit au lieu de tourner toujours sur:

Quand devriez-vous utiliser des opérateurs au niveau du bit?

0 pour trouver le plus grand entier possible. Et décalage de bits à multiplier par deux est une opération assez commune, il ne modifie pas la lisibilité de la manière que AVANCÉS de manipulation de bits peut, dans certains cas (par exemple, en utilisant XOR pour changer les valeurs stockées dans deux variables).

Il y a aussi des moments où vous devez utiliser des opérateurs au niveau du bit: si vous travaillez avec une compression ou certaines formes de chiffrement, ou si vous travaillez sur un système qui prévoit des champs de bits à utiliser pour stocker des attributs booléens.

Vous devriez maintenant être familier avec six opérateurs de bits:

Fonctionne sur des bits de l'argument de gauche, prend un entier comme un second argument Shifts bits de bit_arg shift_arg positions vers la gauche - équivalent à la multiplication par 2 ^ shift_arg décale des bits pour des bit_arg shift_arg positions à droite - équivalent à une division entière par 2 ^ shift_arg

Les travaux sur les bits des deux arguments est d'ET de left_arg bitwise et right_arg prend la XOR de left_arg et right_arg Fonctionne sur les bits de l'argument que les bits de Inverse arg

Compétences et connaissances Vous savez aussi quelques astuces intéressantes que vous pouvez utiliser lorsque la performance est critique, ou l'espace est lent, ou vous avez juste besoin d'isoler et de manipuler des bits individuels d'un certain nombre.

Et vous devriez maintenant avoir une meilleure idée de ce qui se passe au niveau le plus bas de votre ordinateur.

Un casse-tête Parting

Une astuce finale des opérateurs est que vous bitwise pouvez les utiliser, en association avec un peu de mathématiques, pour savoir si un entier est une puissance de deux. Prenez le temps d'y penser, puis vérifier la solution. Articles Liés
Un peu de fond en bits et quelques autres trucs