原文: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。