Go語言中的內建函數new和make是兩個用於內存分配的原語(allocation primitives),其功能相似,卻有本質區別。
1、new
官方文檔
// The new built-in function allocates memory. The first argument is a type, // not a value, and the value returned is a pointer to a newly // allocated zero value of that type. func new(Type) *Type
翻譯如下:
內建函數 new 用來分配內存,第一個參數是一個類型,不是一個值,返回值是一個指向分配零值的指針
new和其他語言中的同名函數一樣,new(t)分配了零值填充的類型為T內存空間,並且返回其地址,即一個*t類型的值。返回的永遠是類型的指針,指向分配類型的內存地址
它並不初始化內存,只是將其置零。*t指向的內容的值為零(zero value)。注意並不是指針為零。
2、make
官方文檔
//The make built-in function allocates and initializes an object //of type slice, map, or chan (only). Like new, the first argument is // a type, not a value. Unlike new, make's return type is the same as // the type of its argument, not a pointer to it. func make(t Type, size ...IntegerType) Type
翻譯如下:
內建函數 make 用來為 slice,map 或 chan 類型分配內存和初始化一個對象(注意:只能用在這三種類型上),跟 new 類似,第一個參數也是一個類型而不是一個值,跟 new 不同的是,make 返回類型的本身而不是指針,而返回值也依賴於具體傳入的類型,因為這三種類型就是引用類型,所以就沒有必要返回他們的指針了
make(t, args)與new(t)的功能區別是,make只能創建slice、map和channel,並且返回一個初始化的類型為t的值(而不是*t)。
注意,因為這三種類型是引用類型,所以必須得初始化,但是 不是置為零值,這個和new是不一樣的。
3、為什么slice、map和channel要由make創建呢?
先來看一下以上三個數據結構的源碼
slice
type slice struct {
array unsafe.Pointer
len int
cap int
}
slice的結構體由3部分構成,Pointer 是指向一個數組的指針,len 代表當前切片的長度,cap 是當前切片的容量。
map
// A header for a Go map.
type hmap struct {
count int
flags uint8
B uint8
noverflow uint16
hash0 uint32
buckets unsafe.Pointer
oldbuckets unsafe.Pointer
nevacuate uintptr
extra *mapextra
}
channel
type hchan struct {
qcount uint
dataqsiz uint
buf unsafe.Pointer
elemsize uint16
closed uint32
elemtype *_type
sendx uint
recvx uint
recvq waitq
sendq waitq
lock mutex
}
看到沒有,都是需要分配內存的
4、總結
new和make都在堆上分配內存,但是它們的行為不同,適用於不同的類型。
new(T) 為每個新的類型T分配一片內存,初始化為 0 並且返回類型為*T的內存地址:這種方法 返回一個指向類型為 T,值為 0 的地址的指針,它適用於值類型如數組和結構體;它相當於 &T{}。
make(T) 返回一個類型為 T 的初始值,是三個引用類型本身,它只適用於3種內建的引用類型:slice、map 和 channel。
換言之,new 函數分配內存,make 函數初始化;
其實new不常用
所以有new這個內置函數,可以給我們分配一塊內存讓我們使用,但是現實的編碼中,它是不常用的。我們通常都是采用短語句聲明以及結構體的字面量達到我們的目的,比如:
i:=0
u:=user{}
這樣更簡潔方便,而且不會涉及到指針這種比麻煩的操作。
make函數是無可替代的,我們在使用slice、map以及channel的時候,還是要使用make進行初始化,然后才才可以對他們進行操作。
package main
import (
"fmt"
)
func main() {
p := new([]int) //p == nil; with len and cap 0
fmt.Println(p)
v := make([]int, 10, 50) // v is initialed with len 10, cap 50
fmt.Println(v)
/*********Output****************
&[]
[0 0 0 0 0 0 0 0 0 0]
*********************************/
(*p)[0] = 18 // panic: runtime error: index out of range
// because p is a nil pointer, with len and cap 0
v[1] = 18 // ok
}
