背景
定位线上服务panic的问题,捕获的panic栈日志显示某一行出现nil指针操作,很常见的一个panic问题,去定位到项目中该行代码,发现有好两处对指针的操作,类似如下的操作
type
本以为很简单,item为空指针,不能对其进行操作。但是,突然发现一个有意思的现象,item.Add函数竟然执行成功了,导致panic的原因是后面的item.A。如果你看到这里,觉得这不是大家都知道的事情吗,那就不用往下看了。
原因探索
其实仔细思考下,原因也很简单,item虽然值为nil,但是它的类型是Item,Item类型绑定了Add函数,而且Add并没有方位对象的任何变量,只是单纯的对参数做了加法返回,而导致panic的item.A 是因为访问了对象,接下来我们通过实际操作,一一验证我们的想法。
验证
- 代码
package
运行结果
结论
我们通过反射,看出结构体空指针与普通的nil还是不同的,结构体指针的类型是可以获取到的,相应的它对应的函数应该可以执行。
- 代码
package
运行结果
结论
这次我们命名了一个接口,并且创建了一个空的接口变量,发现它的类型和value竟然与真正的nil完全相同,至于为什么,可能需要单独写一篇有关interface的结构的文章来解释。但是在这里我们大致可以理解,空的接口变量,与实现该接口的结构体的空指针是完全不同的。结构体空指针应该是在某个地方存着该结构体的函数变量,而空的接口变量对应的函数指针是nil。
- 代码
package
运行结果
结论
这就比较明显了,虽然空的结构体指针可以调用对应的函数,但是绝对不能访问其内部字段。
我们在来看一个比较有意思的事情。
- 代码
package
运行结果
结论
其实也比较好理解,我们把一个空指针,强转成一个*AA类型,必然给它附上了对应的函数指针,它就跟纯nil不太一样了。
最后
其实,在实验的过程中产生了很多的疑问,我会在后续的文章给出一一解答。
- golang 的interface底层是什么结构?(其实有iface与eface两种,区别为是否包含函数)
- golang的结构体与interface又是什么联系,结构体绑定函数后就可以作为interface,golang的编译器在绑定函数的时候做了什么?
- golang的结构体的函数存在哪里,函数调用的时候是如何找到的?
- golang的结构体的函数绑定有值绑定与指针绑定,区别是什么?