Maison > Article > développement back-end > Une explication détaillée des opérations sur les bits dans Golang
Cet article vous donnera une compréhension approfondie des opérations sur les bits dans Golang et présentera des cas détaillant chaque opérateur et comment les utiliser. J'espère qu'il sera utile à tout le monde !
Au bon vieux temps, lorsque 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. [Recommandations associées : Tutoriel vidéo Go]
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.
Dans 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 de bits 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🎜🎜🎜🎜🎜🎜| Operator🎜🎜🎜
| effectue des opérations <code> 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(&amp;amp;amp;amp;amp;amp;amp;quot;%08b\n&amp;amp;amp;amp;amp;amp;amp;quot;, a) a &amp;amp;amp;amp;amp;amp;amp;amp;^= 0x0F fmt.Printf(&amp;amp;amp;amp;amp;amp;amp;quot;%08b\n&amp;amp;amp;amp;amp;amp;amp;quot;, 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 &amp;amp;amp;amp;amp;amp;amp;lt;&amp;amp;amp;amp;amp;amp;amp;lt; n; shifts all bits in a to the left n times a &amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;gt; n; shifts all bits in a to the right n times🎜Des exemples peuvent être exécutés dans le Playing Ground🎜. 🎜🎜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 &amp;amp;amp;amp;amp;amp;amp; 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(&amp;amp;amp;amp;amp;amp;amp;quot;%08b\n&amp;amp;amp;amp;amp;amp;amp;quot;, a) fmt.Printf(&amp;amp;amp;amp;amp;amp;amp;quot;%08b\n&amp;amp;amp;amp;amp;amp;amp;quot;, a&amp;amp;amp;amp;amp;amp;amp;lt;&amp;amp;amp;amp;amp;amp;amp;lt;1) fmt.Printf(&amp;amp;amp;amp;amp;amp;amp;quot;%08b\n&amp;amp;amp;amp;amp;amp;amp;quot;, a&amp;amp;amp;amp;amp;amp;amp;lt;&amp;amp;amp;amp;amp;amp;amp;lt;2) fmt.Printf(&amp;amp;amp;amp;amp;amp;amp;quot;%08b\n&amp;amp;amp;amp;amp;amp;amp;quot;, a&amp;amp;amp;amp;amp;amp;amp;lt;&amp;amp;amp;amp;amp;amp;amp;lt;3) } // 输出的结果: 00000011 00000110 00001100 00011000🎜Exécutez le code sur le Playground.🎜🎜La méthode
procstr(&amp;amp;quot;HELLO PEOPLE!&amp;amp;quot;, LOWER|REV|CAP)
ci-dessus convertira la chaîne en minuscules, puis inversera la chaîne, et enfin Mettez en majuscule la première lettre des mots de la chaîne. Cette fonction est accomplie en définissant les deuxième, troisième et quatrième bits de conf
à une valeur de 14. Le code utilise ensuite des blocs d'instructions if successifs pour obtenir ces opérations sur les bits et effectuer les conversions de chaînes 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(&amp;amp;amp;amp;amp;amp;amp;quot;%08b\n&amp;amp;amp;amp;amp;amp;amp;quot;, a) fmt.Printf(&amp;amp;amp;amp;amp;amp;amp;quot;%08b\n&amp;amp;amp;amp;amp;amp;amp;quot;, a&amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;gt;1) fmt.Printf(&amp;amp;amp;amp;amp;amp;amp;quot;%08b\n&amp;amp;amp;amp;amp;amp;amp;quot;, a&amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;gt;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) < 0
)为 true
的时候,两个整数 a,b 具有相同的符号,如下面的程序所示:
func main() { a, b := -12, 25 fmt.Println(&amp;amp;amp;amp;amp;amp;amp;quot;a and b have same sign?&amp;amp;amp;amp;amp;amp;amp;quot;, (a ^ b) &amp;amp;amp;amp;amp;amp;amp;gt;= 0) }在 Go 的 Playground运行代码。
当执行上面这个程序的时候,将会打印出:
a and b have same sign? false
。在 Go Playground 上修改程序里 a ,b 的符号,以便看到不同的结果。^ 作为取反位运算符 (非)
不像其他语言 (c/c++,Java,Python,Javascript,等), Go 没有专门的一元取反位运算符。取而代之的是,
XOR
运算符^
,也可作为一元取反运算符作用于一个数字。对于给定位 x,在 Go 中 x = 1 ^ x 可以翻转该位。在以下的代码段中我们可以看到使用^a
获取变量a
的取反值的操作。func main() { var a byte = 0x0F fmt.Printf(&amp;amp;amp;amp;amp;amp;amp;quot;%08b\n&amp;amp;amp;amp;amp;amp;amp;quot;, a) fmt.Printf(&amp;amp;amp;amp;amp;amp;amp;quot;%08b\n&amp;amp;amp;amp;amp;amp;amp;quot;, ^a) } // 打印结果 00001111 // var a 11110000 // ^a在练习场中可以运行范例。
&amp;amp;amp;amp;amp;amp;amp;^ 操作符
&amp;amp;amp;amp;amp;amp;amp;^
操作符意为与非
,是与
和非
操作符的简写形式,它们定义如下。Given operands a, b AND_NOT(a, b) = AND(a, NOT(b))如果第二个操作数为 1 那么它则具有清除第一个操作数中的位的趣味特性。
AND_NOT(a, 1) = 0; clears a AND_NOT(a, 0) = a;接下来的代码片段使用
AND NOT
操作符,将变量值1010 1011
变为1010 0000
,清除了操作数上的低四位。func main() { var a byte = 0xAB fmt.Printf(&amp;amp;amp;amp;amp;amp;amp;quot;%08b\n&amp;amp;amp;amp;amp;amp;amp;quot;, a) a &amp;amp;amp;amp;amp;amp;amp;amp;^= 0x0F fmt.Printf(&amp;amp;amp;amp;amp;amp;amp;quot;%08b\n&amp;amp;amp;amp;amp;amp;amp;quot;, a) } // 打印: 10101011 10100000在练习场中运行范例。
<< 和 >> 操作符
与其他 C 的衍生语言类似, Go 使用
<<
和来表示左移运算符和右移运算符,如下所示:
Given integer operands a and n, a &amp;amp;amp;amp;amp;amp;amp;lt;&amp;amp;amp;amp;amp;amp;amp;lt; n; shifts all bits in a to the left n times a &amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;gt; n; shifts all bits in a to the right n times例如,在下面的代码片段中变量
a
(00000011
)的值将会左移位运算符分别移动三次。每次输出结果都是为了说明左移的目的。func main() { var a int8 = 3 fmt.Printf(&amp;amp;amp;amp;amp;amp;amp;quot;%08b\n&amp;amp;amp;amp;amp;amp;amp;quot;, a) fmt.Printf(&amp;amp;amp;amp;amp;amp;amp;quot;%08b\n&amp;amp;amp;amp;amp;amp;amp;quot;, a&amp;amp;amp;amp;amp;amp;amp;lt;&amp;amp;amp;amp;amp;amp;amp;lt;1) fmt.Printf(&amp;amp;amp;amp;amp;amp;amp;quot;%08b\n&amp;amp;amp;amp;amp;amp;amp;quot;, a&amp;amp;amp;amp;amp;amp;amp;lt;&amp;amp;amp;amp;amp;amp;amp;lt;2) fmt.Printf(&amp;amp;amp;amp;amp;amp;amp;quot;%08b\n&amp;amp;amp;amp;amp;amp;amp;quot;, a&amp;amp;amp;amp;amp;amp;amp;lt;&amp;amp;amp;amp;amp;amp;amp;lt;3) } // 输出的结果: 00000011 00000110 00001100 00011000在 Playground 运行代码
注意每次移动都会将低位右侧补零。相对应,使用右移位操作符进行运算时,每个位均向右方移动,空出的高位补零,如下示例 (有符号数除外,参考下面的算术移位注释)。
func main() { var a uint8 = 120 fmt.Printf(&amp;amp;amp;amp;amp;amp;amp;quot;%08b\n&amp;amp;amp;amp;amp;amp;amp;quot;, a) fmt.Printf(&amp;amp;amp;amp;amp;amp;amp;quot;%08b\n&amp;amp;amp;amp;amp;amp;amp;quot;, a&amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;gt;1) fmt.Printf(&amp;amp;amp;amp;amp;amp;amp;quot;%08b\n&amp;amp;amp;amp;amp;amp;amp;quot;, a&amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;gt;2) } // 打印: 01111000 00111100 00011110在 练习场中可以运行范例。
可以利用左移和右移运算中,每次移动都表示一个数的 2 次幂这个特性,来作为某些乘法和除法运算的小技巧。例如,如下代码中,我们可以使用右移运算将
200
(存储在变量 a 中)除以 2 。func main() { a := 200 fmt.Printf(&amp;amp;amp;amp;amp;amp;amp;quot;%d\n&amp;amp;amp;amp;amp;amp;amp;quot;, a&amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;gt;1) } // 打印: 100在 练习场 中可以运行范例。
或是通过左移 2 位,将一个数乘以4:
func main() { a := 12 fmt.Printf(&amp;amp;amp;amp;amp;amp;amp;quot;%d\n&amp;amp;amp;amp;amp;amp;amp;quot;, a&amp;amp;amp;amp;amp;amp;amp;lt;&amp;amp;amp;amp;amp;amp;amp;lt;2) } // 打印: 48在 练习场 中可以运行范例。
位移运算符提供了有趣的方式处理二进制值中特定位置的值。例如,下列的代码中,
|
和<<
用于设置变量a
的第三个 bit 位。func main() { var a int8 = 8 fmt.Printf(&amp;amp;amp;amp;amp;amp;amp;quot;%08b\n&amp;amp;amp;amp;amp;amp;amp;quot;, a) a = a | (1&amp;amp;amp;amp;amp;amp;amp;lt;&amp;amp;amp;amp;amp;amp;amp;lt;2) fmt.Printf(&amp;amp;amp;amp;amp;amp;amp;quot;%08b\n&amp;amp;amp;amp;amp;amp;amp;quot;, a) } // prints: 00001000 00001100可以在 练习场 中运行代码示例。
或者,您可以组合位移运算符和
&amp;amp;amp;amp;amp;amp;amp;
测试是否设置了第n位,如下面示例所示:func main() { var a int8 = 12 if a&amp;amp;amp;amp;amp;amp;amp;amp;(1&amp;amp;amp;amp;amp;amp;amp;lt;&amp;amp;amp;amp;amp;amp;amp;lt;2) != 0 { fmt.Println(&amp;amp;amp;amp;amp;amp;amp;quot;take action&amp;amp;amp;amp;amp;amp;amp;quot;) } } // 打印: take action在 练习场中运行代码。
使用
&amp;amp;amp;amp;amp;amp;amp;^
和位移运算符,我们可以取消设置一个值的某个位。例如,下面的示例将变量 a 的第三位置为 0 :func main() { var a int8 = 13 fmt.Printf(&amp;amp;amp;amp;amp;amp;amp;quot;%04b\n&amp;amp;amp;amp;amp;amp;amp;quot;, a) a = a &amp;amp;amp;amp;amp;amp;amp;amp;^ (1 &amp;amp;amp;amp;amp;amp;amp;lt;&amp;amp;amp;amp;amp;amp;amp;lt; 2) fmt.Printf(&amp;amp;amp;amp;amp;amp;amp;quot;%04b\n&amp;amp;amp;amp;amp;amp;amp;quot;, 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 en anglais : https://dev.to/vladimirvivien/bit-hacking-with-go
Pour plus de connaissances liées à la programmation, veuillez visiter : Vidéo de programmation ! !
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!