make 和 new 都是 Golang 的内置函数,作为用于内存分配的原语(Allocation Primitives),其功能相似,却有着本质的区别。
- new 用来分配内存,它的第一个参数是一个类型,不是一个值,返回值是一个指向为特定类型新分配的零值填充的内存空间的指针。它并不初始化内存,只是将其置零。相当于 C 语言中的 memset 0。
// 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
make 只能用于为 Slice、Map 或 Channel 类型分配内存并进行初始化,所以其除了第一个参数传入一个类型之外,还可以传入用于完成初始化的可变长的 size 实参。跟 new 不同的是,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 和 Channel 变量?
先来看一下以上三个数据结构的源码:
- Slice
type slice struct {
array unsafe.Pointer
len int
cap int
}
- 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
}
可见,上述三个类型的背后都引用了使用前必须完成初始化的成员数据结构。如果我们使用常规的方式创建变量(e.g. var map1 [string]int
)的话,很可能会出现仅仅声明了,但却没有完成初始化的定义,从而在后续的代码中留下错误的隐患。而 make() 函数,时刻提供我们完成一个切实的变量定义(声明并初始化)。