1. 程式人生 > >Golang查缺補漏(一)

Golang查缺補漏(一)

效率 for rem 函數參數 mov printf col .com println


Go語言高級編程(Advanced Go Programming)

Go語言高級編程(Advanced Go Programming)


  1. golang都是傳值,與其他語言不同的是數組作為參數時,也是傳值!
    但是,lambda閉包引用的外部變量,則是引用!
    另外,slice、string雖然也是傳值,但其本質上是一個引用信息(指針、長度等信息),不涉及具體的內容。

  2. 空數組 [0]int{}、空結構體struct{}{} 本質上都不占用內存空間,非常好用,當然後者用的更多。

  3. 空切片 sc[:0] 在特定場合下非常有用!例如字符串的去除空格、過濾等功能。見代碼

    func RemoveBlank(s string)[]byte{ b := s[:0] for(i := 0; i < len(s); i++){ if s[i] != ‘ ‘{ b = append(b, s[i]) //在原內存上操作,且肯定不會超出,效率非常高 } } }

  4. 切片底層是數組,如果切片一直存在,那麽數組也不會被釋放。所以這裏可能存在嚴重的內存浪費行為。
    例如從文件內容中查找指定的內容,則可能會發生這種情況:讀取了整個文件,返回了一個很大的[]byte,但最終返回的是一個很小的[]byte,這時候底層的數組不會被釋放!
    這時,最好就是將獲取到的結果append到全新的切片中。

    func FindPhoneNumber(file string)[]byte{ b, _ := ioutil.ReadFile(file) //return regexp.MustCompile(`[0-9]+`).Find(b) //FIXME 不推薦!存在浪費的可能! b = regexp.MustCompile(`[0-9]+`).Find(b) return append([]byte{}, b...) //這樣就OK了 }

    另外,還有一種可能,就是切片中存的是指針,當縮小切片的範圍時,範圍外的指針仍然存在!同樣會阻礙GC的進行!

    //bad demo var a[]*int{ ... } a = a[:len(a)-1] //註意:此時最後一個元素仍然存在,不會被GC!

    那需要怎麽做呢? 首先將不需要的元素置為nil,再切片就OK啦:

    //good demo var a[]*int{ ... } a[len(a)-1] = nil // a = a[:len(a)-1]

    如果切片存在的周期很短的話,可以不用刻意處理這個問題!

  5. 在main.main函數執行之前所有代碼都運行在同一個goroutine,也就是程序的主系統線程中。
    因此,如果某個init

    函數內部用go關鍵字啟動了新的goroutine的話,新的goroutine只有在進入main.main函數之後才可能被執行到。

    package main import ( "fmt" "time" ) // main.main()執行之前,只有一個main goroutine,因此,哪怕init中有goroutine,也只能等到main.main()執行時才能執行 func init() { fmt.Println("init in") go func() { fmt.Println("init goroutine in") //看看這行信息出現的時間 time.Sleep(time.Second * 5) fmt.Println("init goroutine out") }() fmt.Println("init out") } func main() { fmt.Println("-----main") time.Sleep(time.Second * 10) }

  6. Go語言函數的遞歸調用深度邏輯上沒有限制,函數調用的棧是不會出現溢出錯誤的(相對而言),因為Go語言運行時會根據需要動態地調整函數棧的大小。每個goroutine剛啟動時只會分配很小的棧(4或8KB,具體依賴實現),根據需要動態調整棧的大小,棧最大可以達到GB級(依賴具體實現)。

    package main import "fmt" //golang 的棧不能超過 1000000000-byte limit func main() { defer func() { if e := recover(); e != nil { fmt.Println(e) } }() n := 500000000 r := factorial(n) fmt.Printf("%v 的階乘是:%v\n", n, r) } //factorial 計算階乘 - 不考慮溢出 func factorial(n int) int { if n == 0 { return 1 } return n * factorial(n-1) }

    因為,Go語言函數的棧不會溢出,所以普通Go程序員已經很少需要關心棧的運行機制的。
    在Go語言規範中甚至故意沒有講到棧和堆的概念。
    我們無法知道函數參數或局部變量到底是保存在棧中還是堆中,我們只需要知道它們能夠正常工作就可以了。

  7. 因為不需要考慮堆、棧問題,所以完全可以這麽寫

    func tmp()*int{ x := 10 return &x //這是C/CPP中是完全不允許的,因為局部變量在棧上,函數執行完畢就會被銷毀 - 但是golang足夠智能,會自動在堆上創建 - 如果你非要關註堆、棧的話。 }

    golang足夠智能,會自行判斷。


未完待續

Golang查缺補漏(一)