2.1 运算符
全部运算符及分隔符列表
![](https://img.haomeiwen.com/i12661711/75c485628d8725bb.png)
优先级
一元运算符优先级最高,二元则分成五个级别,从高向低分别是:
![](https://img.haomeiwen.com/i12661711/04cb5a57c02c0e1d.png)
位运算符
![](https://img.haomeiwen.com/i12661711/62b71bdc96cd87d3.png)
需要注意的是,位清除是go独有的,和位异或是不同的。它将左右操作数对应二进制位都位1的重置为0(有些类似位图),以达到一次清楚多个标记的目的。
package main
import "fmt"
const (
exec byte = 1 << iota
write
read
freeze
)
func main() {
a := read | write | freeze
b := read | freeze | exec
c := a &^ b //相当于a ^ read ^ freeze, 但不包括exec
fmt.Printf("%04b &^ %04b = %04b\n", a, b, c)
}
输出:
1110 &^ 1101 = 0010
自增
自增自减不再是运算符,只能作为独立语句,不能用于表达式。
另,go中没有形如++a这种。
指针
内存地址和指针是不同的。
内存地址是内存中每个字节单元的唯一编号,而指针则是一个实体。 指针会分配内存空间,相当于一个专门用来保存地址的整型变量。
![](https://img.haomeiwen.com/i12661711/87ee83b5fccab6d8.png)
- 取值运算符“&”用于获取对象地址。
- 指针运算符“*”用于间接引用目标对象。
- 二级指针*T,如包含包名则协程package.T。
并非所有对象都能进行取地址操作,但变量总是能正确返回。指针运算符为左值时,我们可更新目标对象状态;而为右值时则是为了获取目标状态。
package main
func main() {
x := 10
var p *int = &x //获取地址,保存到指针变量
*p += 20 //用指针间接引用,并更新对象
println(&p,p, *p) //输出指针本身的地址,所存储的地址,以及目标对象
}
输出:
0xc04202ff68 0xc04202ff58 30
m:=map[string]int{"a":2}
println(&m["a"]) //报错:不能取地址
指针类型支持相等运算符,但不能作加减法和类型转换。如果两个指针指向同一地址或都为nil,那么他们相等。
x:=10
p:=&x
println(*p) //10
*p++
//var p2 *int = *p + 2 //报错,*int和int不能相加
println(*p) //11
p3 := &x
println(p == p3)
可通过unsafe.Pointer将指针转换为uintpr后进行加减法运算,但可能会造成非法访问。
Pinter 类似C中的void*万能指针,可用来转换指针类型。它能安全持有对象或对象成员,但uintptr不行。后者仅是一种特殊整型,并不引用目标对象,无法阻止垃圾回收器回收对象内存。
指针没有专门指向成员的“->”运算符,统一使用“.”选择表达式。
package main
func main() {
var a struct{
x int
}
//a:= struct {
// x int
//}{} //同上,不同写法
a.x = 100
p := &a
p.x += 100
println(p.x)
}
零长度对象的地址是否相等和具体的实现版本有关,不过肯定不等于nil。
package main
func main() {
var a,b struct{}
println(&a,&b)
println(&a == &b,&a == nil)
}
输出:
0xc04202ff70 0xc04202ff70
false false
即便长度为0,可该对象依然是“合法存在”的,拥有合法内存地址,这与nil语义完全不同。
在runtime/malloc.go里有个zerobase全局变量,所有通过mallocgc分配的零长度对象都使用该地址。不过上栗中,对象a,b在栈上分配,并未调用mallocgc函数。
网友评论