生产环境中遇到了一次奇怪的报错,代码结构大致如下所示:

type A struct {
    a string
    b int
}

func (a *A) foo() {
    fmt.Println("foo")
}

func (a *A) bar() {
    fmt.Println(a.a)
}

func main() {
    var a *A
    a.foo()
    a.bar()
}


程序运行时,会在a.bar()处报空指针错误。具体时fmt.Println(a.a)的位置。

排查问题时感到比较疑惑:如果是空指针错误,a在调用foo()时就是空指针,为什么在a.foo()处没有报错?

在foo()和bar()中加入调用栈打印后,得到运行结果:

main.(*A).foo(0x0)
        .../main.go:14 +0x22
main.main()
        .../main.go:25 +0x2a
        
main.(*A).bar(0x0)
        .../main.go:19 +0x26
main.main()
        .../main.go:26 +0x37
panic: runtime error: invalid memory address or nil pointer dereference

这么一看就是合理的了:

foo()和bar()的入参都是0x0,也就是空指针a。

虽然调用foo()的是空指针,但成员函数的地址在函数编译时就确定了,所以在执行时没有使用到空指针。

bar()中对空指针解引用,所以会导致错误。

结论是: golang中结构提空指针调用它的方法,如果函数执行过程中没有对空指针解引用,则不会报nil pointer dereference。