golang中var、make、new的使用[转]
一、var、make、new、:=的使用习惯:#
1.使用make(),来初始化slice,map 和channel 。
2.大多数场合,类型明确的场合下,使用短变量声明方式:=。
3.当使用文字方式初始化一个变量,并且需要指明类型时,使用var变量声明方式。
4.避免使用new(),除非你需要一个指针变量。
注意点:
make
返回值是: 引用类型本身。
new
返回值: 指向类型的指针。
二、详细对比:#
new#
定义:#
func new(Type) *Type
特点:#
- 分配内存、内存里存的值是对应类型的零值。
- 只有一个参数、参数是分配的内存空间所存储的数据类型,Go语言里的任何类型都可以是new的参数,比如int, 数组,结构体,甚至函数类型都可以。
- 返回的是指针。
例子:#
// 方式1
ptr := new(T)
// 方式2
var t T
ptr := &t
上面两种赋值方式是等价的。
注意点:#
- Go的new分配的内存可能在栈(stack)上,可能在堆(heap)上。
- Go的new分配的内存里的值是对应类型的零值,不能显示初始化指定要分配的值。
make#
定义:#
func make(t Type, size ...IntegerType) Type
特点:#
1.分配和初始化内存。
2.只能用于slice, map和chan这3个类型,不能用于其它类型。 如果是用于slice类型,make函数的第2个参数表示slice的长度,这个参数必须给值。
3.返回的是原始类型(引用类型的本身),也就是slice, map和chan,不是返回指向slice, map和chan的指针。
例子:#
var c chan int //声明管道类型变量c,此时c还是nil,不可用;
fmt.Printf("%#v \n",c) //(chan int)(nil)
c = make(chan int)
fmt.Printf("%#v", c) //(chan int)(0xc000062060)
//声明管道类型变量c,此时c还是nil,不可用;
//通过make来分配内存并初始化,c就获得了内存可以使用了。
make 是分配内存并初始化,初始化并不是置为零值。
与new一样,它的第一个参数也是一个类型,但是不一样的是,make返回的是传入的类型,而不是指针!
问题理解:#
1.为什么针对slice, map和chan类型专门定义一个make函数?
答案:这是因为slice, map和chan的底层结构上要求在使用slice,map和chan的时候必须初始化,如果不初始化,那slice,map和chan的值就是零值,也就是nil
nil特性:
- map如果是nil,是不能往map插入元素的,插入元素会引发panic 。
- chan如果是nil,往chan发送数据或者从chan接收数据都会阻塞。
- slice会有点特殊,理论上slice如果是nil,也是没法用的。但是append函数处理了nil slice的情况,可以调用append函数对nil slice做扩容。但是我们使用slice,总是会希望可以自定义长度或者容量,这个时候就需要用到make。
2.可以用new来创建slice, map和chan 么?
答案:可以。
参考代码:
package main
import "fmt"
func main() {
a := *new([]int)
fmt.Printf("%T, %v\n", a, a==nil) // []int, true
b := *new(map[string]int)
fmt.Printf("%T, %v\n", b, b==nil) // map[string]int, true
c := *new(chan int)
fmt.Printf("%T, %v\n", c, c==nil) // chan int, true
}
虽然new可以用来创建slice, map和chan,但实际上并没有卵用,因为new创建的slice, map和chan的值都是零值,也就是nil。这3种类型如果是nil,就出现上面介绍nil特性了,无法使用。
3.为什么slice是nil也可以直接append?
答案:对于nil slice,append会对slice的底层数组做扩容,通过调用mallocgc向Go的内存管理器申请内存空间,再赋值给原来的nil slice。
slice用make创建的时候,如果指定的长度len>0,则make创建的slice下标索引从0到len-1的值都是对应slice里元素类型的零值。参考下例:
package main
import "fmt"
func main() {
s := make([]int, 2, 3)
fmt.Println(s) // [0 0]
}
参考文档: