您好,登錄后才能下訂單哦!
在什么情況下使用Go指針,針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。
Go 代碼中使用指針對于新手來說不太友好,尤其有時很難區分使用場景。
我認為使用指針時最大的誤解之一就是覺得 Go 中指針和 C 語言中的指針非常像。然而,事情并非如此。指針在 Go 中不像它們在 C/C++ 中那樣工作。
本文將一起探討如何正確使用 Go 指針(Go Pointer)。
普遍認為當使用指針時,應用將會運行的更快,因為這將避免一直進行值復制。在 Go 中我們有同樣的想法也就不足為奇了。
然而,Go 中指針傳遞通常都比值傳遞慢。這是 Go 是具有垃圾回收機制語言的一個結果。當你向一個函數傳遞指針, Go 需要執行逃逸分析來確定變量是應該存儲在堆中,還是棧中。 這已經增加了一些額外的開銷,但除此之外變量可以存儲在堆中。當你在堆中存儲一個變量,你也就在 GC 執行時損失了時間。
Go 的一個便捷的功能是你可以通過執行命令 go build -gcflags="-m"
來檢查逃逸分析做了什么。如果你這樣做,Go 將告訴你一個變量是否逃到堆上:
./main.go:44:20: greet ... argument does not escape ./main.go:44:21: greeting escapes to heap ./main.go:44:21: name escapes to heap
如果一個變量沒有逃逸到堆中,它就在棧中。棧是不需要垃圾回收器來清除變量的,它只做 push/pop
操作。
如果任何內容都進行值傳遞,那么將一直在棧中做相關處理,這不會帶來垃圾回收方面的開銷。(GC 將按默認設置運行。堆中內容越少使得 GC 需要做的事情也越少)。
現在你知道了吧,使用指針反而會降低性能,那么什么時候需要使用指針呢?
指針是否一直表現的比值傳遞差呢?顯然不是這樣的。對大的數據結構進行處理時,指針將發揮作用。這樣可能會使得垃圾回收的開銷被拷貝大量數據的開銷抵消掉。
當我提到這點時,總是被問到‘那個大數據應該多大’?
我覺得這里沒有一個固定的數值,凡是與性能相關的,都應該對其進行基準測試。 Go 有內置的強大的基準測試工具,完全可以利用起來
唯一能修改函數參數的方式是傳指針。默認對值的修改都是在副本上進行的。因此這些修改不能在調用它的函數中體現。
看下面的代碼:
type person struct { name string }func main() { p := person{"Richard"} rename(p) fmt.Println(p) }func rename(p person) { p.name = "test" }
輸出是 Richard
,因為對 person 的修改是在它的副本上進行的。如果要改變底層 person 對象的值,需要使用指針。
func main() { p := person{"Richard"} rename(&p) fmt.Println(p) }func rename(p *person) { p.name = "test" }
如上,輸出 test
。可變性是指針在 Go 中使用的一種情景。這是否是好事,還需要討論。
使用指針可以維持最新值。這可以保持 API 一致性,即使不是所有的方法都改變它的值。
因此,這個:
func (p *person) rename(s string) { p.name = s }func (p *person) printName() { fmt.Println(p.name) }
優于
func (p *person) rename(s string) { p.name = s }func (p person) printName() { fmt.Println(p.name) }
雖然為了一致性并不需要在 printName
中使用指針。但是這將使得 API 更簡單,避免去記到底哪里需要引用。
一般值在使用時,具有默認零值。但有些情景需要知道某個事物是缺少或未填充值。例如一個結構體包含學生的考試分數,如果結構體是空且有分數 0 ,這表示這個學生考的不好,還是壓根沒有參加考試呢?
指針的默認零值是 nil
指針,表示沒有設置值。也可以像下面這樣實現這種要求:
type exam struct { score int present bool }
使用單獨的 present
字段表示學生沒有參加考試。
這多少會有些主觀意識在里面。不同的人對編程有不同的理解,所以不要求大家觀念一致
我相信讓 Go 中值盡量有默認值是有意義的。這也許不適用所有的場景,但在我開來這可以避免造成一個大的事故。使用值替代指針不會因空指針造成 Tony Hoare 的 “百萬美元失誤”。
默認零值是很有用的,可以避免大量的聲明。
另一個好處是易變性造成的問題比它解決的問題多的得多。易變性給函數帶來的副作用同時使得調試變得更加困難。 通過讓函數返回修改之后的結構體,可以避免這種突變。
重寫之前的例子
func main() { p := person{"richard"} p = rename(p) fmt.Println(p) }func rename(p person) person { p.name = "test" return p }
這也是 append
如何工作的,所以并不陌生。
x := []int{1,2} x = append(x, 3) x = append(x, 4)
鑒于指針的安全性,和值處理比指針處理更快,使用指針需要反復斟酌。
關于在什么情況下使用Go指針問題的解答就分享到這里了,希望以上內容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注億速云行業資訊頻道了解更多相關知識。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。