ken

001 slice的切片隱藏?cái)?shù)據(jù)問題

當(dāng)你重新劃分一個(gè)slice時(shí),新的slice將引用原有slice的數(shù)組。如果你忘了這個(gè)行為的話,在你的應(yīng)用分配大量臨時(shí)的slice用于創(chuàng)建新的slice來引用原有數(shù)據(jù)的一小部分時(shí),會(huì)導(dǎo)致難以預(yù)期的內(nèi)存使用。

package main

import "fmt"

func get() []byte {
    raw := make([]byte, 10000)
    fmt.Println(len(raw), cap(raw), &raw[0]) //prints: 10000 10000 0xc000086000
    return raw[:3]
}

func main() {
    data := get()
    fmt.Println(len(data), cap(data), &data[0]) //prints: 3 10000 0xc000086000
}

為了避免這個(gè)陷阱,你需要從臨時(shí)的slice中拷貝數(shù)據(jù)(而不是重新劃分slice)

package main

import "fmt"

func get() []byte {
    raw := make([]byte, 10000)
    fmt.Println(len(raw), cap(raw), &raw[0]) //prints: 10000 10000 0xc000090000
    res := make([]byte, 3)
    copy(res, raw[:3])
    return res
}

func main() {
    data := get()
    fmt.Println(len(data), cap(data), &data[0]) //prints: 3 3 0xc000016088
}

002 slice數(shù)據(jù)“損壞”問題

package main

import (
    "bytes"
    "fmt"
)

func main() {
    path := []byte("AAAA/BBBBBBB")
    sepIndex := bytes.IndexByte(path, '/')
    dir1 := path[:sepIndex]
    dir2 := path[sepIndex+1:]
    fmt.Println(string(dir1)) // prints: AAAA
    fmt.Println(string(dir2)) // prints: BBBBBBB

    dir1 = append(dir1, "suffix"...)
    fmt.Println(string(dir1)) // prints: AAAAsuffix
    fmt.Println(string(dir2)) // prints: uffixBB

    newPath := bytes.Join([][]byte{dir1, dir2}, []byte{'/'})
    fmt.Println(string(newPath)) // prints: AAAAsuffix/uffixBB
}

通過分配新的slice并拷貝需要的數(shù)據(jù),你可以修復(fù)這個(gè)問題。另一個(gè)選擇是使用完整的slice表達(dá)式。

package main

import (
    "bytes"
    "fmt"
)

func main() {
    path := []byte("AAAA/BBBBBBB")
    sepIndex := bytes.IndexByte(path, '/')
    dir1 := path[:sepIndex:sepIndex] // 使用完整表達(dá)式
    dir2 := path[sepIndex+1:]
    fmt.Println(string(dir1)) // prints: AAAA
    fmt.Println(string(dir2)) // prints: BBBBBBB

    dir1 = append(dir1, "suffix"...)
    fmt.Println(string(dir1)) // prints: AAAAsuffix
    fmt.Println(string(dir2)) // prints: BBBBBBB

    newPath := bytes.Join([][]byte{dir1, dir2}, []byte{'/'})
    fmt.Println(string(newPath)) // prints: AAAAsuffix/BBBBBBB
}

003 函數(shù)傳遞slice,map,struct

我們知道切片是3個(gè)字段構(gòu)成的結(jié)構(gòu)類型,所以在函數(shù)間以值的方式傳遞的時(shí)候,占用的內(nèi)存非常小,成本很低。在傳遞復(fù)制切片的時(shí)候,其底層數(shù)組不會(huì)被復(fù)制,也不會(huì)受影響,復(fù)制只是復(fù)制的切片本身,不涉及底層數(shù)組。

func main() {
    slice := []int{1, 2, 3, 4, 5}
    fmt.Printf("%p\n", &slice)  // 0xc420082060
    modify(slice)
    fmt.Println(slice)  // [1 10 3 4 5]
}

func modify(slice []int) {
    fmt.Printf("%p\n", &slice)  // 0xc420082080
    slice[1] = 10
}

仔細(xì)看,這兩個(gè)切片的地址不一樣,所以可以確認(rèn)切片在函數(shù)間傳遞是復(fù)制的。而我們修改一個(gè)索引的值后,發(fā)現(xiàn)原切片的值也被修改了,說明它們共用一個(gè)底層數(shù)組。

函數(shù)間傳遞Map是不會(huì)拷貝一個(gè)該Map的副本的,也就是說如果一個(gè)Map傳遞給一個(gè)函數(shù),該函數(shù)對(duì)這個(gè)Map做了修改,那么這個(gè)Map的所有引用,都會(huì)感知到這個(gè)修改。

func main() {
    dict := map[string]int{"王五": 60, "張三": 43}
    modify(dict)
    fmt.Println(dict["張三"]) // 10
}

func modify(dict map[string]int) {
    dict["張三"] = 10
}

上面這個(gè)例子輸出的結(jié)果是10,也就是說已經(jīng)被函數(shù)給修改了,可以證明傳遞的并不是一個(gè)Map的副本。這個(gè)特性和切片是類似的,這樣就會(huì)更高,因?yàn)閺?fù)制整個(gè)Map的代價(jià)太大了。

函數(shù)傳參是值傳遞,所以對(duì)于結(jié)構(gòu)體來說也不例外,結(jié)構(gòu)體傳遞的是其本身以及里面的值的拷貝。

func main() {
    jim := person{10, "Jim"}
    fmt.Println(jim) // {10 Jim}
    modify(jim)
    fmt.Println(jim) // {10 Jim}
}

func modify(p person) {
    p.age = p.age + 10
}

type person struct {
    age  int
    name string
}

如果上面的例子我們要修改age的值可以通過傳遞結(jié)構(gòu)體的指針,我們稍微改動(dòng)下例子

func main() {
    jim := person{10, "Jim"}
    fmt.Println(jim) // {10 Jim}
    modify(&jim)
    fmt.Println(jim) // {20 Jim}
}

func modify(p *person) {
    p.age = p.age + 10
}

type person struct {
    age  int
    name string
}

非常明顯的,age的值已經(jīng)被改變。如果結(jié)構(gòu)體里有引用類型的值,比如map,那么我們即使傳遞的是結(jié)構(gòu)體的值副本,如果修改這個(gè)map的話,原結(jié)構(gòu)的對(duì)應(yīng)的map值也會(huì)被修改。

003 map初始化問題

// 未初始化的map可以取值(零值),但不能賦值
    var m map[string]map[string]float64

    score := m["a"]["ac"]
    fmt.Println(score) // 0
    //m["a"]["b"] = 34  // panic

    var m1 map[string]int
    it := m1["s"]
    fmt.Println(it) // 0
    //m1["b"] = 5 // panic

004 interface轉(zhuǎn)換問題

package main

import (
    "encoding/json"
    "fmt"
)

func main() {
    
    // 由字符串unmarshal得到的結(jié)構(gòu)
    a := `{
    "width": "200",
    "height": "200",
    "img_url": "http://v2.addnewer.com/media/2019/03/1551843852785.jpeg",
    "title": "舒膚佳-長效抑菌",
    "source": "舒膚佳",
    "origin_price": "11",
    "discount_price": "10.80",
    "lp_url": "-1",
    "open_url": -1
    }`
    var cd cardMeta
    err := json.Unmarshal([]byte(a), &cd)
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println(cd)
    if v, ok := cd.LpUrl.(string); ok {
        fmt.Println(v) // -1
    }

    if v, ok := cd.OpenUrl.(float64); ok { // 數(shù)值型的要使用float64去嘗試轉(zhuǎn)換
        fmt.Println(v) // -1
    }

    // 自然生成的結(jié)構(gòu)
    var cd2 cardMeta
    var e, f int
    e = 3
    f = 4
    cd2.LpUrl = e
    cd2.OpenUrl = 6.5 // 默認(rèn)為float64類型

    // d := cd2.LpUrl + f 不可以,需要先把cd2.LpUrl轉(zhuǎn)換
    if v, ok := cd2.LpUrl.(int); ok {
        fmt.Println(v + f) // 7
    }
    if v, ok := cd2.OpenUrl.(float64); ok { // 使用float32轉(zhuǎn)換不行
        fmt.Println(v) // 6.5
    }

    var ext float32
    ext = 6.5
    cd2.Ext = ext
    if v, ok := cd2.Ext.(float32); ok { // 使用float64轉(zhuǎn)換不行
        fmt.Println(v) // 6.5
    }

    cd2.Ext2 = 3                     // 默認(rèn)為int
    if v, ok := cd2.Ext2.(int); ok { // 使用int32, int64 不行
        fmt.Println(v) //3
    }

}

type cardMeta struct {
    Width         string      `json:"width"`
    Height        string      `json:"height"`
    ImgUrl        string      `json:"img_url"`
    Title         string      `json:"title"`
    Source        string      `json:"source"`
    OriginPrice   string      `json:"origin_price,omitempty"` //使用float類型去unmarshal會(huì)報(bào)錯(cuò),因?yàn)樵純?nèi)容數(shù)值加了雙引號(hào)的
    DiscountPrice string      `json:"discount_price,omitempty"`
    LpUrl         interface{} `json:"lp_url,omitempty"`
    OpenUrl       interface{} `json:"open_url,omitempty"`
    Ext           interface{} `json:"ext"`
    Ext2          interface{} `json:"ext_2"`
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容