本篇文章基於 Golang 1.17.2
胖虎坐在公司工位上正在吃剛才買的包子。
新來的小學妹就匆忙地跑過來,慌慌張張說:“學長,不好了,線上程式碼出現問題了。”
胖虎趕緊放下包子,來不及擦嘴,迅速掏出電腦,邊開啟電腦邊問“你知道哪裡報錯嗎,為什麼報錯嗎”
學妹:“不知道啊……”
胖虎:“……行吧,我自己看下吧。”
學妹從來沒有見過如此嚴肅的學長,大氣也不敢喘,也不敢走,就默默的看學長在查問題。
胖虎:“找到了,這是誰寫的程式碼啊,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)
感興趣的同學可以去看下原始碼
學妹:懂了學長,那我總結一下吧
胖虎:“恩,不錯不錯,果然沒有看錯你,今天晚上有時間嗎,跟大家分享一下你今天學到的知識吧。”
學妹:“改天可以嗎?晚上男朋友約我一起吃飯了”
胖虎os:當初面試時候她說沒有男朋友,才把她招進來的,怎麼現在突然有男朋友了?沒想到小丑竟然是我自己
本作品採用《CC 協議》,轉載必須註明作者和本文連結