shownb / shownb.github.com

shownb.github.io
shownb.github.io
5 stars 1 forks source link

golang学习中碰到的各种问题总结。 #16

Open shownb opened 6 years ago

shownb commented 6 years ago

go不会对 类型是interface{} 的 slice 进行转换 。

既然空的 interface 可以接受任何类型的参数,那么一个 interface{}类型的 slice 是不是就可以接受任何类型的 slice ?

func printAll(vals []interface{}) { //1
    for _, val := range vals {
        fmt.Println(val)
    }
}
func main(){
    names := []string{"stanley", "david", "oscar"}
    printAll(names)
}

上面的代码是按照我们的假设修改的,执行之后竟然会报

cannot use names (type []string) as type []interface {} in argument to printAll

错误,why? 这个错误说明 go 没有帮助我们自动把 slice 转换成 interface{} 类型的 slice,所以出错了。go 不会对 类型是interface{} 的 slice 进行转换 。为什么 go 不帮我们自动转换,一开始我也很好奇,最后终于在 go 的 wiki 中找到了答案 https://github.com/golang/go/wiki/InterfaceSlice 大意是 interface{} 会占用两个字长的存储空间,一个是自身的 methods 数据,一个是指向其存储值的指针,也就是 interface 变量存储的值,因而 slice []interface{} 其长度是固定的N2,但是 []T 的长度是Nsizeof(T),两种 slice 实际存储值的大小是有区别的(文中只介绍两种 slice 的不同,至于为什么不能转换猜测可能是 runtime 转换代价比较大)。

不用make来初始化map,会生成一个nil的map

var templates map[string]string
templates["sb"]="2"

会产生 panic: assignment to entry in nil map 因为他开始是个nil指针,自然找不到指向哪里,自然赋值失败。

golang的每个变量都有一个内存地址。 var nilpointer map[int]string //定义了是一个map变量,他有内存地址存储这个变量。nilpointer实际是一个指针,但现在他默认是nil,空指针。

make函数创建map返回的是一个指针类型。

func makemap(t *maptype,hint int64,h *hmap,bucket unsafe.Pointer) *hmap{
}
package main

import (
    "fmt"
)

var nilpointer map[int]string
var initpointer = make(map[int]string)

func main() {
    fmt.Println(nilpointer == nil)   //没有用make,只定义了是一个map,默认值是nil,空指针
    fmt.Printf("%p\n", nilpointer)   //0x0 当一个指针被定义后没有分配到任何变量时,它的值为 nil。
    fmt.Printf("%p\n", &nilpointer)  //0x183cf0,每个golang的变量都有一个内存地址存放这个变量
    fmt.Printf("%p\n", initpointer)  //0x10448240 make初始化后返回一个*hmap指针,这个指针的值是0x10448240
    fmt.Printf("%p\n", &initpointer) //指向指针的指针
    fmt.Println(initpointer == nil)  //经过make之后,已经不再是nil
}

https://play.golang.org/p/kYtUd7XAqXs

切片作为参数时候,在函数内append会有坑

如果切片作为参数传入的时候,在函数内不用append来扩容的话,是可以作为引用来用的。 但如果用了append,就改变了切片里面的指针地址,达不到作为引用效果了。 当slice_c=append(slice_a,slice_b)中len(slice_a)+len(slice_b)<=cap(slice_a)时不会扩容,否则会被扩为cap(slice_a)的两倍,如果slice len大于1024了,会扩容四分之一cap。具体实现源码如下:

newcap := old.cap
if newcap+newcap < cap {
    newcap = cap
} else {
    for {
        if old.len < 1024 {
            newcap += newcap
        } else {
            newcap += newcap / 4
        }
        if newcap >= cap {
            break
        }
    }
}

从例2可以看见,append返回的slice指向了个新的地址。但是即使没扩容,slice指向同一个地址的情况下,为何slice_c还是不等于slice_a呢?其实slice不是单纯一个指针,而是个C的struct实现的 详见 https://segmentfault.com/a/1190000012008869

shownb commented 6 years ago

map并发读写问题

多个协程同时写也会出现fatal error: concurrent map writes的错误 如下代码是问题代码

for _,v :=range accounts {
wg.Add(1)
go func (w *sync.WaitGroup,v string) {
    tmp[v],_ = conn.EthGetBalance(v,"latest")
    w.Done()
    }(wg,v)
}

解决方法是加锁

go func (w *sync.WaitGroup,v string,lock *sync.Mutex) {
    ret,_ := conn.EthGetBalance(v,"latest")
    lock.Lock()
    tmp[v]=ret
    lock.Unlock()
    w.Done()
    }(wg,v,lock)
}