Golang笔记-make

发布于 2023-05-08  4830 次阅读


关于var、make、new的区别大家应该都知道

1. var

var仅仅就是定义了一个变量,定义的是什么变量,分配的就是什么类型的内存,比如定义一个

var p *int
*p=10
fmt.println(*p)

他不会申请int大小的内存,只会申请一个指针大小的内存,而p的默认值又是nil,那如果你访问p的内容,给p指向的位置赋值,自然就会报错喽

2. new

但是如果你new一下,就不一样了,new之后分配了int大小的空间,自然就可以访问了

var p *int = new (int)
*p=10
fmt.println(*p)

总结

  • 对于int,string,float,rune,byte,bool等类型,在定义变量的时候系统已经给申请了内存,而且给了对应的默认值(int的默认值为0,string的默认值为"",bool的默认值为false),所以我们可以直接给变量进行赋值操作
  • 对于指针,切片,map等类型,这些变量直接定义的时候系统是没有给分配内存的,并且默认值为nil,所以不能直接赋值。如果想赋值的话,需要用new或者make函数向系统提前申请内存才行。

3. make

make和new都是分配内存,那既然有new了为什么又需要make呢?

new只是简简单单的分配内存,对于slice,map,channel这些数据结构,仅仅分配内存有时候不能满足需求,还需要对这种类型进行初始化。比如一个slice,如果用new的话,那么他的底层数组指针为nil,长度为0,容量为0,但如果用make去创建,就可以为长度和容量赋值,同时系统就会创建底层数组

func TestTMP(t *testing.T) {
list_make := make([]int, 5, 100) //初始化一个切片
list_new := *new([]int)
fmt.Println("list_make的长度:", len(list_make), "容量:", cap(list_make))
fmt.Println("list_new的长度:", len(list_new), "容量:", cap(list_new))
}

输出结果

list_make的长度: 5 容量: 100
list_new的长度: 0 容量: 0

课后习题

那么现在给你这样一段代码,他的输出结果是什么呢?

list := make([]int, 5, 100) //初始化一个切片
list = append(list, 666, 777, 888) //向切片追加三个元素
for i, v := range list {
fmt.Println("第", i, "个元素为:", v)
}

答案解析:在make初始化之后就已经创建生成了一个长度为5的切片,再向该切片末尾追加元素,应该是从第六个位置开始加的

第 0 个元素为: 0
第 1 个元素为: 0
第 2 个元素为: 0
第 3 个元素为: 0
第 4 个元素为: 0
第 5 个元素为: 666
第 6 个元素为: 777
第 7 个元素为: 888

4. 遍历指针切片的易错点

这里提到一个题外的易错点,golang中的for在遍历时的临时变量是同一个地址的变量以覆盖形式重新赋值的,比如这样一段代码:

list := []int{1, 2, 3, 4, 5, 6} //初始化一个切片
var res []*int
for _, v := range list {
res = append(res, &v)
}
for i, v := range res {
fmt.Println("第", i, "个元素为:", *v)
}
第 0 个元素为: 6
第 1 个元素为: 6
第 2 个元素为: 6
第 3 个元素为: 6
第 4 个元素为: 6
第 5 个元素为: 6

因为我最后输出那个切片存的是指针,所以其实我这个res指针切片里存的六个元素都是同一个内存地址也就是同一个指针,所以我再遍历的时候其实是输出了六次同一个内存地址的内容,自然就是v最后一次被赋的值,也就是list切片最后一个元素6

所以应该用一个临时变量转存一下,这样就可以了

list := []int{1, 2, 3, 4, 5, 6} //初始化一个切片
var res []*int
for _, v := range list {
tmp := v
res = append(res, &tmp)
}
for i, v := range res {
fmt.Println("第", i, "个元素为:", *v)
}

总结

  • make 专门用于 map 、slice 、channel 这三种类型的内存分配及初始化
  • new 计算类型大小,为其分配零值内存,返回指针。
  • make 会被编译器翻译成具体的创建函数,由其分配内存和初始化成员结构,返回类型的引用而非指针。


星星温柔泛滥,人间至善