Maison  >  Article  >  développement back-end  >  Une brève analyse des opérations sur les bits (opérateurs au niveau du bit) dans Golang

Une brève analyse des opérations sur les bits (opérateurs au niveau du bit) dans Golang

青灯夜游
青灯夜游avant
2023-02-24 19:42:254431parcourir

Une brève analyse des opérations sur les bits (opérateurs au niveau du bit) dans Golang

Au bon vieux temps, où la mémoire informatique était coûteuse et la puissance de traitement limitée, l'utilisation d'opérations binaires de type hacker pour traiter les informations était la méthode privilégiée (et dans certains cas, la seule). À ce jour, l’utilisation directe d’opérations sur bits fait toujours partie intégrante de nombreux domaines informatiques, tels que la programmation système de bas niveau, le traitement graphique, la cryptographie, etc.

Le langage de programmation Go prend en charge les opérateurs bit à bit suivants :

&   bitwise AND
 |   bitwise OR
 ^   bitwise XOR
&^   AND NOT
<<   left shift
>>   right shift

Le reste de cet article détaille chaque opérateur et des exemples de la façon dont ils sont utilisés.

& Opérateur

En Go, l'opérateur & effectue une opération ET au niveau du bit sur deux opérandes entiers. L'opération AND a les attributs suivants : & 运算符在两个整型操作数中执行按位 AND 操作。AND 操作具有以下属性:

Given operands a, b
AND(a, b) = 1; only if a = b = 1
               else = 0

AND 运算符具有选择性的把整型数据的位清除为 0 的好的效果。 例如,我们可以使用  & 运算符去清除(设置)最后 4 个最低有效位(LSB)全部为 0 。

func main() {
    var x uint8 = 0xAC    // x = 10101100
    x = x & 0xF0          // x = 10100000
}

所有的位运算都支持简写的赋值形式。 例如,前面的例子可以重写为如下。

func main() {
    var x uint8 = 0xAC    // x = 10101100
    x &= 0xF0             // x = 10100000
}

另外一个巧妙的技巧是:你可以用 & 操作去测试一个数字是奇数还是偶数。原因是当一个数字的二进制的最低位是 1 的时候,那他就是奇数。我们可以用一个数字和 1 进行 & 操作,然后在和 1 做 AND 运算,如果的到的结果是 1 ,那么这个原始的数字就是奇数

import (
    "fmt"
    "math/rand"
)
func main() {
    for x := 0; x < 100; x++ {
        num := rand.Int()
        if num&1 == 1 {
            fmt.Printf("%d is odd\n", num)
        } else {
            fmt.Printf("%d is even\n", num)
        }
    }
}

在  Playground  上运行上面的例子

| 操作符

| 对其整型操作数执行按位操作。回想一下操作符具备以下性质:

Given operands a, b
OR(a, b) = 1; when a = 1 or b = 1
              else = 0

我们可以利用按位操作符为给定的整数有选择地设置单个位。例如,在如下示例中我们使用按位将示例数(从低位到高位(MSB))中的第 3 ,第 7 和第 8 位置为 1 。

func main() {
    var a uint8 = 0
    a |= 196
    fmt.Printf("%b", a)
}

// 打印结果  11000100
            ^^   ^

练习场中可运行范例。

在使用位掩码技术为给定的整型数字设置任意位时,运算非常有用。例如,我们可以扩展之前的程序为变量 a 存储的值设置更多的位。

func main() {
    var a uint8 = 0
    a |= 196
    a |= 3
    fmt.Printf("%b", a)
}

// 打印结果 11000111

练习场中可以运行范例。

在前面的程序里,不仅要按位设置十进制的 196,而且要设置低位上的十进制 3。我们还可以继续(上更多的值)设置完所有的位。

位运算的配置用法

现在,回顾一下 AND(a, 1) = a 当且仅当 a = 1。 我们可以利用这个特性去查询其设置位的值。例如,在上述代码中 a & 196 会返回 196 是因为这几位的值在 a 中确实都存在。所以我们可以结合使用 OR 和  AND 运算的方式来分别设置和读取某位的配置值。.

接下来的源码片段演示了这个操作。函数 procstr 会转换字符串的内容。它需要两个参数:第一个, str,是将要被转换的字符串,第二个, conf,是一个使用位掩码的方式指定多重转换配置的整数。

const (
    UPPER  = 1 // 大写字符串
    LOWER  = 2 // 小写字符串
    CAP    = 4 // 字符串单词首字母大写
    REV    = 8 // 反转字符串
)

func main() {
    fmt.Println(procstr("HELLO PEOPLE!", LOWER|REV|CAP))
}

func procstr(str string, conf byte) string {
    // 反转字符串
    rev := func(s string) string {
        runes := []rune(s)
        n := len(runes)
        for i := 0; i < n/2; i++ {
            runes[i], runes[n-1-i] = runes[n-1-i], runes[i]
        }
        return string(runes)
    }

    // 查询配置中的位操作
    if (conf & UPPER) != 0 {
        str = strings.ToUpper(str)
    }
    if (conf & LOWER) != 0 {
        str = strings.ToLower(str)
    }
    if (conf & CAP) != 0 {
        str = strings.Title(str)
    }
    if (conf & REV) != 0 {
        str = rev(str)
    }
    return str
}

Playground上面运行代码.

上面的 procstr("HELLO PEOPLE!", LOWER|REV|CAP) 方法会把字符串变成小写,然后反转字符串,最后把字符串里面的单词首字母变成大写。这个功能是通过设置 conf 里的第二,三,四位的值为 14 来完成的。然后代码使用连续的 if 语句块来获取这些位操作进行对应的字符串转换。

^ 操作符

在 Go 中  按位 异或 操作是用 ^ 来表示的。 异或运算符有如下的特点:

Given operands a, b
XOR(a, b) = 1; only if a != b
     else = 0

异或

func main() {
    var a uint16 = 0xCEFF
    a ^= 0xFF00 // same a = a ^ 0xFF00
}

// a = 0xCEFF   (11001110 11111111)
// a ^=0xFF00   (00110001 11111111)

L'opérateur AND a le bon effet d'effacer sélectivement les bits des données entières à 0. Par exemple, nous pouvons utiliser l'opérateur & pour effacer (mettre) les 4 derniers bits les moins significatifs (LSB) à tous 0. 🎜
func main() {
    a, b := -12, 25
    fmt.Println("a and b have same sign?", (a ^ b) >= 0)
}
🎜Toutes les opérations au niveau du bit prennent en charge les formulaires d'affectation abrégés. Par exemple, l’exemple précédent pourrait être réécrit comme suit. 🎜
func main() {
    var a byte = 0x0F
    fmt.Printf("%08b\n", a)
    fmt.Printf("%08b\n", ^a)
}

// 打印结果
00001111     // var a
11110000     // ^a
🎜Une autre astuce intéressante est la suivante : vous pouvez utiliser l'opération & pour tester si un nombre est pair ou impair. La raison en est que lorsque le bit le plus bas d’un nombre est 1, il s’agit d’un nombre impair. Nous pouvons utiliser un nombre et 1 pour effectuer l'opération &, puis effectuer l'opération AND avec 1. Si le résultat est 1, alors le nombre d'origine est un nombre impair. 🎜
Given operands a, b
AND_NOT(a, b) = AND(a, NOT(b))
🎜Exécutez l'exemple ci-dessus sur Playground 🎜🎜🎜| Opérateur🎜🎜🎜| Effectue des opérations ou au niveau du bit sur ses opérandes entiers. Rappelez-vous que l'opérateur ou a les propriétés suivantes : 🎜
AND_NOT(a, 1) = 0; clears a
AND_NOT(a, 0) = a;
🎜 Nous pouvons utiliser l'opérateur ou au niveau du bit pour définir sélectivement des bits individuels pour un entier donné. Par exemple, dans l'exemple suivant, nous utilisons ou au niveau du bit pour définir les 3ème, 7ème et 8ème bits du numéro d'échantillon (du bit faible au bit élevé (MSB)) sur 1. 🎜
func main() {
    var a byte = 0xAB
     fmt.Printf("%08b\n", a)
     a &^= 0x0F
     fmt.Printf("%08b\n", a)
}

// 打印:
10101011
10100000
🎜Des exemples peuvent être exécutés sur le terrain d'entraînement. 🎜🎜L'opération ou est très utile lors de l'utilisation de techniques de masquage de bits pour définir des bits arbitraires pour un nombre entier donné. Par exemple, nous pouvons étendre le programme précédent pour définir plus de bits pour la valeur stockée dans la variable a. 🎜
Given integer operands a and n,
a << n; shifts all bits in a to the left n times
a >> n; shifts all bits in a to the right n times
🎜Des exemples peuvent être exécutés dans le domaine de pratique. 🎜🎜Dans le programme précédent, non seulement la décimale 196 doit être définie au niveau du bit, mais également la décimale 3 dans les bits faibles doit être définie. On peut continuer (plus de valeurs sur ou ) pour positionner tous les bits. 🎜🎜🎜 Utilisation de la configuration des opérations sur les bits🎜🎜🎜Maintenant, examinez AND(a, 1) = a si et seulement si a = 1 . Nous pouvons utiliser cette fonctionnalité pour interroger la valeur de son bit de réglage. Par exemple, dans le code ci-dessus, a & 196 renverra 196 car les valeurs de ces bits existent dans a. Nous pouvons donc utiliser les opérations OR et AND en combinaison pour définir et lire la valeur de configuration d'un certain bit respectivement. .🎜🎜L'extrait de code source suivant illustre cette opération. La fonction procstr convertit le contenu d'une chaîne. Il nécessite deux paramètres : le premier, str, est la chaîne à convertir, et le second, conf, est un masque de bits spécifiant plusieurs entiers pour convertir la configuration. 🎜
func main() {
    var a int8 = 3
    fmt.Printf("%08b\n", a)
    fmt.Printf("%08b\n", a<<1)
    fmt.Printf("%08b\n", a<<2)
    fmt.Printf("%08b\n", a<<3)
}

// 输出的结果:
00000011
00000110
00001100
00011000
🎜Exécutez le code ci-dessus Playground 🎜🎜Le procstr(" ci-dessus. HELLO PEOPLE!", LOWER|REV|CAP) convertira la chaîne en minuscules, puis inversera la chaîne et enfin changera la première lettre du mot de la chaîne en majuscule. Cette fonction est accomplie en définissant les deuxième, troisième et quatrième chiffres de conf sur 14. Le code utilise ensuite des blocs d'instructions if successifs pour obtenir ces opérations sur bits et effectuer les conversions de chaîne correspondantes. 🎜🎜🎜^ opérateur 🎜🎜🎜Dans Go, l'opération XOR au niveau du bit est représentée par ^. L'opérateur XOR a les caractéristiques suivantes : 🎜
func main() {
 var a uint8 = 120
 fmt.Printf("%08b\n", a)
 fmt.Printf("%08b\n", a>>1)
 fmt.Printf("%08b\n", a>>2)
}

// 打印:
01111000
00111100
00011110
🎜 Cette fonctionnalité de l'opérateur XOR peut être utilisée pour changer une valeur d'un bit binaire en une autre valeur. Par exemple, étant donné une valeur hexadécimale, nous pouvons utiliser le code suivant pour commuter les 8 premiers bits (en commençant par MSB) de la valeur. 🎜
func main() {
    var a uint16 = 0xCEFF
    a ^= 0xFF00 // same a = a ^ 0xFF00
}

// a = 0xCEFF   (11001110 11111111)
// a ^=0xFF00   (00110001 11111111)

在前面的代码片段中,与 1 进行异或的位被翻转(从 0 到 1 或从 1 到  0)。异或 运算的一个实际用途,例如,可以利用 异或运算去比较两个数字的符号是否一样。当 (a ^ b) ≥ 0 (或相反符号的 (a ^ b) 087d86671d155612d4f02e9d24199fd2> 操作符<p>与其他 C 的衍生语言类似, Go 使用 <code>036118feef1720dceedad2101673ba7c> 来表示左移运算符和右移运算符,如下所示:

Given integer operands a and n,
a << n; shifts all bits in a to the left n times
a >> n; shifts all bits in a to the right n times

例如,在下面的代码片段中变量 a00000011)的值将会左移位运算符分别移动三次。每次输出结果都是为了说明左移的目的。

func main() {
    var a int8 = 3
    fmt.Printf("%08b\n", a)
    fmt.Printf("%08b\n", a<<1)
    fmt.Printf("%08b\n", a<<2)
    fmt.Printf("%08b\n", a<<3)
}

// 输出的结果:
00000011
00000110
00001100
00011000

Playground 运行代码

注意每次移动都会将低位右侧补零。相对应,使用右移位操作符进行运算时,每个位均向右方移动,空出的高位补零,如下示例 (有符号数除外,参考下面的算术移位注释)。

func main() {
 var a uint8 = 120
 fmt.Printf("%08b\n", a)
 fmt.Printf("%08b\n", a>>1)
 fmt.Printf("%08b\n", a>>2)
}

// 打印:
01111000
00111100
00011110

练习场中可以运行范例。

可以利用左移和右移运算中,每次移动都表示一个数的 2 次幂这个特性,来作为某些乘法和除法运算的小技巧。例如,如下代码中,我们可以使用右移运算将 200(存储在变量 a 中)除以 2 。

func main() {
    a := 200
    fmt.Printf("%d\n", a>>1)
}

// 打印:
100

练习场 中可以运行范例。

或是通过左移 2 位,将一个数乘以4:

func main() {
    a := 12
    fmt.Printf("%d\n", a<<2)
}
// 打印:

48

练习场 中可以运行范例。

位移运算符提供了有趣的方式处理二进制值中特定位置的值。例如,下列的代码中,|<< 用于设置变量 a 的第三个 bit 位。

func main() {
    var a int8 = 8
    fmt.Printf("%08b\n", a)
    a = a | (1<<2)
    fmt.Printf("%08b\n", a)
}
// prints:
00001000
00001100

可以在 练习场 中运行代码示例。

或者,您可以组合位移运算符和 & 测试是否设置了第n位,如下面示例所示:

func main() {
    var a int8 = 12
    if a&(1<<2) != 0 {
        fmt.Println("take action")
    }
}

// 打印:
take action

练习场中运行代码。

使用  &^ 和位移运算符,我们可以取消设置一个值的某个位。例如,下面的示例将变量 a 的第三位置为 0 :

func main() {
    var a int8 = 13 
    fmt.Printf("%04b\n", a)
    a = a &^ (1 << 2)
    fmt.Printf("%04b\n", a)
}

// 打印:
1101
1001

练习场 中运行代码。

关于算术位移运算的笔记

当要位移的值(左操作数)是有符号值时,Go 自动应用算术位移。在右移操作期间,复制(或扩展)二进制补码符号位以填充位移的空隙。

Résumé

Comme d'autres opérateurs modernes, Go prend en charge tous les opérateurs de manipulation de bits binaires. Cet article fournit simplement des exemples des différents hacks pouvant être réalisés avec ces opérateurs. Vous pouvez trouver de nombreux articles sur le web, notamment Bit Twiddling Hacks de Sean Eron Anderson.

Suivez Vladim @vladimirvivien sur Twitter.

Si vous apprenez le Go, lisez le livre de Vladimir Vivien sur le Go intitulé Learning Go Programming .

Cet article a été initialement publié sur Medium par l'auteur Vladimir Vivien intitulé Bit Hacking with Go.

Adresse originale : https://dev.to/vladimirvivien/bit-hacking-with-go

Adresse de traduction : https://learnku.com/go/t/23460/bit-operation-of-go

Apprentissage recommandé : Tutoriel Golang

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer