詳解Go語言中make和new的區(qū)別
寫在前面
雖然 make
和 new
都是能夠用于初始化數(shù)據(jù)結構,但是它們兩者能夠初始化的結構類型卻有著較大的不同;make
在 Go 語言中只能用于初始化語言中的3種類型:slice、map、chan
slice := make([]int, 0, 100) hash := make(map[int]bool, 10) ch := make(chan int, 5)
這些基本類型都是語言為我們提供的,我們在前面的章節(jié)中其實已經(jīng)介紹過了它們初始化的過程以及原理,但是在這里還是需要提醒各位讀者注意的是,這三者返回了不同類型的數(shù)據(jù)結構:
slice
是一個包含data
、cap
和len
的結構體;hash
是一個指向hmap
結構體的指針;ch
是一個指向hchan
結構體的指針;
而另一個用于初始化數(shù)據(jù)結構的關鍵字 new
的作用其實就非常簡單了,它只是接收一個類型作為參數(shù)然后返回一個指向這個類型的指針:
i := new(int) var v int i := &v
上述代碼片段中的兩種不同初始化方法其實是等價的,它們都會創(chuàng)建一個指向 int
零值的指針。
到了這里我們對 Go 語言中這兩種不同關鍵字的使用也有了一定的了解:make
用于創(chuàng)建切片、哈希表和管道等內(nèi)置數(shù)據(jù)結構,new
用于分配并創(chuàng)建一個指向?qū)愋偷闹羔槨?/p>
實現(xiàn)原理
接下來我們將分別介紹 make
和 new
在初始化不同數(shù)據(jù)結構時的具體過程,我們會從編譯期間和運行時兩個不同的階段理解這兩個關鍵字的原理,不過由于前面已經(jīng)詳細地介紹過 make
的實現(xiàn)原理,所以我們會將重點放在 new
上從 Go 語言的源代碼層面分析它的實現(xiàn)。
make
在前面的章節(jié)中我們其實已經(jīng)談到過 make
在創(chuàng)建 數(shù)組和切片、哈希表 和 Channel 的具體過程,所以在這一小節(jié)中,我們也只是會簡單提及 make
相關的數(shù)據(jù)結構初始化原理。
在編譯期間的 類型檢查 階段,Go 語言其實就將代表 make
關鍵字的 OMAKE
節(jié)點根據(jù)參數(shù)類型的不同轉(zhuǎn)換成了 OMAKESLICE
、OMAKEMAP
和 OMAKECHAN
三種不同類型的節(jié)點,這些節(jié)點最終也會調(diào)用不同的運行時函數(shù)來初始化數(shù)據(jù)結構。
new
內(nèi)置函數(shù) new
會在編譯期間的 SSA 代碼生成 階段經(jīng)過 callnew
函數(shù)的處理,如果請求創(chuàng)建的類型大小時 0,那么就會返回一個表示空指針的 zerobase
變量,在遇到其他情況時會將關鍵字轉(zhuǎn)換成 newobject
:
func callnew(t *types.Type) *Node { if t.NotInHeap() { yyerror("%v is go:notinheap; heap allocation disallowed", t) } dowidth(t) if t.Size() == 0 { z := newname(Runtimepkg.Lookup("zerobase")) z.SetClass(PEXTERN) z.Type = t return typecheck(nod(OADDR, z, nil), ctxExpr) } fn := syslook("newobject") fn = substArgTypes(fn, t) v := mkcall1(fn, types.NewPtr(t), nil, typename(t)) v.SetNonNil(true) return v }
需要提到的是,哪怕當前變量是使用 var
進行初始化,在這一階段可能會被轉(zhuǎn)換成 newobject
的函數(shù)調(diào)用并在堆上申請內(nèi)存:
func walkstmt(n *Node) *Node { switch n.Op { case ODCL: v := n.Left if v.Class() == PAUTOHEAP { if prealloc[v] == nil { prealloc[v] = callnew(v.Type) } nn := nod(OAS, v.Name.Param.Heapaddr, prealloc[v]) nn.SetColas(true) nn = typecheck(nn, ctxStmt) return walkstmt(nn) } case ONEW: if n.Esc == EscNone { r := temp(n.Type.Elem()) r = nod(OAS, r, nil) r = typecheck(r, ctxStmt) init.Append(r) r = nod(OADDR, r.Left, nil) r = typecheck(r, ctxExpr) n = r } else { n = callnew(n.Type.Elem()) } } }
當然這也不是絕對的,如果當前聲明的變量或者參數(shù)不需要在當前作用域外『生存』,那么其實就不會被初始化在堆上,而是會初始化在當前函數(shù)的棧中并隨著 函數(shù)調(diào)用 的結束而被銷毀。
newobject
函數(shù)的工作就是獲取傳入類型的大小并調(diào)用 mallocgc
在堆上申請一片大小合適的內(nèi)存空間并返回指向這片內(nèi)存空間的指針:
func newobject(typ *_type) unsafe.Pointer { return mallocgc(typ.size, typ, true) }
mallocgc
函數(shù)的實現(xiàn)大概有 200 多行代碼,在這一節(jié)中就不展開詳細分析了,我們會在后面的章節(jié)中詳細介紹 Go 語言的內(nèi)存管理機制。
總結
到了最后,簡單總結一下 Go 語言中 make
和 new
關鍵字的實現(xiàn)原理,make
關鍵字的主要作用是創(chuàng)建切片、哈希表和 Channel 等內(nèi)置的數(shù)據(jù)結構,而 new
的主要作用是為類型申請一片內(nèi)存空間,并返回指向這片內(nèi)存的指針。
參考
到此這篇關于詳解Go語言中make和new的區(qū)別的文章就介紹到這了,更多相關Go語言make new內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
go 迭代string數(shù)組操作 go for string[]
這篇文章主要介紹了go 迭代string數(shù)組操作 go for string[],具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-12-12使用Go module和GoLand初始化一個Go項目的方法
這篇文章主要介紹了使用Go module和GoLand初始化一個Go項目,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-12-12