面试官:Golang 的 new 与make 区别是什么?

胖虎坐在公司工位上正在吃刚才买的包子。

新来的小学妹就匆忙地跑过来,慌慌张张说:“学长,不好了,线上代码出现问题了。”

胖虎赶紧放下包子,来不及擦嘴,迅速掏出电脑,边打开电脑边问“你知道哪里报错吗,为什么报错吗”

学妹:“不知道啊……”

胖虎:“……行吧,我自己看下吧。”

学妹从来没有见过如此严肃的学长,大气也不敢喘,也不敢走,就默默的看学长在查问题。

胖虎:“找到了,这是谁写的代码啊,map 使用了 new 初始化”

map1 := new(map[string]string)
复制代码

学妹:“是我,学长……” 胖虎:“行吧,看在你是我学妹份上,今天跟你简单科普一下吧”

变量声明的方式

 var test1 int
复制代码

我们可以通过 var+变量名称+变量类型 进行声明变量,当我们没有给它赋值的时候,它们的结果是变量类型的零值。

比如说 string 的零值是"", int 的零值是0,引用类型的零值是nil。

以上两种类型我们可以直接使用,但如果把它改成指针会怎么样呢?

package main

import "fmt"

func main() {
  var test *string
  fmt.Println(test)
  *test = "测试"
}
复制代码

执行结果如下:

这是为什么呢,因为对于引用类型的变量,不仅要声明,并且还要给它分配内存。怎么给它分配内存呢?这就要用到了new了

什么是new

new 是 Golang 的内置函数,源代码如下:

大意是,分配内存的内置函数,第一个参数是类型,而不是具体的值,返回值是该类型的指针。分配的值是该类型零值的指针。

“我知道怎么改了” 学妹兴奋的说道,说完便在编辑器加了两行代码。

package main

import "fmt"

func main() {
  var test *string
  fmt.Println(test)
  test = new(string)
  *test = "测试"
  fmt.Println(*test)
}
复制代码

胖虎:“恩,不错,学得挺快的嘛,那我再问你一下,复合类型的slice、map、chan使用 new 后可以使用吗?为什么呢”

学妹:“这你刚才没说啊,我不知道”

胖虎:“咱们可以敲下代码,演示一下嘛”

package main

import "fmt"

func main() {
  testMap := new(map[string]string)
  (*testMap)["aa"] = "aa"
  fmt.Println(testMap)
}
复制代码

执行下代码,竟然报错了,“这是为什么呢?”

胖虎:“真相只有一个,那就是,map 底层是结构体,这样说,你可能不理解,举个🌰吧。”

Talk is cheap. Show me the code

package main

import "fmt"

type XueMei struct {
  age          *int64 `json:"age"`
  BoyFiriendYn bool   `json:"boy_firiend_yn"`
}

func main() {
  test := new(XueMei)
  //是否有男朋友
  test.BoyFiriendYn = false

  //此处代码会导致panic
  //panic: runtime error: invalid memory address or nil pointer dereference
  //[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x108a0e9]
  *test.age = 1
}
复制代码

也就是说它里面的成员变量仍未进行初始化,所以它们几个初始化要使用make来进行。

学妹崇拜的眼光“ 学长你懂得真多,你还能说说什么是 make 吗?”

什么是make

make 也是用于内存分配的内置函数,但是和new不同,源码如下图所示。

大意是make内置函数分配并初始化一个slice、map或chan类型的对象。像new函数一样,第一个参数是类型,而不是值。

与new不同,make的返回类型与其参数的类型相同,而不是指向它的指针。结果的取决于传入的类型。

并且 slice在 make 的时候,第二个参数必须传递,也就是切片的长度。否则会编译失败。

new函数底层实现

new函数底层主要是调用go1.17/src/runtime/malloc.go中的 newobject 方法。

编辑切换为居中

添加图片注释,不超过 140 字(可选)

这里可以看到 newobject 方法,底层调用 mallocgc 方法的时候,needzero 传的是 true ,所以返回值是传入类型零值的指针。

make函数底层实现

通过执行以下命令go tool compile -N -l -S file.go

我们可以看到make函数初始化

slice调用的是runtime.makeslice、runtime.makeslice64这两个方法.

func makeslice(typ *byte, len int, cap int) unsafe.Pointer
func makeslice64(typ *byte, len int64, cap int64) unsafe.Pointer
复制代码

编辑切换为居中

添加图片注释,不超过 140 字(可选)

map调用的是runtime.makemap64、runtime.makemap、runtime.makemap_small这三个方法.

func makemap64(mapType *byte, hint int64, mapbuf *any) (hmap map[any]any)
func makemap(mapType *byte, hint int, mapbuf *any) (hmap map[any]any)
func makemap_small() (hmap map[any]any)
复制代码

chan分别调用的是runtime.makechan64、runtime.makechan这三个方法.

func makechan64(chanType *byte, size int64) (hchan chan any)
func makechan(chanType *byte, size int) (hchan chan any)
复制代码

感兴趣的同学可以去看下源代码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

科技百宝箱

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值