Golang 什么時候使用指針(Pointer
)?什么時候使用值(Value
)?對于go
開發者來說是一件頭疼的事情, 而且這個問題似乎沒有絕對的答案,那是否代表我們可以隨意使用呢?答案當然是否定的。本文我將試圖總結什么場景使用指針更合理。 在開始閱讀前,建議讀者先能夠清晰理解 Golang 指針、類型和值等概念。
本文并不是標準更不是唯一答案,而是自己根據使用經驗和社區的一些討論而總結的實踐
有下幾種情形,我們是否需要考慮使用指針:
type SliceHeader struct { Data uintptr Len int Cap int }
結構體定義的字段
方法中接受者
函數傳參
函數和方法返回值
這里我先給出使用一般準則,后面詳細介紹不同場景的細節。
方法通常使用指針作為接受者, 官方文檔的建議是:如果猶豫,請使用指針;
Slices,maps,channels,strings,function value and interface value 這些類型內部通過指針實現,再定一個指針指向這些類型的變量是多余的;
當一個結構體很復雜或者需要修改結構體使用指針,其他情況使用值,因為濫用指針會出現一些不可預料的情況;
CodeReviewComments
中建議傳輸(pass
)小型結構體, 如type Point struct{ latitude,longtitude float64 }
,使用原始類型,除非需要修改它們,理由有以下幾點
值語義可以避免歧義,因為指針類型的賦值;
犧牲干凈的語義換取速度并不是Golang
所推薦的做法,有時值傳遞性能更好,因為值傳遞能夠避免緩存遺漏和隊分配;
對于切片,不需要使用指針指向它,仍然可以改變其元素,這是因為切片內部是通過指針指向底層數組實現的, 標準庫中io.Reader.Read(p []byte)
函數中即通過傳遞值類型實現對切片 p 的修改。其實切片也可以當作是小型結構體,其定義如下, 在 64 位系統一個切片變量只占用 24 個字節。同樣地,對于 map 和 channel 類型,通常也不需要使用指針。
對于slices you'll reslice
(更改其起始元素位置/長度/容量),如內置函數func append(slice []Type, elems ...Type) []Type
, 接受一個切片,返回新的切片。返回一個新數組會讓調用者更清晰地理解函數的語義。
對于以下場景,使用指針是必須的:
如果結構體中包含sync.Mutex
獲取類似其他同步字段時,由于這類字段類型是禁止拷貝的,所以無論其方法的接受者, 還是其作為參數和返回值都應該使用指針:
type FIFO struct { lock sync.RWMutex cond sync.Cond items map[string]interface{} queue []string populated bool initialPopulationCount int keyFunc KeyFunc closed bool } // Close the queue. func (f *FIFO) Close() { f.lock.Lock() defer f.lock.Unlock() f.closed = true f.cond.Broadcast() } // NewFIFO returns a Store which can be used to queue up items to // process. func NewFIFO(keyFunc KeyFunc) *FIFO { f := &FIFO{ items: map[string]interface{}{}, queue: []string{}, keyFunc: keyFunc, } f.cond.L = &f.lock return f }
結構體中,除了需要考慮是否內存的占用之外,還需要考慮結構體的用途,一般主要分為工具結構體
和資源結構體
, 資源結構體
很容易理解,主要包括VO,DAO,Entity
等,這類結構體一般用于模塊或分層之間的通信。對于這類結構體, 如用于序列化的結構體,根據序列化協議和庫可能有所區別,如Golang
默認的Json
序列化協議對于是否顯示字段, 定義為指針可以可好解決。下面是kubernetes IngressSpec
的定義。
// IngressSpec describes the Ingress the user wishes to exist. type IngressSpec struct { IngressClassName *string `json:"ingressClassName,omitempty" protobuf:"bytes,4,opt,name=ingressClassName"` Backend *IngressBackend `json:"backend,omitempty" protobuf:"bytes,1,opt,name=backend"` TLS []IngressTLS `json:"tls,omitempty" protobuf:"bytes,2,rep,name=tls"` Rules []IngressRule `json:"rules,omitempty" protobuf:"bytes,3,rep,name=rules"` }
我們可以看到字段IngressClassName
定義為指針類型,就是為了序列化時更方便處理。 工具結構體
通常指非資源結構體
,主要是controller,config,factory
和自定義數據結構類型, 這些結構體往往更需要考慮內存占用。
使用指針并不是總能提升性能。使用指針可以避免值拷貝,減少棧內存的占用,由于堆內存的分配會導致GC
頻繁地執行,從而降低性能, 而值傳遞則不會。我們通過下面的實例Demo驗證。
以下兩個函數分別通過值拷貝和指針共享結構體:
分別進行進行基準測試:
執行基準測試,benchstat
工具需要下載,鏈接為perf
:
可以看出,這時候通過值傳遞的方式執行更快,內存占用也更少。關于如何分析Golang
程序和代碼段性能, 后續我會總結一篇博客多帶帶介紹。
本篇博文,簡單介紹怎樣如何使用指針更合理,其實很多場景都沒有標準答案,更多的是性能
和語義
兩者的權衡。 掌握Golang
中類型,值,指針類型等基礎概念才能優雅地使用指針。遇到疑惑的參考標準庫中的實現和借鑒一些成熟的項目中的實踐;
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/127929.html
摘要:小白前端一枚,最近在研究,記錄自己學習過程中的一些筆記,以及自己的理解。此外,結構體也支持嵌套。在函數聲明時,在函數名前放上一個變量,這個變量稱為方法的接收器,一般是結構體類型的。 小白前端一枚,最近在研究golang,記錄自己學習過程中的一些筆記,以及自己的理解。 go中包的依賴管理 go中的切片 byte 和 string go中的Map go中的struct結構體 go中的方...
摘要:和都是目前在各自領域最流行的開發語言之一。在機器學習數據分析領域成為必學語言。 showImg(https://segmentfault.com/img/remote/1460000019167290); Golang和Python都是目前在各自領域最流行的開發語言之一。 Golang其高效而又友好的語法,贏得了很多后端開發人員的青睞,最適用于高并發網絡編程的語言之一。 Python不...
摘要:在機器學習數據分析領域成為必學語言。不定長參數,支持不定長參數,用定義參數名,調用時多個參數將作為一個元祖傳遞到函數內返回函數結果。showImg(https://user-gold-cdn.xitu.io/2019/5/13/16ab0b937e7329d4); Golang和Python都是目前在各自領域最流行的開發語言之一。 Golang其高效而又友好的語法,贏得了很多后端開發人員的青...
閱讀 430·2024-11-07 18:25
閱讀 130681·2024-02-01 10:43
閱讀 923·2024-01-31 14:58
閱讀 893·2024-01-31 14:54
閱讀 82948·2024-01-29 17:11
閱讀 3224·2024-01-25 14:55
閱讀 2036·2023-06-02 13:36
閱讀 3133·2023-05-23 10:26