作者:ReganYue
起源:恒生LIGHT云社区
Go语言学习查缺补漏ing Day7
零、前言
因为笔者根底不牢,在应用Go语言的时候常常遇到很多摸不着头脑的问题,所以笔者下定决心好好对Go语言进行查漏补缺,本【Go语言查缺补漏ing】系列次要是帮忙老手Gopher更好的理解Go语言的易错点、重难点。心愿各位看官可能喜爱,点点赞、关注一下呗!
一、再谈defer的执行程序
大家来看一看这段代码:
package mainimport "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的程序问题。
这个程序运行后果是:
212821
咱们都晓得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 []inta := []int{}
这里第一种申明的是nil切片,而第二种申明是创立一个长度以及容量为零的空切片。
第一种切片申明办法比拟好,因为它这种申明形式不占用空间,而第二种申明后会占用一部分空间。
三、获得构造体成员的几种办法
package mainimport "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 mainimport "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 five0 zero1 one3 three4 four
第二次执行后果如下:
0 zero1 one3 three4 four5 five
第三次执行后果如下:
4 four5 five0 zero1 one3 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()取随机数决定的,所以每次运行,随机数都不一样,所以输入程序也不一样。