35

golang new和make的区别

 5 years ago
source link: https://studygolang.com/articles/19973?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Go语言中new和make都是用来内存分配的原语(allocation primitives)。简单的说,new只分配内存,make用于slice,map,和channel的初始化。

new

new(T)函数是一个分配内存的内建函数。

我们都知道,对于一个已经存在变量,可对其指针进行赋值。

示例

var p int
var v *int
v = &p
*v = 11
fmt.Println(*v)

那么,如果不是已经存在的变量会如何呢?能对其直接赋值吗?

示例

var v *int
*v = 8
fmt.Println(*v)

结果会报如下错误

panic: runtime error: invalid memory address or nil pointer dereference

[signal 0xc0000005 code=0x1 addr=0x0 pc=0x48df66]

如何解决?通过Go提供了new来初始化一地址就可以解决。

var v *int
v = new(int)
*v = 8
fmt.Println(*v)

那么我们来分析一下

var v *int
    fmt.Println(*v)
fmt.Println(v) //<nil>
v = new(int) 
fmt.Println(*v)//
fmt.Println(v)//0xc00004c088

我们可以看到初始化一个指针变量,其值为nil,nil的值是不能直接赋值的。通过new其返回一个指向新分配的类型为int的指针,指针值为0xc00004c088,这个指针指向的内容的值为零(zero value)。

同时,需要注意的是不同的指针类型零值是不同的。

示例

type Name struct {
    P string
}
var av *[5]int
var iv *int
var sv *string
var tv *Name

av = new([5]int)
fmt.Println(*av) //[0 0 0 0 0 0]
iv = new(int)
fmt.Println(*iv) // 0
sv = new(string) 
fmt.Println(*sv) //
tv = new(Name)
fmt.Println(*tv) //{}

上面讲了对普通类型new()处理过后是如何赋值的,这里再讲一下对复合类型(数组,slice,map,channel等),new()处理过后,如何赋值。

数组示例

var a [5]int
    fmt.Printf("a: %p %#v \n", &a, a)//a: 0xc04200a180 [5]int{0, 0, 0, 0, 0} 
    av := new([5]int)
    fmt.Printf("av: %p %#v \n", &av, av)//av: 0xc000074018 &[5]int{0, 0, 0, 0, 0}
    (*av)[1] = 8
    fmt.Printf("av: %p %#v \n", &av, av)//av: 0xc000006028 &[5]int{0, 8, 0, 0, 0}

silce 示例

var a *[]int
    fmt.Printf("a: %p %#v \n", &a, a) //a: 0xc042004028 (*[]int)(nil)
    av := new([]int)
    fmt.Printf("av: %p %#v \n", &av, av) //av: 0xc000074018 &[]int(nil)
    (*av)[0] = 8
    fmt.Printf("av: %p %#v \n", &av, av) //panic: runtime error: index out of range

map 示例

var m map[string]string
    fmt.Printf("m: %p %#v \n", &m, m)//m: 0xc042068018 map[string]string(nil) 
    mv := new(map[string]string)
    fmt.Printf("mv: %p %#v \n", &mv, mv)//mv: 0xc000006028 &map[string]string(nil)
    (*mv)["a"] = "a"
    fmt.Printf("mv: %p %#v \n", &mv, mv)//这里会报错panic: assignment to entry in nil map

channel示例

cv := new(chan string)
fmt.Printf("cv: %p %#v \n", &cv, cv)//cv: 0xc000074018 (*chan string)(0xc000074020) 
//cv <- "good" //会报 invalid operation: cv <- "good" (send to non-chan type *chan string)

通过上面示例我们看到数组通过new处理,数组av初始化零值,数组虽然是复合类型,但不是引用类型,其他silce、map、channel类型也属于引用类型,go会给引用类型初始化为nil,nil是不能直接赋值的。并且不能用new分配内存。无法直接赋值。那么用make函数处理会是怎么样呢?

make

示例

av := make([]int, 5)
fmt.Printf("av: %p %#v \n", &av, av) //av: 0xc000046400 []int{0, 0, 0, 0, 0}
av[0] = 1
fmt.Printf("av: %p %#v \n", &av, av) //av: 0xc000046400 []int{1, 0, 0, 0, 0}
mv := make(map[string]string)
fmt.Printf("mv: %p %#v \n", &mv, mv) //mv: 0xc000074020 map[string]string{}
mv["m"] = "m"
fmt.Printf("mv: %p %#v \n", &mv, mv) //mv: 0xc000074020 map[string]string{"m":"m"}
chv := make(chan string)
fmt.Printf("chv: %p %#v \n", &chv, chv) //chv: 0xc000074028 (chan string)(0xc00003e060)
go func(message string) {
   chv <- message // 存消息
}("Ping!")
fmt.Println(<-chv) // 取消息 //"Ping!"
close(chv)

make不仅可以开辟一个内存,还能给这个内存的类型初始化其零值。

它和new还能配合使用

示例

var mv *map[string]string
fmt.Printf("mv: %p %#v \n", &mv, mv)//mv: 0xc042004028 (*map[string]string)(nil) 
mv = new(map[string]string)
fmt.Printf("mv: %p %#v \n", &mv, mv)//mv: 0xc000006028 &map[string]string(nil)
(*mv) = make(map[string]string)
(*mv)["a"] = "a"
fmt.Printf("mv: %p %#v \n", &mv, mv)//mv: 0xc042004028 &map[string]string{"a":"a"}

通过new给指针变量mv分配了一个内存,并赋予其内存地址。Map是引用类型,其零值为nil,使用make初始化 map,然后变量就能使用 * 给指针变量mv赋值了。

小结:

  • make和new都是golang用来分配内存的內建函数,且在堆上分配内存,make 即分配内存,也初始化内存。new只是将内存清零,并没有初始化内存。
  • make返回的还是引用类型本身;而new返回的是指向类型的指针。
  • make只能用来分配及初始化类型为slice,map,channel的数据;new可以分配任意类型的数据。

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK