make 和 new 的区别 | 青训营

100 阅读4分钟

make 和 new 的区别

当我们在 Go 中想初始化一个结构时,可能会考虑两个关键词 makenew . makenew

是内建的两个函数,主要用来创建分配类型内存。

make

builtin.go 中对 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. The specification of the result depends on
// the type:
//	Slice: The size specifies the length. The capacity of the slice is
//	equal to its length. A second integer argument may be provided to
//	specify a different capacity; it must be no smaller than the
//	length. For example, make([]int, 0, 10) allocates an underlying array
//	of size 10 and returns a slice of length 0 and capacity 10 that is
//	backed by this underlying array.
//	Map: An empty map is allocated with enough space to hold the
//	specified number of elements. The size may be omitted, in which case
//	a small starting size is allocated.
//	Channel: The channel's buffer is initialized with the specified
//	buffer capacity. If zero, or the size is omitted, the channel is
//	unbuffered.
func make(t Type, size ...IntegerType) Type

也就是说, make 是一个内置函数,用来初始化一个 slicemapchan 类型的对象。第一个需要的参数是一个类型 type ,然后可以有第二个或第三个参数,这需要根据类型来确定。对于 slice 来说,第二个、第三个参数分别是长度和容量,但是如果长度和容量相等时可以缺省一个,看起来就只有两个参数。对于 mapchan 类型来说,第二个参数是容量,没有第三个参数。

Untitled.png

new 不同的是, make 的返回值的类型和输入的类型是一样的,也就是说返回值不是一个指针,而是一个对象。

然后需要注意的是,对于 slice 来说,容量不能小于长度。 make([]int, 0, 10) 会分配一个 size 为10的底层数组,然后返回一个 slice ,这个 slice 由该底层数组支持,长度为0,容量为10.

对于 map ,会分配一个足够的空间来容纳指定数量的元素,但这个 map 目前是空的。当省略了大小的时候,会分配一个小的初始大小。

对于 chan ,会用指定的容量,来初始化通道的缓冲区。如果省略了容量,那么得到的通道就是无缓冲的。

Untitled 1.png

需要注意的是, make 的只能用来初始化内置的数据结构,也就是slice、map和 channel,此时编译器不一定不知道你需要使用多少内存,这些数据结构占用的内存是运行时才能知晓的。slice、map、channel 这些类型它是复合类型数据结构,通常是一个结构体+堆内存,因此 make 的额外作用就是初始化这些数据和指针,从这一点看,make 的作用是申请内存,并且初始化数据。

new

built.go 中对 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 是 Go 中的一个内置函数,用来分配内存的。传给它一个 类型 Type ,它就会根据该类型分配一块内存,该内存里存储的是该类型的零值,然后返回一个该类型的指针,指向这块内存。

使用 new 时,编译器知道你需要使用多少内存(通过计算该类型的大小), new 可以用于任何一种类型。

例如:

t := new(T)

等同于

var temp T
t := &temp

通常情况下,我们很少用 new .

如果用到,通常是用来 new 一个结构体,如

type S struct {
	a int
	b float64
}
s := new(S)

我们在编写 Mit6.824 实验的代码中,在 MakeClerk 时就是通过 new 来获得一个 clerk 结构体类型的指针的。

此外,当我们声明一个指针类型时,一定要new一个,这是为了给这个指针分配内存,不然会报内存地址为空,如

var i *[]int
i = new([]int)
fmt.Println(i) // &[]
fmt.Println(len(i)) // 0

Untitled 2.png

此时打印 i 的话,会得到 &[] , 打印 len(*i) 会得到 0

实在不懂可以记住一句话:make 只能用于 map 、slice 、channel ,new 只能用于结构体

回答

make:make能够分配并初始化类型所需的内存空间和结构,返回引用类型的本身;make具有使用范围的局限性,仅支持 channel、map、slice三种类型;make 函数会对三种类型的内部数据结构(长度、容量等)赋值。

new:new能够分配类型所需的内存空间,返回指针引用(指向内存的指针);new可被替代,能够通过字面值快速初始化。