web-dev-qa-db-fra.com

Différence entre certains opérateurs "|", "^", "&", "& ^". Golang

Récemment, j'ai lu la spécification golang et j'ai rencontré des opérateurs intéressants:

&    bitwise AND            integers
|    bitwise OR             integers
^    bitwise XOR            integers
&^   bit clear (AND NOT)    integers

J'ai essayé de jouer avec, mais le seul que j'ai compris est que "|" ajouter des entiers et l'opérateur "+" travailler en plus avec des flottants, des chaînes, etc.

À quoi servent-ils dans la pratique? Quelqu'un pourrait-il donner des explications sur ces 4 opérateurs ci-dessus?

20
Timur Fayzrakhmanov

Les opérateurs au niveau du bit entrent en jeu lorsque vous devez travailler avec données au niveau octet ou bit.

Ici, je liste une poignée d'exemples utilisant des opérations binaires avec des exemples de code (sans ordre particulier):


1. Ils sont communs et font partie de nombreux algorithmes dans cryptographie et fonctions de hachage = (par exemple MD5 ).

2. Ils sont également souvent utilisés si vous voulez "économiser" de l'espace et que vous emballez plusieurs variables "bool" dans un int par exemple, vous affectez un bit à chaque variable booléenne. Vous devez utiliser des opérateurs au niveau du bit pour pouvoir modifier/lire individuellement les bits.

Par exemple, empaqueter 8 bits/bools dans un int:

flags := 0x00  // All flags are 0
flags |= 0x02  // Turn the 2nd bit to 1 (leaving rest unchanged)
flags |= 0xff  // Turn 8 bits (0..7) to 1
flags &= 0xfe  // Set the lowest bit to 0 (leaving rest unchanged)

istrue := flags&0x04 != 0 // Test if 3rd bit is 1

3. Un autre domaine est compression des données où vous voulez tirer le meilleur parti d'un byte et utiliser tous ses bits pour stocker/récupérer des informations (un bit est l'unité de base de l'information dans les communications informatiques et numériques).

4. Similaire à la compression mais pas tout à fait la même: bitstreams. Il est également utilisé pour économiser de l'espace dans un flux de données en n'envoyant pas d'octets complets mais plutôt des champs ayant une longueur de bits arbitraire.

J'ai écrit et publié un package de lecteur et d'écrivain hautement optimisé au niveau du bit, open source ici: github.com/icza/bitio . Vous verrez une utilisation extensive de toutes sortes d'opérations sur les bits dans ses sources.

5. Autre utilisation pratique: test de certaines propriétés d'un nombre (entier). Connaissant la représentation binaire des nombres entiers ( complément à deux ) il existe certaines caractéristiques des nombres dans leur représentation binaire. Par exemple, un nombre entier (en complément de 2) est pair (peut être divisé par 2) si le bit le plus bas est 0:

func isEven(i int) bool {
    return i&0x01 == 0
}

En testant les bits d'un entier, vous pouvez également savoir s'il s'agit d'une puissance de 2. Par exemple, si un nombre positif ne contient qu'un 1 bit, alors c'est une puissance de 2 (par exemple 2 = 0x02 = 00000010b, 16 = 0x10 = 00010000 mais par exemple 17 = 0x11 = 00010001 pas la puissance de 2).

6. De nombreuses procédures de codage/décodage utilisent également des opérations binaires. Le plus trivial est le encodage UTF-8 qui utilise un encodage de longueur variable pour représenter les points de code unicode (rune in Go) sous forme de séquences d'octets.
. sont toujours les données "utiles". Vous pouvez tester le bit le plus élevé et "séparer" les 7 bits utiles comme ceci:

b := readOneByte()
usefulBits := b & 0x7f
hasMoreBytes := b & 0x80 != 0

Le bénéfice de l'utilisation d'un tel encodage de longueur variable est que même si vous utilisez uint64 tapez Go qui est de 8 octets en mémoire, les petits nombres peuvent toujours être représentés en utilisant moins d'octets (nombres dans la plage 0..127 ne nécessite qu'un octet!). Si les échantillons que vous souhaitez stocker ou transférer ont de nombreuses petites valeurs, cela seul peut compresser les données au 1/8ème = 12,5%. L'inconvénient est que les grands nombres (qui ont des bits même dans l'octet le plus élevé) utiliseront plus de 8 octets. Que cela en vaille la peine dépend de l'heuristique des échantillons.

X. Et la liste continue ...


Pouvez-vous vivre sans connaître/utiliser des opérateurs au niveau du bit dans Go (et dans de nombreux autres langages de programmation)? La réponse est oui. Mais si vous les connaissez, ils peuvent parfois vous faciliter la vie et rendre vos programmes plus efficaces.

Si vous voulez en savoir plus sur le sujet, lisez l'article Wikipedia: Opération bit à bit et google le terme "Tutoriel d'opérateurs bit à bit", il existe de nombreux bons articles.

32
icza

Pour ce qu'ils font techniquement, consultez les commentaires dans ce

package main

import "fmt"

func main() {
    // Use bitwise OR | to get the bits that are in 1 OR 2
    // 1     = 00000001
    // 2     = 00000010
    // 1 | 2 = 00000011 = 3
    fmt.Println(1 | 2)

    // Use bitwise OR | to get the bits that are in 1 OR 5
    // 1     = 00000001
    // 5     = 00000101
    // 1 | 5 = 00000101 = 5
    fmt.Println(1 | 5)

    // Use bitwise XOR ^ to get the bits that are in 3 OR 6 BUT NOT BOTH
    // 3     = 00000011
    // 6     = 00000110
    // 3 ^ 6 = 00000101 = 5
    fmt.Println(3 ^ 6)

    // Use bitwise AND & to get the bits that are in 3 AND 6
    // 3     = 00000011
    // 6     = 00000110
    // 3 & 6 = 00000010 = 2
    fmt.Println(3 & 6)  

    // Use bit clear AND NOT &^ to get the bits that are in 3 AND NOT 6 (order matters)
    // 3      = 00000011
    // 6      = 00000110
    // 3 &^ 6 = 00000001 = 1
    fmt.Println(3 &^ 6)
}

View it on the playground

Veuillez noter que j'ai donné deux exemples de | pour montrer que ce n'est pas vraiment un ajout comme 1 + 5.

En ce qui concerne les utilisations pratiques, je suis sûr que d'autres pourraient commenter avec plus d'exemples, mais une utilisation courante consiste à créer un masque de bits de drapeaux pour quelque chose comme un système d'autorisation.

17
jcbwlkr