GoLang nil与interface的空指针深入分析
作者:alwaysrun
Go语言中任何类型在未初始化时都对应一个零值:布尔类型是false,整型是0,字符串是"",而指针、函数、interface、slice、channel和map的零值都是nil
nil
Go中,每个指针都有2个基本信息,指针的类型和指针的值(type,value);当执行==
时,需要比较类型与值(只有类型与值都相等时,才会相等)。
nil并不是Go语言的关键字或者保留字,而是一个预定义好的标识符:
- nil之间不能比较:
nil==nil
是不允许的,会抛出operator == not defined on untyped nil
异常; - 不同类型的nil之间不能互相比较:如切片的nil,不能与map的nil做比较;
- nil是
map、slice、pointer、channel、func、interface
的零值; - 不同类型nil值占用空间可能大小不同;
在64位机器上运行时nil的大小:
func main() { var p *struct{} fmt.Println(unsafe.Sizeof(p), p == nil) // 8 var s []int fmt.Println(unsafe.Sizeof(s), s == nil) // 24 var m map[int]bool fmt.Println(unsafe.Sizeof(m), m == nil) // 8 var c chan string fmt.Println(unsafe.Sizeof(c), c == nil) // 8 var f func() fmt.Println(unsafe.Sizeof(f), f == nil) // 8 var i interface{} fmt.Println(unsafe.Sizeof(i), i == nil) // 16 }
slice
一个nil的slice,除了不能索引外,其他的操作都正常;当append元素时,slice会自动进行扩容。
slice是一个简单的结构体,包含(长度、容量、指向数组的指针);当slice为nil时,长度、容量都为0,指针为空。
map
一个nil的map,是一个真正的空指针,除len与for-range外,其他操作不能正常使用。
非nil的map,是一个指向内部HashMap的指针;空map(map[string]int{}
)与为nil的map是不同的,空map只是没有内容,可在上面做任何的map操作。
interface
interface底层由两部分组成(参见《golang反射简介》),一个是类型,一个值,也就是类似于:(Type, Value)。只有当类型和值都是nil的时候,才等于nil:
func inFun(v interface{}) { fmt.Println("fun-interface:", v == nil) } func main() { var a interface{} var b []string var c string fmt.Println(a == nil) inFun(a) // true fmt.Println(b == nil) inFun(b) // false //fmt.Println(c == nil) // can not compare with nil inFun(c) // false } // true // fun-interface: true // true // fun-interface: false // fun-interface: false
本身是interface时,传递interface参数,其nil属性不变;若是普通指针,则传递给interface参数时,都为非空(!=nil);
指针是否为空
那如何判定interface里面的动态值是否空?此时需要借助反射reflect来实现:
func nilCheck(v interface{}) { defer func() { if err := recover(); err != nil { fmt.Println("panic:", err) } }() if v == nil { fmt.Println("nilCheck: interface is nil") return } vi := reflect.ValueOf(v) fmt.Println("nilCheck:", vi.IsNil()) } func main() { var a interface{} var b []string var c string nilCheck(a) nilCheck(b) nilCheck(c) } // nilCheck: interface is nil // nilCheck: true // panic: reflect: call of reflect.Value.IsNil on string Value
对于非指针类型,在反射后调用IsNil时会抛出异常。其实现:
func (v Value) IsNil() bool { k := v.kind() switch k { case Chan, Func, Map, Pointer, UnsafePointer: if v.flag&flagMethod != 0 { return false } ptr := v.ptr if v.flag&flagIndir != 0 { ptr = *(*unsafe.Pointer)(ptr) } return ptr == nil case Interface, Slice: // Both interface and slice are nil if first word is 0. // Both are always bigger than a word; assume flagIndir. return *(*unsafe.Pointer)(v.ptr) == nil } panic(&ValueError{"reflect.Value.IsNil", v.kind()}) }
到此这篇关于GoLang nil与interface的空指针深入分析的文章就介绍到这了,更多相关GoLang nil内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!