本篇文章基于 Golang 1.17.2
胖虎坐在公司工位上正在吃刚才买的包子。
新来的小学妹就匆忙地跑过来,慌慌张张说:“学长,不好了,线上代码出现问题了。”
胖虎赶紧放下包子,来不及擦嘴,迅速掏出电脑,边打开电脑边问“你知道哪里报错吗,为什么报错吗”
学妹:“不知道啊……”
胖虎:“……行吧,我自己看下吧。”
学妹从来没有见过如此严肃的学长,大气也不敢喘,也不敢走,就默默的看学长在查问题。
胖虎:“找到了,这是谁写的代码啊,map 使用了 new 初始化”
map1 := new(map[string]string)
学妹:“
是我,学长……”
胖虎:“行吧,看在你是我学妹份上,今天跟你简单科普一下吧”
var test1 int
var test2 string
我们可以通过 var+变量名称+变量类型 进行声明变量,当我们没有给它赋值的时候,它们的结果是变量类型的零值。
比如说 string 的零值是"", int 的零值是0,引用类型的零值是nil。
以上两种类型我们可以直接使用,但如果把它改成指针会怎么样呢?
package main
import "fmt"
func main() {
var test *string
fmt.Println(test)
*test = "测试"
}
执行结果如下:
这是为什么呢,因为对于引用类型的变量,不仅要声明,并且还要给它分配内存。怎么给它分配内存呢?这就要用到了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 底层是结构体,这样说,你可能不理解,举个🌰吧。”
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 也是用于内存分配的内置函数,但是和new不同,源码如下图所示。
大意是make
内置函数分配并初始化一个slice
、map
或chan
类型的对象。像new
函数一样,第一个参数是类型,而不是值。
与new
不同,make
的返回类型与其参数的类型相同,而不是指向它的指针。结果的取决于传入的类型。
并且 slice
在 make 的时候,第二个参数必须传递,也就是切片的长度。否则会编译失败。
new
函数底层主要是调用go1.17/src/runtime/malloc.go
中的 newobject 方法。
这里可以看到 newobject 方法,底层调用 mallocgc 方法的时候,needzero 传的是 true ,所以返回值是传入类型零值的指针。
通过执行以下命令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
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)
感兴趣的同学可以去看下源代码
学妹:“懂了学长,那我总结一下吧”
胖虎:“恩,不错不错,果然没有看错你,今天晚上有时间吗,跟大家分享一下你今天学到的知识吧。”
学妹:“改天可以吗?晚上男朋友约我一起吃饭了”
胖虎os:当初面试时候她说没有男朋友,才把她招进来的,怎么现在突然有男朋友了?没想到小丑竟然是我自己
我为大家整理了一些学习资料礼包,关注公众号回复指令:【Golang】、【操作系统】、【Linux】、【LeetCode】、【Golang电子书】即可获得相应学习资料!