首頁  >  文章  >  後端開發  >  你知道在啥情況下使用Go指針嗎?

你知道在啥情況下使用Go指針嗎?

藏色散人
藏色散人轉載
2021-09-24 15:08:563010瀏覽

本文由go語言教學專欄為大家介紹在什麼情況下使用Go指標(Go Pointer),希望對需要的朋友有幫助!

你知道在啥情況下使用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 一致性

使用指標可以維持最新值。這可以保持 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)

鉴于指针的安全性,和值处理比指针处理更快,使用指针需要反复斟酌。

原文地址:https://medium.com/@meeusdylan/when-to-use-pointers-in-go-44c15fe04eac

译文地址:https://learnku.com/go/t/60923

以上是你知道在啥情況下使用Go指針嗎?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:learnku.com。如有侵權,請聯絡admin@php.cn刪除