当我们想要在 Go 语言中初始化一个结构时,可能会用到两个不同的关键字 — make
和 new
。因为它们的功能相似,所以初学者可能会对这两个关键字的作用感到困惑,但是它们两者能够初始化的却有较大的不同。
make
的作用是初始化内置的数据结构,也就是我们在前面提到的切片、哈希表和 Channelnew
的作用是根据传入的类型分配一片内存空间并返回指向这片内存空间的指针
make()
Example
Slices can be created with the built-in make
function; this is how you create dynamically-sized arrays.
The make
function allocates a zeroed array and returns a slice that refers to that array:
1 | a := make([]int, 5) // len(a)=5 |
To specify a capacity, pass a third argument to make
:
1 | b := make([]int, 0, 5) // len(b)=0, cap(b)=5 |
1 | package main |
Description
1 | func make(t Type, size ...IntegerType) Type |
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:
1 | Slice: The size specifies the length. The capacity of the slice is |
Difference between make()
and new()
Back to allocation. The built-in function make(T,
args)
serves a purpose different from new(T)
. It creates slices, maps, and channels only, and it returns an initialized (not zeroed) value of type T
(not *T
). The reason for the distinction is that these three types represent, under the covers, references to data structures that must be initialized before use. A slice, for example, is a three-item descriptor containing a pointer to the data (inside an array), the length, and the capacity, and until those items are initialized, the slice is nil
. For slices, maps, and channels, make
initializes the internal data structure and prepares the value for use. For instance,
1 | make([]int, 10, 100) |
allocates an array of 100 ints and then creates a slice structure with length 10 and a capacity of 100 pointing at the first 10 elements of the array. (When making a slice, the capacity can be omitted; see the section on slices for more information.) In contrast, new([]int)
returns a pointer to a newly allocated, zeroed slice structure, that is, a pointer to a nil
slice value.
These examples illustrate the difference between new
and make
.
1 | var p *[]int = new([]int) // allocates slice structure; *p == nil; rarely useful |
Remember that make
applies only to maps, slices and channels and does not return a pointer. To obtain an explicit pointer allocate with new
or take the address of a variable explicitly.
make(T, args)
返回的是 T 的引用(指针) - 易错
在 Go 的世界中,如果没有显式的声明,则都是按值传递(无论数据类型)。这意味着,当调用一个函数时,这个值会被复制一份并传递到这个函数中。因此,在函数内部对值修改不影响值的本身。
但是,make(T, args)
返回的值通过函数参数传递到另一个函数中后,可以在改函数内部直接修改,即任何 map,slice,channel 的实例传入函数之后,在函数内部修改该实例的值,将影响该实例在函数外部的状态。
1 | func modifySlice(s []int) { |
这其实说明,调用 make(T, args)
时返回的是 T 实例的引用(指针),而不是 T 实例。
make()
和 new()
new 和 make 都可以用来分配空间,初始化类型,但是它们确有不同。
new (T) 返回的是 T 的指针
new(T)
会实例化出一个 T 类型的实例,并为此分配内存空间,该示例为 T 类型的默认值。
new(T)
返回的是示例的地址(也就是一个 T 类型的指针 *T)该指针指向这个 T 的实例。
1 | p1 := new(int) |
上面的代码是等价的,new (int) 将分配的空间初始化为 int 的零值,也就是 0,并返回 int 的指针,这和直接声明指针并初始化的效果是相同的。
make()
make 只能用于 slice,map,channel 三种类型,也就是说,在调用 make(T, args)
时,T 只能是 slice,map,channel 三种中的其实一种。
make(T, args)
返回的是初始化之后的 T 类型的实例(而不是一个指针)。
1 | var s1 []int |
对于 slice 而言,slice 的零值是 nil,当调用 make()
之后, slice 的初始化被完成,即 slice 的长度、容量、底层指向的 array 都在 make()
函数中被初始化了,此时 slice 内容被类型 int 的零值填充,形式是 [0 0 0],map 和 channel 也是类似的。
1 | func main() { |
new()
相比与复杂的 make
关键字,new
的功能就很简单了,它只能接收一个类型作为参数,然后返回一个指向该类型的指针:
1 | i := new(int) |
append()
可以通过直接使用 append()
来向数组中添加元素,此后数组的 len 和 cap 都会自动变大,比如:
1 | func main() { |
It is common to append new elements to a slice, and so Go provides a built-in append
function. The documentation of the built-in package describes append
.
1 | func append(s []T, vs ...T) []T |
The first parameter s
of append
is a slice of type T
, and the rest are T
values to append to the slice.
The resulting value of append
is a slice containing all the elements of the original slice plus the provided values.
If the backing array of s
is too small to fit all the given values a bigger array will be allocated. The returned slice will point to the newly allocated array.
(To learn more about slices, read the Slices: usage and internals article.)
1 | package main |