Go語言學習查缺補漏ing Day7
來源:
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()取隨機數決定的,所以每次執行,隨機數都不一樣,所以輸出順序也不一樣。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70001864/viewspace-2846899/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Go語言學習查缺補漏ing Day8Go
- Go語言學習查缺補漏ing Day5Go
- Go語言學習查缺補漏ing Day6Go
- Go語言學習查缺補漏ing Day3Go
- Go語言學習查缺補漏ing Day2Go
- Go語言學習查缺補漏ing Day4Go
- Go語言學習查缺補漏ing Day1Go
- 查漏補缺
- [學習筆記]TypeScript查缺補漏(一):類筆記TypeScript
- HashMap 查漏補缺HashMap
- Typescript 查缺補漏TypeScript
- iOS 查漏補缺 - PerformSelectoriOSperformSelector
- Flutter查漏補缺1Flutter
- JavaScript Promise查缺補漏JavaScriptPromise
- ROS灰灰的日常查漏補缺ROS
- [學習筆記]TypeScript查缺補漏(二):型別與控制流分析筆記TypeScript型別
- SLAM 灰灰restudy及查漏補缺—octomapSLAMREST
- 正規表示式的查漏補缺
- C++灰灰的日常查漏補缺C++
- shell基礎知識查缺補漏
- C# 執行緒查漏補缺C#執行緒
- 前端面試查漏補缺--(十三) 記憶體洩漏前端面試記憶體
- 前端面試查漏補缺--(八) 前端加密前端面試加密
- 前端面試查漏補缺--(十五) Event Loop前端面試OOP
- C# 執行緒同步查漏補缺C#執行緒
- 技術棧查漏補缺——架構師架構
- go語言學習Go
- 前端面試查漏補缺--(十) 前端鑑權前端面試
- 前端面試查漏補缺--(九) HTTP與HTTPS前端面試HTTP
- 【查漏補缺】那些漏掉的面試知識面試
- 前端面試查漏補缺--(四) 前端本地儲存前端面試
- 前端面試查漏補缺--(二) 垃圾回收機制前端面試
- Redis基礎你掌握多少了?來查漏補缺?Redis
- [筆記](更新中)CSP-S 2024 查漏補缺筆記
- go語言學習-介面Go
- go語言學習-goroutineGo
- Go語言學習——mapGo
- [查漏補缺]正規表示式匹配演算法演算法