欢迎光临
我们一直在努力

golang中的new和make的区别

Go 语言中的 new 和 make 一直是新手比较容易混淆的东西,咋一看很相似。不过解释两者之间的不同也非常容易。

new

new 的主要特性

首先 new 是内建函数,你可以从 http://golang.org/pkg/builtin/#new 这儿看到它,它的定义也很简单:

func new(Type) *Type

官方文档对于它的描述是:

内建函数 new 用来分配内存,它的第一个参数是一个类型,不是一个值,它的返回值是一个指向新分配类型零值的指针
根据这段描述,我们可以自己实现一个类似 new 的功能:

func newInt() *int {
  var i int
  return &i
}
someInt := newInt()

我们这个函数的功能跟 someInt := new(int) 一模一样。所以在我们自己定义 new 开头的函数时,出于约定也应该返回类型的指针。

new(T) 为每个新的类型T分配一片内存,初始化为 0 并且返回类型为*T的内存地址:这种方法 返回一个指向类型为 T,值为 0 的地址的指针,它适用于值类型如数组和结构体;它相当于 &T{}。

make(T) 返回一个类型为 T 的初始值,它只适用于3种内建的引用类型:slice、map 和 channel。

换言之,new 函数分配内存,make 函数初始化;下图给出了区别:

通过实验,可以更直观的查看两者之间的区别

Go 语言中 new 和 make 都是内置函数,用于内存的分配,本文主要简述两者使用上的异同与特性。

例子一:

func main() {
  var i *int
  *i = 1
  fmt.Println(*i)
}

上面的程序并不会打印1,而会抛 panic 异常,因为i是一个引用类型,需要给它分配内存空间,通俗来说就是指针(内存地址)需要指向一片内存空间才有意义。

为 i 分配内存:

func main() {
  var i *int
  i = new(int)
  *i = 1
  fmt.Println(*i)
}

用 new 内置函数为 i 分配内存空间,并返回该内存空间的地址,即指针,new 函数格式如下:

func new(Type) *Type

可知,new 为每个类型分配一片内存空间,初始化为 0 并返回该内存空间的地址。

new 的内存分配示意图:

其实要说明一点的就是,new 不常用,我们常常会通过结构体的字面量达到 new 的效果,而且这样写也比较优雅:

man := &People{Name: "zhangchenghui", Age: 18, Sex: "男"}

例子二:

package main

import "fmt"

//  new函数用来分配内存,主要分配值类型,比如int、float32、struct等,返回的是指针

func main() {
    num1 := 100
    add(&num1)
    fmt.Printf("num1的类型是%T,num1的值为%v,num1的地址是%v %d\n", num1, num1, &num1, num1)
    //结果---num1的类型是int,num1的值为100,num1的地址是0xc042052058
    //内存分配说明:
    //内存里会开辟一个空间,num1指向这个空间,这个空间了里放了值100,那么num1的地址就是这块空间的地址

    num2 := new(int)
    *num2 = 200
    add(num2)

    fmt.Printf("num2的类型是%T,num2的值为%v,num2的地址是%v %d\n", num2, num2, &num2, *num2)
    //结果---num2的类型是*int,num2的值为0xc0420520a0,num2的地址是0xc042072020

    //内存分配说明:
    //num2本身是指针类型,num2会指向一个空间,但是这个空间里的值是一个地址,就是0xc0420520a0,但是这个
    //空间本身也有一个地址,地址就是0xc042072020,而0xc042072020这个地址默认情况的值是个0,因为没有赋值

    //进一步说明
    fmt.Printf("num2的类型是%T,num2的值为%v,num2的地址是%v,num2这个指针指向的值是%v", num2, num2, &num2, *num2)
    //结果---num2的类型是*int,num2的值为0xc0420520a0,num2的地址是0xc042072020,num2这个指针指向的值是0

}

func add(i *int) {
    *i = *i + 56
}

例子三:

p := new([]int) //p == nil; with len and cap 0
fmt.Println(p)

v := make([]int, 10, 50) // v is initialed with len 10, cap 50
fmt.Println(v)

/*********Output****************
    &[]
    [0 0 0 0 0 0 0 0 0 0]
*********************************/

//(*p)[0] = 18 // panic: runtime error: index out of range
// because p is a nil pointer, with len and cap 0
*p = append(*p, 2, 3, 4)
v[1] = 18  // ok
v[0] = 218 // ok
fmt.Printf("%T %d %d %d\n", p, *p, len(*p), len(*p))
fmt.Printf("%T %d %d %d\n", v, v, len(v), len(v))

make

make 的主要特性

make 也是内建函数,你可以从 http://golang.org/pkg/builtin/#make 这儿看到它,它的定义比 new 多了一个参数,返回值也不同:

func make(Type, size IntegerType) Type

官方文档对于它的描述是:

内建函数 make 用来为 slice,map 或 chan 类型分配内存和初始化一个对象(注意:只能用在这三种类型上),跟 new 类似,第一个参数也是一个类型而不是一个值,跟 new 不同的是,make 返回类型的引用而不是指针,而返回值也依赖于具体传入的类型,具体说明如下:

Slice: 第二个参数 size 指定了它的长度,它的容量和长度相同。
你可以传入第三个参数来指定不同的容量值,但必须不能比长度值小。
比如 make([]int, 0, 10)

Map: 根据 size 大小来初始化分配内存,不过分配后的 map 长度为 0,如果 size 被忽略了,那么会在初始化分配内存时分配一个小尺寸的内存

Channel: 管道缓冲区依据缓冲区容量被初始化。如果容量为 0 或者忽略容量,管道是没有缓冲区的

make 也是分配内存分配,但是仅限 chan、map、slice 的内存创建,并返回其类型的引用,这一点很重要, chan、map、slice 其本身已经是引用类型了,所以make不需要再返回其指针,引用类型的本质就是指针!例如:

type i *int;

如上,i 就是一个自定义的引用类型,其类型是一个 int 类型的指针。

Make 内置函数格式:

func make(t Type, size ...IntegerType) Type

make 的内存分配示意图:

 

总结

new 的作用是初始化一个指向类型的指针(*T),make 的作用是为 slice,map 或 chan 初始化并返回引用(T)。

赞(0) 打赏

评论 抢沙发

评论前必须登录!

 

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏