Golang 什么時(shí)候使用指針(Pointer
)?什么時(shí)候使用值(Value
)?對(duì)于go
開發(fā)者來說是一件頭疼的事情, 而且這個(gè)問題似乎沒有絕對(duì)的答案,那是否代表我們可以隨意使用呢?答案當(dāng)然是否定的。本文我將試圖總結(jié)什么場景使用指針更合理。 在開始閱讀前,建議讀者先能夠清晰理解 Golang 指針、類型和值等概念。
本文并不是標(biāo)準(zhǔn)更不是唯一答案,而是自己根據(jù)使用經(jīng)驗(yàn)和社區(qū)的一些討論而總結(jié)的實(shí)踐
有下幾種情形,我們是否需要考慮使用指針:
type SliceHeader struct { Data uintptr Len int Cap int }
結(jié)構(gòu)體定義的字段
方法中接受者
函數(shù)傳參
函數(shù)和方法返回值
這里我先給出使用一般準(zhǔn)則,后面詳細(xì)介紹不同場景的細(xì)節(jié)。
方法通常使用指針作為接受者, 官方文檔的建議是:如果猶豫,請(qǐng)使用指針;
Slices,maps,channels,strings,function value and interface value 這些類型內(nèi)部通過指針實(shí)現(xiàn),再定一個(gè)指針指向這些類型的變量是多余的;
當(dāng)一個(gè)結(jié)構(gòu)體很復(fù)雜或者需要修改結(jié)構(gòu)體使用指針,其他情況使用值,因?yàn)闉E用指針會(huì)出現(xiàn)一些不可預(yù)料的情況;
CodeReviewComments
中建議傳輸(pass
)小型結(jié)構(gòu)體, 如type Point struct{ latitude,longtitude float64 }
,使用原始類型,除非需要修改它們,理由有以下幾點(diǎn)
值語義可以避免歧義,因?yàn)橹羔橆愋偷馁x值;
犧牲干凈的語義換取速度并不是Golang
所推薦的做法,有時(shí)值傳遞性能更好,因?yàn)橹祩鬟f能夠避免緩存遺漏和隊(duì)分配;
對(duì)于切片,不需要使用指針指向它,仍然可以改變其元素,這是因?yàn)榍衅瑑?nèi)部是通過指針指向底層數(shù)組實(shí)現(xiàn)的, 標(biāo)準(zhǔn)庫中io.Reader.Read(p []byte)
函數(shù)中即通過傳遞值類型實(shí)現(xiàn)對(duì)切片 p 的修改。其實(shí)切片也可以當(dāng)作是小型結(jié)構(gòu)體,其定義如下, 在 64 位系統(tǒng)一個(gè)切片變量只占用 24 個(gè)字節(jié)。同樣地,對(duì)于 map 和 channel 類型,通常也不需要使用指針。
對(duì)于slices you'll reslice
(更改其起始元素位置/長度/容量),如內(nèi)置函數(shù)func append(slice []Type, elems ...Type) []Type
, 接受一個(gè)切片,返回新的切片。返回一個(gè)新數(shù)組會(huì)讓調(diào)用者更清晰地理解函數(shù)的語義。
對(duì)于以下場景,使用指針是必須的:
如果結(jié)構(gòu)體中包含sync.Mutex
獲取類似其他同步字段時(shí),由于這類字段類型是禁止拷貝的,所以無論其方法的接受者, 還是其作為參數(shù)和返回值都應(yīng)該使用指針:
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 }
結(jié)構(gòu)體中,除了需要考慮是否內(nèi)存的占用之外,還需要考慮結(jié)構(gòu)體的用途,一般主要分為工具結(jié)構(gòu)體
和資源結(jié)構(gòu)體
, 資源結(jié)構(gòu)體
很容易理解,主要包括VO,DAO,Entity
等,這類結(jié)構(gòu)體一般用于模塊或分層之間的通信。對(duì)于這類結(jié)構(gòu)體, 如用于序列化的結(jié)構(gòu)體,根據(jù)序列化協(xié)議和庫可能有所區(qū)別,如Golang
默認(rèn)的Json
序列化協(xié)議對(duì)于是否顯示字段, 定義為指針可以可好解決。下面是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
定義為指針類型,就是為了序列化時(shí)更方便處理。 工具結(jié)構(gòu)體
通常指非資源結(jié)構(gòu)體
,主要是controller,config,factory
和自定義數(shù)據(jù)結(jié)構(gòu)類型, 這些結(jié)構(gòu)體往往更需要考慮內(nèi)存占用。
使用指針并不是總能提升性能。使用指針可以避免值拷貝,減少棧內(nèi)存的占用,由于堆內(nèi)存的分配會(huì)導(dǎo)致GC
頻繁地執(zhí)行,從而降低性能, 而值傳遞則不會(huì)。我們通過下面的實(shí)例Demo驗(yàn)證。
以下兩個(gè)函數(shù)分別通過值拷貝和指針共享結(jié)構(gòu)體:
分別進(jìn)行進(jìn)行基準(zhǔn)測試:
執(zhí)行基準(zhǔn)測試,benchstat
工具需要下載,鏈接為perf
:
可以看出,這時(shí)候通過值傳遞的方式執(zhí)行更快,內(nèi)存占用也更少。關(guān)于如何分析Golang
程序和代碼段性能, 后續(xù)我會(huì)總結(jié)一篇博客多帶帶介紹。
本篇博文,簡單介紹怎樣如何使用指針更合理,其實(shí)很多場景都沒有標(biāo)準(zhǔn)答案,更多的是性能
和語義
兩者的權(quán)衡。 掌握Golang
中類型,值,指針類型等基礎(chǔ)概念才能優(yōu)雅地使用指針。遇到疑惑的參考標(biāo)準(zhǔn)庫中的實(shí)現(xiàn)和借鑒一些成熟的項(xiàng)目中的實(shí)踐;
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/127929.html
摘要:小白前端一枚,最近在研究,記錄自己學(xué)習(xí)過程中的一些筆記,以及自己的理解。此外,結(jié)構(gòu)體也支持嵌套。在函數(shù)聲明時(shí),在函數(shù)名前放上一個(gè)變量,這個(gè)變量稱為方法的接收器,一般是結(jié)構(gòu)體類型的。 小白前端一枚,最近在研究golang,記錄自己學(xué)習(xí)過程中的一些筆記,以及自己的理解。 go中包的依賴管理 go中的切片 byte 和 string go中的Map go中的struct結(jié)構(gòu)體 go中的方...
摘要:而且數(shù)組是定長的,而切片可以調(diào)整長度。也就是說類型的值可以用于表示任意語言類型的值。下劃線讓編譯器接受這類導(dǎo)入,并且調(diào)用對(duì)應(yīng)包內(nèi)的所有代碼文件里定義的函數(shù)。結(jié)構(gòu)體可以當(dāng)做是一種數(shù)據(jù)類型一般都默認(rèn)函 緣起 之前下載視頻用的you-get,但是b站一直下不了,優(yōu)酷也經(jīng)常出問題,所以接觸到lulu https://github.com/iawia002/Lulu 這個(gè)也是基于you-get ...
摘要:和都是目前在各自領(lǐng)域最流行的開發(fā)語言之一。在機(jī)器學(xué)習(xí)數(shù)據(jù)分析領(lǐng)域成為必學(xué)語言。 showImg(https://segmentfault.com/img/remote/1460000019167290); Golang和Python都是目前在各自領(lǐng)域最流行的開發(fā)語言之一。 Golang其高效而又友好的語法,贏得了很多后端開發(fā)人員的青睞,最適用于高并發(fā)網(wǎng)絡(luò)編程的語言之一。 Python不...
摘要:在機(jī)器學(xué)習(xí)數(shù)據(jù)分析領(lǐng)域成為必學(xué)語言。不定長參數(shù),支持不定長參數(shù),用定義參數(shù)名,調(diào)用時(shí)多個(gè)參數(shù)將作為一個(gè)元祖?zhèn)鬟f到函數(shù)內(nèi)返回函數(shù)結(jié)果。showImg(https://user-gold-cdn.xitu.io/2019/5/13/16ab0b937e7329d4); Golang和Python都是目前在各自領(lǐng)域最流行的開發(fā)語言之一。 Golang其高效而又友好的語法,贏得了很多后端開發(fā)人員的青...
閱讀 283·2024-11-07 18:25
閱讀 130362·2024-02-01 10:43
閱讀 868·2024-01-31 14:58
閱讀 828·2024-01-31 14:54
閱讀 82766·2024-01-29 17:11
閱讀 3047·2024-01-25 14:55
閱讀 1985·2023-06-02 13:36
閱讀 3032·2023-05-23 10:26