您的位置:首页 > 编程语言 > Go语言

Go的位操作

2018-03-07 12:30 309 查看

Go的位操作

在计算机内存和处理能力都成本昂贵的时代,位操作是(在某些情况下是唯一的)一种被优先选择来处理信息的方式。即使在今天,直接的位操作在一些情况下同样也很重要。例如:低级系统编程,图像处理,密码学等。

Go编程语言支持以下几种位操作,包括:

&位与
|位或
^位异或
&^位清空
<<左移
>>右移


文章后面的部分将详细讨论每一种操作,以及举例说明它们如何使用。

&运算

在Go中,&操作表示两个整数按位
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,而偶数的最低有效位为0。因此我们可以利用整数和数字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)
}
}
}


| 运算

|
表示两个整数按位
OR
OR
操作具有如下特点。

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


我们可以使用
OR
操作来有选择的设置给定整数的某个位。例如下面的例子,我们使用
OR
了设置MSB(最高有效位),第7位和第三位的值为1。

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


使用位掩码技术对一个给定的整数值任意位设置时,
OR
相当有用。例如,我们可以扩展之前的程序。下面的程序使用十进制的196和3来设置对应位。

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


位作为配置信息

我们可以结合
OR
AND
两种操作符,作为指定配置值和分别读取它们的一种方法。下面的源代码简要的说明如何使用。函数procstr转化string类型的内容,它需要两个参数:第一个参数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 {
//reverse 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)
}

//query config bits
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
}


上面主函数中调用方法procstr(“HELLO PEOPLE”,LOWER|REV|CAP),将会把字符串“HELLO PEOPLE”转化为小写,单词首字母大写,然后颠倒字符串前后顺序。此时参数conf的值为14,它的第二,第三和第四位设置了转化的这些条件。然后,代码使用连续的if语句块来提取这些配置,并对字符串应用对应的字符串转化。

^操作

Go中用
^
表示
XOR
,它具有以下属性:

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


异或定义的含义是它可以将一个值切换到另一个。例如,给定的16位值,我们可以使用下面代码切换前8位(从最高有效位开始)。

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

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


XOR
的一个实际用途就是判定符号,(a^b)>=0判定两个数的符号是否相同。

func main(){
a,b:=-12,25
fmt.Println("a and b have same sign?",(a^b)>=0)
}

/*prints:
a and b have same sign? false */


^作为按位补码(NOT)

与其他语言不同(c/c++,Java,Python,Javascript等),Go没有一个专门的一元补码操作符来对值取反,代替的是
XOR
操作符。

func main(){
var a byte=0x0F
fmt.Printf("%08b\n",a)
fmt.Printf("%08b\n",^a)
}

/* prints:
00001111  //var a
11110000  //^a


&^操作

&^
操作,也称
AND NOT
,它是一个使用
AND
NOT
的简写形式,具有以下特征:

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
操作来清除变量a的四个最低有效位。

func main(){
var a byte=0xAB
fmt.Printf("%08b\n",a)
a &^=0x0F
fmt.Printf("%08b\n",a)
}

/* prints:
10101011
10100000 */


<<和>>操作

类似于其他c派生语言,Go使用
<<
>>
来分别代表左移和右移操作。具体特点如下:

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


例如下面的例子中将a的值左移了三次,每次都将其移动的结果打印出来说明他的用途。

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)
}

/*prints:
00000011
00000110
00001100
00011000 */


记住每次移动,最低有效位用0填充,相反的,使用右移操作位时,最高有效位用0填充。(但是有符号位有些区别,详见文章后面的算数移位)

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

/*prints:
01111000
00111100
00011110
00001111 */


左移和右移操作的小技巧是可以在乘除法用算时,每次操作表示对原数进行乘以2或除以2例如下面将200右移一位,表示将其除以2。

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

/*prints:
100 */


或者用左移2位,表示乘以4。例如

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

/* prints:
48 */


位移操作给我们提供了一种有趣的方式来操作二进制值的指定位。例如下面的例子中用
|
<<
操作来设置变量a的第三位。

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)!={
fmt.Println("take action")
}
}

/* prints:
take action */


利用
&^
和移位操作,我们将第n位的值不设置。例如下面的例子将变量a的第3位没有设置。

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

/* prints:
1101
1001 */


算数移位

当一个有符号值(左移)移位时,Go自动使用算数移位。在右移操作中,符号位被拷贝(或者扩展)来填充移动的位置。

结论

与其他现代语言一样,Go也支持所有的位操作。这篇文章仅仅提供了一些有关位操作的简单例子。你可以在由Sean Eron Anderson写的Bit Twiddling Hacks文章中发掘更多诀窍。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息