Open sinomoe opened 6 years ago
最近在看net/http的源码巩固golang知识,使用了一些基本特性写了一个http下载工具,在网络不好时总是会报以下错误
net/http
2018/05/14 asm_amd64.s:573: goroutine 0 closed panic: runtime error: invalid memory address or nil pointer dereference [signal 0xc0000005 code=0x0 addr=0x40 pc=0x5d988e] goroutine 5 [running]: main.download(0x66b1d5, 0x63, 0x66b212, 0x26, 0x0, 0x0, 0x68a720, 0xc04206eae0) D:/golang/src/github.com/sino2322/playground/downloader.go:14 +0x8e main.main.func1(0xc042008360, 0xc04204e120, 0xc0420cc620, 0xc042008370, 0xc04204e180, 0x0) D:/golang/src/github.com/sino2322/playground/main.go:48 +0x4ce created by main.main D:/golang/src/github.com/sino2322/playground/main.go:30 +0x1ce
顺着错误信息找到了错误提示处的代码
res, err := client.Get(url) defer res.Body.Close() if err != nil { return 0, err }
乍一眼好像没错,参考这条解释后才恍然大悟。
根据net/http的文档描述,When err is nil, resp always contains a non-nil resp.Body,由于此处报错invalid memory address or nil pointer dereference,是由于网络原因导致返回err进而导致res为nil,并且,由于defer只是延时函数调用,域和方法都是直接访问的,这样就导致了错误的访问。
When err is nil, resp always contains a non-nil resp.Body
invalid memory address or nil pointer dereference
err
res
defer
解决方法就是在defer res.Body.Close()之前处理错误并返回。修改后的代码如下:
defer res.Body.Close()
res, err := client.Get(url) if err != nil { return 0, err } defer res.Body.Close()
defer的函数在执行时就会对参数进行求值,并不是等到被调用时才求值。下面的例子将有助于理解。
func trace(s string) string { fmt.Println("entering:", s) return s } func un(s string) { fmt.Println("leaving:", s) } func a() { defer un(trace("a")) // 会对参数进行求值,所以运行到此处立即执行 trace("a") fmt.Println("in a") } func b() { defer un(trace("b")) // 会对参数进行求值,所以运行到此处立即执行 trace("b") fmt.Println("in b") a() } func main() { b() }
打印结果为
entering: b in b entering: a in a leaving: a leaving: b
最近在看
net/http
的源码巩固golang知识,使用了一些基本特性写了一个http下载工具,在网络不好时总是会报以下错误顺着错误信息找到了错误提示处的代码
乍一眼好像没错,参考这条解释后才恍然大悟。
根据
net/http
的文档描述,When err is nil, resp always contains a non-nil resp.Body
,由于此处报错invalid memory address or nil pointer dereference
,是由于网络原因导致返回err
进而导致res
为nil,并且,由于defer
只是延时函数调用,域和方法都是直接访问的,这样就导致了错误的访问。解决方法就是在
defer res.Body.Close()
之前处理错误并返回。修改后的代码如下:defer
注意事项defer
的函数在执行时就会对参数进行求值,并不是等到被调用时才求值。下面的例子将有助于理解。打印结果为