乐趣区

关于golang:Go语言学习查缺补漏ing-Day7

作者:ReganYue

起源:恒生 LIGHT 云社区

Go 语言学习查缺补漏 ing Day7

零、前言

因为笔者根底不牢,在应用 Go 语言的时候常常遇到很多摸不着头脑的问题,所以笔者下定决心好好对 Go 语言进行查漏补缺,本【Go 语言查缺补漏 ing】系列次要是帮忙老手 Gopher 更好的理解 Go 语言的易错点、重难点。心愿各位看官可能喜爱,点点赞、关注一下呗!

一、再谈 defer 的执行程序

大家来看一看这段代码:

package main

import "fmt"

type Person struct {age int}
func main() {person := &Person{28}

    //A
    defer func(p *Person) {fmt.Println(p.age)
    }(person)
    //B
    defer fmt.Println(person.age)
    //C
    defer func() {fmt.Println(person.age)
    }()
    person.age = 21
}

后面咱们介绍过 defer 的执行程序,然而我明天又遇到新问题,于是这里又补充介绍这个 defer 的程序问题。

这个程序运行后果是:

21
28
21

咱们都晓得 defer 的执行程序是先进后出,所以执行程序是 C、B、A。

B 中 defer fmt.Println(person.age) 输入 28,为什么呢?

因为这里是将 28 作为 defer()函数的参数,会把 28 推入栈中进行缓存,失去执行这条 defer 语句时就把它拿进去。所以输入 28.

而 A 中:

defer func(p *Person) {fmt.Println(p.age)
    }(person)

defer()函数是将构造体 Person 的地址进行缓存,当后续扭转这个地址的内值时,后续输入时这里就会输入那个地址内扭转后的值。所以 B defer 语句执行时从地址中取出的值是 29.

而 C defer 语句理由很简略:

defer func() {fmt.Println(person.age)
    }()

就是无参匿名函数的一种情景。闭包援用,person.age 扭转就会扭转。

二、哪种切片的申明比拟好?为什么?

var a []int
a := []int{}

这里第一种申明的是 nil 切片,而第二种申明是创立一个长度以及容量为零的空切片。

第一种切片申明办法比拟好,因为它这种申明形式不占用空间,而第二种申明后会占用一部分空间。

三、获得构造体成员的几种办法

package main

import "fmt"

type S struct {m string}

func f() *S {return &S{"ReganYue"}
}
func main() {p := f()
    p2 := *f()
    fmt.Println(p.m, p2.m)
}

咱们运行可能发现:

p、p2 都能获取构造体的成员变量。

为什么呢?f()函数的返回值是指针类型,所以 p2 获取 *f()时,p2 是 S 类型,p2.m 能够获取其成员变量。

而 f()的后果是指针,不过咱们后面说过,一级指针可能主动进行解援用。所以也可能拜访成员变量。

四、遍历 map 的存在程序变动?为什么?

咱们执行上面这段代码屡次,看输入后果:

package main

import "fmt"

func main() {m := map[int]string{0: "zero", 1: "one", 3: "three", 4: "four", 5: "five"}
    for k, v := range m {fmt.Println(k, v)
    }
}

第一次执行后果如下:

5 five
0 zero
1 one
3 three
4 four

第二次执行后果如下:

0 zero
1 one
3 three
4 four
5 five

第三次执行后果如下:

4 four
5 five
0 zero
1 one
3 three

咱们发现每一次执行的程序都是变动的。这阐明遍历 map 的程序是无序的。为什么呢?

在 runtime.mapiterinit 中有这样一段代码:

// mapiterinit initializes the hiter struct used for ranging over maps.
// The hiter struct pointed to by 'it' is allocated on the stack
// by the compilers order pass or on the heap by reflect_mapiterinit.
// Both need to have zeroed hiter since the struct contains pointers.
func mapiterinit(t *maptype, h *hmap, it *hiter) {
    if raceenabled && h != nil {callerpc := getcallerpc()
        racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapiterinit))
    }

    if h == nil || h.count == 0 {return}

    if unsafe.Sizeof(hiter{})/sys.PtrSize != 12 {throw("hash_iter size incorrect") // see cmd/compile/internal/gc/reflect.go
    }
    it.t = t
    it.h = h

    // grab snapshot of bucket state
    it.B = h.B
    it.buckets = h.buckets
    if t.bucket.ptrdata == 0 {
        // Allocate the current slice and remember pointers to both current and old.
        // This preserves all relevant overflow buckets alive even if
        // the table grows and/or overflow buckets are added to the table
        // while we are iterating.
        h.createOverflow()
        it.overflow = h.extra.overflow
        it.oldoverflow = h.extra.oldoverflow
    }

    // decide where to start
    r := uintptr(fastrand())
    if h.B > 31-bucketCntBits {r += uintptr(fastrand()) << 31
    }
    it.startBucket = r & bucketMask(h.B)
    it.offset = uint8(r >> h.B & (bucketCnt - 1))

    // iterator state
    it.bucket = it.startBucket

    // Remember we have an iterator.
    // Can run concurrently with another mapiterinit().
    if old := h.flags; old&(iterator|oldIterator) != iterator|oldIterator {atomic.Or8(&h.flags, iterator|oldIterator)
    }

    mapiternext(it)
}
    // decide where to start
    r := uintptr(fastrand())
    if h.B > 31-bucketCntBits {r += uintptr(fastrand()) << 31
    }
    it.startBucket = r & bucketMask(h.B)
    it.offset = uint8(r >> h.B & (bucketCnt - 1))

    // iterator state
    it.bucket = it.startBucket

咱们能够看到,决定从哪开始是依据 fastrand()取随机数决定的,所以每次运行,随机数都不一样,所以输入程序也不一样。

退出移动版