在 go 中對某種型別進行初始化時會用到make
和new
, 因為它們的功能相似,所以初學者可能對它們的感到困惑;本文將由淺入深的介紹其功能和區別
長話短說,先放上結論:
方法作用
作用物件
返回值new
分配記憶體
值型別和使用者定義的型別
初始化為零值,返回指標
make
分配記憶體
內建引用型別(map, slice, channel)
初始化為零值,返回引用型別本身
以上為make
和new
的區別,如果有人追問能說的更詳細點嗎?
哦豁,這就很尷尬了
短話長說,我們看看make
和new
究竟做了什麼
先說要點,new 用來分配記憶體,並初始化零值,返回零值指標
在編譯過程中,使用 new 大致會產生 2 種情況:
若該物件申請的空間為 0,則返回表示空指標的zerobase
變數,這類物件比如:slice
,map
,channel
以及一些結構體等。
// path: src/runtime/malloc.go
// base address for all 0-byte allocations
var zerobase uintptr
其他情況則會使用runtime.newobject
函式:
// path: src/runtime/malloc.go
func
newobject
(typ *_type) unsafe.pointer
這一塊的內容也比較簡單,runtime.newoject
呼叫了runtime.mallocgc
函式去開闢一段記憶體空間,然後返回那塊空間的位址
這裡可以做個簡單的例子去驗證一下:
func
main()
說到底new
實現什麼功能呢,可以這樣去理解
// a := new(int); 其他型別以此模擬
var a int
return
&a
make
是用來初始化map
,slice
,channel
這幾種特定型別的
在編譯過程中,用make
去初始化不同的型別會呼叫不同的底層函式:
初始化map
, 呼叫runtime.makemap
初始化slice
, 呼叫runtime.makeslice
初始化channel
,呼叫runtime.makechan
接下來我們看這些函式的原始碼部分,**它們與new
的不同,如果了解這幾種型別的原始碼,很容易理解下面的**;如果不了解這塊內容的同學可以跟著注釋走,了解流程就可以了
runtime.makemap
:
// path: src/runtime/map.go
func
makemap
(t *maptype, hint int
, h *hmap)
*hmap
// 生成 hash 種子
h.hash0 =
fastrand()
// 計算 桶 的數量
b :=
uint8(0
)for
overloadfactor
(hint, b)
h.b = b
if h.b !=
0return h
}
這裡為了方便檢視,省去了部分**。我們可以看到這裡的步驟很多,h = new(hmap)
只是其中的一部分
runtime.makeslice
:
// path: src/runtime/slice.go
func
makeslice
(et *_type,
len,
capint
) unsafe.pointer
// panic: cap 超出範圍
panicmakeslicecap()
}return
mallocgc
(mem, et,
true
)}
這裡其實和new
底層的runtime.newobject
很相似了,只是這裡多了一些異常處理
runtime.makechan
:
// path: src/runtime/chan.go
func
makechan
(t *chantype, size int
)*hchan
// 初始化 hchan 的內部字段
c.elemsize =
uint16
(elem.size)
c.elemtype = elem
c.dataqsiz =
uint
(size)
...}
這裡省略了部分**,包括一些異常處理
總之,make
相對於new
來說,做的事情更多,new
只是開闢了記憶體空間,make
為更加複雜的資料結構開闢記憶體空間並對一些字段進行初始化
注意:有心細的同學可以發現,runtime,makemap
,runtime,makeslice
,runtime.makechan
返回的是指標型別,但並不意味著用 make 初始化後,返回的是指標型別,這裡上面列出來的是比較核心部分的原始碼,並不是所有的原始碼
可以用 new 去初始化 map, slice 和 channel 嗎?
首先我們回憶一下new
的功能,簡單理解如下:
var i int
return
&int
如果我們要去初始化上面幾種型別要怎麼去做:
m :=
*new
(map
[int
]int
)// 先取值,因為 new 返回的是指標
s :=
*new([
]int
) ch :=
*new
(chan
int)
在上述**中,使用new
去初始化這幾個型別,是不會 panic 的
針對上述**的情況,我們可以分類討論:
map, new 沒有對 map 做建立桶等初始操作,所以當我們新增鍵值對的時候回 panic, 查詢 和 刪除不存在的 key 時不會引發 panic, 因為查詢和刪除都要查詢桶和 key的過程,如果沒有對應的桶和key,查詢返回零值,刪除則不作操作
channel,也沒有對 channel 的緩衝區開闢記憶體空間以及更多的內部初始話操作,所建立的 channel 始終是 nil,往裡面傳送或從裡面接收資料都會引發 panic
可以用 make 去初始化其他型別嗎,如 int, string ?
不可以,因為 make 沒有對其他型別提供相應的底層方法
以上,由於能力有限,疏忽和不足之處難以避免,歡迎讀者指正,以便及時修改。
go make 和 new 的區別
new 和 make 都可以用來分配空間,初始化型別,但是它們確有不同。new t 為乙個 t 型別新值分配空間並將此空間初始化為 t 的零值,返回的是新值的位址,也就是 t 型別的指標 t,該指標指向 t 的新分配的零值。p1 new int fmt.printf p1 v n p1 int 0x...
Go make 和 new的區別
在go語言中 make 被用來分配引用型別的記憶體 map,slice,channel new 被用來分配除了引用型別的所有其他型別的記憶體 int,string,array等 本文主要給大家介紹了go語言中函式new與make的使用和區別,關於go語言中new和make是內建的兩個函式,主要用來建...
new和malloc的區別
1 new 是c 中的操作符,malloc是c 中的乙個函式 2 new 不止是分配記憶體,而且會呼叫類的建構函式,同理delete會呼叫類的析構函式,而malloc則只分配記憶體,不會進行初始化類成員的工作,同樣free也不會呼叫析構函式 3 記憶體洩漏對於malloc或者new都可以檢查出來的,...