第二章 运算

作者: 牧码人爱跑马 | 来源:发表于2018-07-11 21:12 被阅读0次

    2.1 运算符

    全部运算符及分隔符列表

    运算符及分隔符列表.png

    优先级

    一元运算符优先级最高,二元则分成五个级别,从高向低分别是:


    优先级.png

    位运算符

    位运算符.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这种。

    指针

    内存地址和指针是不同的。

    内存地址是内存中每个字节单元的唯一编号,而指针则是一个实体。 指针会分配内存空间,相当于一个专门用来保存地址的整型变量。

    内存地址和指针.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函数。

    相关文章

      网友评论

        本文标题:第二章 运算

        本文链接:https://www.haomeiwen.com/subject/exyhpftx.html