Author: Regan Yue
Source: Hang Seng LIGHT cloud community
Go language learning checking and patching Day7
Zero. Preface
Because I have a weak foundation, I often encounter a lot of confused problems when using Go language, so I am determined to check and fill in the gaps in Go language. This [Go language checking and filling in] series is mainly to help novice Gopher better understand the mistakes, key and difficult points of Go language. I hope you can like it, praise it and pay attention to it!
1, On the execution order of defer
Let's take a look at this Code:
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 }
Previously, we introduced the order of defer execution, but I encountered a new problem today, so here we introduce the order of defer.
The running result of this program is:
21 28 21
We all know that the execution order of defer is first in and last out, so the execution order is C, B and A.
Defer FMT in B Println (person. Age) outputs 28. Why?
Because 28 is taken as the parameter of the defer() function, 28 will be pushed into the stack for caching, and it will be taken out when the defer statement is executed. So output 28
In A:
defer func(p *Person) { fmt.Println(p.age) }(person)
The defer() function caches the address of the structure Person. When the internal value of this address is changed later, the changed value in that address will be output later. Therefore, the value taken from the address when the B defer statement is executed is 29
The reason for the C defer statement is simple:
defer func() { fmt.Println(person.age) }()
This is a case of nonparametric anonymous functions. Closure reference, person Change will change.
2, Which slice statement is better? Why?
var a []int a := []int{}
The first declaration here is the nil slice, while the second declaration is to create an empty slice with zero length and capacity.
The first slice declaration method is better because it does not occupy space, while the second declaration will occupy some space.
3, Several methods of obtaining structure members
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) }
We can find:
p. p2 can get the member variables of the structure.
Why? The return value of the f() function is the pointer type, so p2 is the S type when p2 obtains * f() M can get its member variables.
The result of f() is a pointer, but as we said earlier, the first level pointer can be dereferenced automatically. So you can also access member variables.
4, The existence order of traversal map changes? Why?
Let's execute the following code several times to see the output results:
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) } }
The results of the first implementation are as follows:
5 five 0 zero 1 one 3 three 4 four
The results of the second implementation are as follows:
0 zero 1 one 3 three 4 four 5 five
The results of the third implementation are as follows:
4 four 5 five 0 zero 1 one 3 three
We find that the order of execution changes every time. This shows that the order of traversing the map is out of order. Why?
At runtime Mapiterinit has this Code:
// 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
We can see that the decision of where to start is based on the random number taken by fastrand(), so the random number is different every time, so the output order is also different.