原文:https://www.cnblogs.com/beatleC/p/16081832.html
前言:Go语言中new和make是内置函数,主要用来创建分配类型内存(相同点:堆上),其功能相似,却有本质区别。
引入:变量的声明
var i int var s string
变量的声明我们可以通过var关键字,然后就可以在程序中使用。当我们不指定变量的默认值时,这些变量的默认值是零值,比如int类型的零值是0,string类型的零值是"",引用类型的零值是nil。
对于例子中的两种类型的声明,我们可以直接使用,对其进行赋值输出。但是如果我们换成引用类型呢?
package main import ( "fmt" ) func main() { var i *int *i=10 fmt.Println(*i) }
这个例子会打印出什么?0还是10?
以上结果全错,运行的时候会painc,原因如下:
panic: runtime error: invalid memory address or nil pointer dereference
从这个提示中可以看出,对于引用类型的变量,我们不仅要声明它,还要为它分配内容空间,否则我们的值放在哪里去呢?这就是上面错误提示的原因。
对于值类型的声明不需要,因为已经默认帮我们分配好了内存。
要分配内存,就引出来今天的new和make。
1、new
new 的作用是初始化一个指向类型的指针(*T),使用new函数来分配空间。传递给new 函数的是一个类型,不是一个值。返回值是指向这个新分配的零值的指针。
函数定义:
func new(Type) *Type
对于上面引入中的问题我们如何解决呢?既然我们知道了没有为其分配内存,那么我们使用new分配一个吧。
func main() { var i *int i=new(int) *i=10 //没有*i=10,打印的就是0 fmt.Println(*i) }
//运行程序,完美PASS,打印10
再举一个栗子:
func main() { u:=new(user) u.lock.Lock() u.name = "张三" u.lock.Unlock() fmt.Println(u) } type user struct { lock sync.Mutex name string age int }
user类型中的lock字段我不用初始化,直接可以拿来用,不会有无效内存引用异常,因为它已经被零值了。
这就是new,它返回的永远是指向类型的指针,指向分配类型的内存地址。
有new这个内置函数,可以给我们分配一块内存让我们使用,但是现实的编码中,它是不常用的,但make函数是无可替代的。
我们通常都是采用短语句声明以及结构体的字面量达到我们的目的,比如:
i:=0 u:=user{}
这样更简洁方便,而且不会涉及到指针这种比麻烦的操作。
2、make
make 的作用是为 slice,map 或 chan 初始化并返回引用(T)。第一个参数是一个类型,第二个参数是长度。返回一个有初始值的对象,注意不是指针。
函数定义:
func make(Type, size IntegerType) Type
1)切片Slice的初始化
//创建一个初始元素长度为5的数组切片,元素初始值为0,并预留10个元素的存储空间: mySlice := make([]int, 5, 10)
2)映射Map的初始化
//在创建时指定该map的初始存储能力,创建了一个初始存储能力为100的map myMap = make(map[string] PersonInfo, 100)
new的栗子:
//使用new创建一个map指针 ma := new(map[string]int) *ma = map[string]int{} (*ma)["test"] = 666 fmt.Println(*ma) // map[test:666]
3)通道channel的初始化
//创建有缓存通道 ch := make(chan int, 10) //创建无缓存通道 ch := make(chan int)
注意⚠️: slice, map 和 channel 的零值都是nil。
make(T, args)函数的目的与new(T)不同。它仅仅用于创建 slice, map 和 channel(引用类型),并且返回类型是 T(不是T*)的一个初始化的(不是零值)的实例。3、总结:
new(T):创建一个没有任何数据的类型为T(零值填充)的实例,并返回该实例的指针(即地址);【用于各种类型】
make(T, args):只能创建 slice、map和channel【因为这三个类型在使用前必须被初始化】,并且返回一个初始化后(有初始值args【不是零值】)的T类型的实例,非指针*T。