golang之new和make

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/phantom_111/article/details/79475759

golang提供的内建函数new()和make()。二者做的是不同的事情并且应用于不同的类型,但是因为规则类似,所以在使用过程中经常容易混淆。

new

这是用来分配内存的内建函数,它不初始化内存,只是将其置零。也就是说,new(T)会为T类型分配置零的存储,并返回它的地址(*T)。在Go的术语中,其返回一个指向新分配的类型为T的指针,这个指针指向的内容的值为零。注意并不是指针为零

对于不同的数据类型,零值的意义是不同的:

    //int的零值为0
    pI := new(int)
    fmt.Printf("int: %p, %d\n", pI, *pI)
    //string的零值为空字符串
    pS := new(string)
    fmt.Printf("string: %p, %s, %t\n", pS, *pS, *pS == "")
    //bool的零值为false
    pB := new(bool)
    fmt.Printf("bool: %p, %t\n", pB, *pB)
    fmt.Println("Hello, playground")

使用及例子

使用

p := new(DataType)
//DataType包含基本的数据类型int,string等以及自定义的数据类型struct等

例子

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

func main() {
  i := newInt()
}

注意: 这里与C/C++不同,返回一个局部变量的地址在go语言是可以的,通过go run -gcflags=-m main.go 发现该变量已逃逸到堆,所以mian函数可以直接使用。

make

内建函数make(T, args)与new(T)的用途不同。它只用来创建slice、map和channel,并且返回一个初始化的(而不是置零),类型为T的值(而不是*T)。之所以有所不同,是因为这三个类型的背后引用了使用前必须初始化的数据结构。例如,slice是一个三元描述符,包含一个指向数据(在数组中)的指针,长度以及容量,在这些项被初始化之前,slice都是nil的。对于slice,map和channel,make初始化这些内部数据结构,并准备好可用的值。

使用及例子

使用

p := make(type, len, cap)

例子

    //分配一个长度为10, 容量为100的slice结构,该slice引用包含前10个元素的数组
    m := make([]int, 10, 100)
    fmt.Printf("make's value %x\n",m )

    //返回一个指向新分配的,被置零的slice结构体指针,即指向值为nil的slice指针
    n := new([]int)  //allocates slice structure, *p == nil , rarely userful
    *n = append(*n, 10)
    fmt.Printf("new's address:%p, vaule:%x\n", n, *n) 

问题

以下代码是否存在问题?如果存在问题请改正?

type student struct {
    Name string
    Age  int 
}

func main() {
    m := make(map[string]*student)
    stus := []student{
        {Name: "zhou", Age: 24},
        {Name: "li", Age: 23},
        {Name: "wang", Age: 22},
    }   
    for _, stu := range stus {
        m[stu.Name] = &stu
    }   
    for _, v := range m { 
        fmt.Printf("%v", v)

    }   
}  

结果打印

&{wang 22}
&{wang 22}
&{wang 22}

原因分析

创建map的类型为map[string]*int,而在通过for range对该map进行赋值的时候,只是把局部变量stu的地址赋值给map的value,而stu变量存储的内容必定为stus迭代的map的某一项的值,所以才会出现上述的打印结果。

改正方式

   //将遍历赋值的代码改为如下方式
   for i, stu := range stus {
        m[stu.Name] = &stus[i]
    }

参考资料

猜你喜欢

转载自blog.csdn.net/phantom_111/article/details/79475759