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