ken

作者: 一斗 | 来源:发表于2019-05-29 16:56 被阅读0次

    001 slice的切片隐藏数据问题

    当你重新划分一个slice时,新的slice将引用原有slice的数组。如果你忘了这个行为的话,在你的应用分配大量临时的slice用于创建新的slice来引用原有数据的一小部分时,会导致难以预期的内存使用。

    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
    }
    

    为了避免这个陷阱,你需要从临时的slice中拷贝数据(而不是重新划分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数据“损坏”问题

    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并拷贝需要的数据,你可以修复这个问题。另一个选择是使用完整的slice表达式。

    package main
    
    import (
        "bytes"
        "fmt"
    )
    
    func main() {
        path := []byte("AAAA/BBBBBBB")
        sepIndex := bytes.IndexByte(path, '/')
        dir1 := path[:sepIndex: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: BBBBBBB
    
        newPath := bytes.Join([][]byte{dir1, dir2}, []byte{'/'})
        fmt.Println(string(newPath)) // prints: AAAAsuffix/BBBBBBB
    }
    

    003 函数传递slice,map,struct

    我们知道切片是3个字段构成的结构类型,所以在函数间以值的方式传递的时候,占用的内存非常小,成本很低。在传递复制切片的时候,其底层数组不会被复制,也不会受影响,复制只是复制的切片本身,不涉及底层数组。

    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
    }
    

    仔细看,这两个切片的地址不一样,所以可以确认切片在函数间传递是复制的。而我们修改一个索引的值后,发现原切片的值也被修改了,说明它们共用一个底层数组。

    函数间传递Map是不会拷贝一个该Map的副本的,也就是说如果一个Map传递给一个函数,该函数对这个Map做了修改,那么这个Map的所有引用,都会感知到这个修改。

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

    上面这个例子输出的结果是10,也就是说已经被函数给修改了,可以证明传递的并不是一个Map的副本。这个特性和切片是类似的,这样就会更高,因为复制整个Map的代价太大了。

    函数传参是值传递,所以对于结构体来说也不例外,结构体传递的是其本身以及里面的值的拷贝。

    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的值可以通过传递结构体的指针,我们稍微改动下例子

    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的值已经被改变。如果结构体里有引用类型的值,比如map,那么我们即使传递的是结构体的值副本,如果修改这个map的话,原结构的对应的map值也会被修改。

    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转换问题

    package main
    
    import (
        "encoding/json"
        "fmt"
    )
    
    func main() {
        
        // 由字符串unmarshal得到的结构
        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 { // 数值型的要使用float64去尝试转换
            fmt.Println(v) // -1
        }
    
        // 自然生成的结构
        var cd2 cardMeta
        var e, f int
        e = 3
        f = 4
        cd2.LpUrl = e
        cd2.OpenUrl = 6.5 // 默认为float64类型
    
        // d := cd2.LpUrl + f 不可以,需要先把cd2.LpUrl转换
        if v, ok := cd2.LpUrl.(int); ok {
            fmt.Println(v + f) // 7
        }
        if v, ok := cd2.OpenUrl.(float64); ok { // 使用float32转换不行
            fmt.Println(v) // 6.5
        }
    
        var ext float32
        ext = 6.5
        cd2.Ext = ext
        if v, ok := cd2.Ext.(float32); ok { // 使用float64转换不行
            fmt.Println(v) // 6.5
        }
    
        cd2.Ext2 = 3                     // 默认为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会报错,因为原始内容数值加了双引号的
        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"`
    }
    
    

    相关文章

      网友评论

          本文标题:ken

          本文链接:https://www.haomeiwen.com/subject/rbnytctx.html