1. Go语言中的值类型和引用类型
-
值类型:int,float,bool,string,struct和数组 (
数组要特别注意,别搞混了
)
变量直接存储值,分配栈区的内存空间,这些变量所占据的空间在函数被调用完后会自动释放。 -
引用类型:
slice,map,chan和值类型对应的指针
变量存储的是一个地址(或者理解为指针),指针指向内存中真正存储数据的首地址。内存通常在堆上分配,通过GC回收。
2. new() vs make()
对值类型和引用类型了解之后,再来理解new()和make()就不难了,其实只要有C/C++背景的理解起来都不难。比如我们声明一个int类型的数据,一般是这样的:
var i int
var s string
当我们不指定变量的默认值时,这些变量的默认值就是对应的零值。(int类型的零值是0,string类型的零值是"",引用类型的零值是nil)。我们可以直接对值类型的变量进行赋值,修改变量对应的值等等。但是,如果声明的变量是引用类型呢?这就需要用到new()或make()函数了。先来看这样一个例子:
var i int // 值类型
i = 10
fmt.Println(i) // 10
var p *int // 引用类型
*p = 10
fmt.Println(*p) //panic: runtime error: invalid memory address or nil pointer dereference
从例子可以看到,当对指针p进行赋值的时候,出现了panic,提示p 是无效的内存地址或是空指针引用。也就是说,对于引用类型的变量,我们不仅要声明变量(这仅仅是给变量取个名字),更重要的是,我们得手动为它分配空间!好在Go中还有GC,如果是C/C++,对于指针类型,除了手动申请空间,更要手动释放空间。
new()方法就是为int/bool/struct
等 值类型分配内存空间,返回相应类型的指针;而make()就是专为slice,map,channel
分配空间。
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
因此,我们可以稍稍修改上面错误的程序.
var p *int // 引用类型,或者写成 p := new(int)
p = new(int)
*p = 10
fmt.Println(*p) // 10
make()
make也是用于内存分配,但是和new不同,它只用于slice、map和channel的内存创建,它返回的类型就是类型本身,而不是它们的指针类型。
// 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