1. 函數(shù)傳參
package main
import "fmt"
func main() {
slice := []int{1, 2}
fmt.Printf("data:%v, len:%d, cap:%dn", slice, len(slice), cap(slice))
updateslice(slice)
fmt.Printf("after data:%v, len:%d, cap:%dn", slice, len(slice), cap(slice))
}
func updateslice(slice []int) {
slice[0] = 12
slice = Append(slice, 10)
fmt.Printf("inner data:%v, len:%d, cap:%dn", slice, len(slice), cap(slice))
}
執(zhí)行結(jié)果為:
data:[1 2], len:2, cap:2
after data:[12 2], len:2, cap:2
由于切片是引用類型,所以數(shù)據(jù)修改會(huì)影響外部,實(shí)際上內(nèi)外是指向同一個(gè)底層數(shù)據(jù)。但是當(dāng)對(duì)updateslice函數(shù)做append操作后,指向結(jié)果為:
data:[1 2], len:2, cap:2
inner data:[12 2 10], len:3, cap:4
after data:[12 2], len:2, cap:2
發(fā)現(xiàn)內(nèi)部的添加數(shù)據(jù)修改并沒有影響到外部變量,此時(shí)可以看到內(nèi)部的切片容量發(fā)生了擴(kuò)容,之前的數(shù)據(jù)會(huì)被copy一份到新的地址,此時(shí)該擴(kuò)容后的切片指向的是新的地址,函數(shù)內(nèi)外切片并不是指向同一個(gè)底層數(shù)據(jù)。
總結(jié):當(dāng)數(shù)據(jù)調(diào)整發(fā)生擴(kuò)容時(shí),內(nèi)外切片不是指向同一個(gè)底層數(shù)據(jù);否則,指向同一個(gè)底層數(shù)組。
2. for range
type student struct {
name string
age int
}
func main() {
m := make(map[string]*student)
stus := []student{
{name: "小王子", age: 18},
{name: "娜扎", age: 23},
{name: "大王八", age: 9000},
}
// 這里出現(xiàn)問題
for _, stu := range stus {
m[stu.name] = &stu
}
for k, v := range m {
fmt.Println(k, "=>", v.name)
}
}
對(duì)于該代碼,我們的預(yù)期結(jié)果是
娜扎 => 娜扎
大王八 => 大王八
小王子 => 小王子
但是得到的結(jié)果是:
小王子 => 大王八
娜扎 => 大王八
大王八 => 大王八
原因是 for遍歷時(shí),變量stu是值的副本,每次遍歷僅進(jìn)行struct值拷貝,它指向的地址不變,所以每一次操作m[stu.name] = &stu,實(shí)際指向的都是同一個(gè)地址,遍歷完成后,存儲(chǔ)的是結(jié)構(gòu)體最后一個(gè)值的拷貝。根本原因在于for-range會(huì)使用同一塊內(nèi)存去接收循環(huán)中的值。
應(yīng)取切片中原始值的指針,修改如下:
for _, stu := range stus {
value := stu
m[stu.name] = &value
}
3.錯(cuò)誤踩坑
- struct結(jié)構(gòu)體中變量的名字不能和tag標(biāo)簽中的名字一樣
- 避免go包互相調(diào)用中出現(xiàn)初始化函數(shù)init(),會(huì)發(fā)生runtime異常。panic: runtime error: invalid memory address or nil pointer dereference
- Go語言中所有的傳參都是值傳遞(傳值),都是一個(gè)副本,一個(gè)拷貝。因?yàn)榭截惖膬?nèi)容有時(shí)候是非引用類型(int、string、struct等),這樣就在函數(shù)中無法修改原內(nèi)容的數(shù)據(jù);有的是引用類型(指針、map、slice、chan),因此可以修改原來內(nèi)容。引用類型和傳引用是兩個(gè)概念。
- 要檢查切片是否為空,請(qǐng)始終使用len(s) == 0來判斷,而不應(yīng)該使用s == nil來判斷,已分配內(nèi)存(使用make)的切片不等于nil,未分配內(nèi)存時(shí)為nil。切片本質(zhì)上是對(duì)底層數(shù)組的封裝,包含指向底層數(shù)組的地址、切片的容量、長(zhǎng)度。