写在开头
非原创,知识搬运工,本节介绍了nil的底层结构,nil的类型
demo代码地址
往期回顾
带着问题去阅读
- nil可以被篡改吗
- 两个nil可能不相等吗
- nil可以有哪些类型
- 该demo有错误吗?如果有请指明原因
a := nil
- nil的长度是多少
1.什么是nil
首先抛出问题,我们默认nil是零值,为什么不能和0比较
nil == 0
go文档中将nil的解释为,是一个预定义的标识符zero value,它不是关键词,同时他也不是基础类型和函数类型,是被单独罗列的。
上面文档的解释太少,我们再从源码中寻找蛛丝马迹,在go的文件buildin/buildin.go中定义了很多内置类型,其中
// nil is a predeclared identifier representing the zero value for a
// pointer, channel, func, interface, map, or slice type.
var nil Type // Type must be a pointer, channel, func, interface, map, or slice type
type Type int //这里的int不是整数类型是交给汇编的
从注释中我们可以得到以下信息:
1.它是一个预定义标识符针
2.它代表的是一个零值
3.这些零值的类型即Type必须是一个指针/通道/函数/接口/map/slice类型
1.1实验验证
所以为啥开头的不能和0比较明白了吧?0是int类型,与结构体比较也是错误的,它不在上面罗列的范围内demo
var s student
t.Log(s==nil)
nil和其他复杂类型作比较demo,这里其实之前在接口那一章详细提到过,不同的数据结构比较规则不同,如接口是T和V全为空值时才为nil,和底层数据结构挂钩
type A interface{}
type B struct{}
var a *B //a是一个指针
var m map[int]int
var ch chan int
var f func()
var s []int
t.Log(a == nil)
t.Log(m == nil)
t.Log(ch == nil)
t.Log(f == nil)
t.Log(s == nil)
结果全为true,这里还需要注意map、slice和ch在声明后,没用make赋值是不能使用的,不是代表空的通道或者map,是真的一个零值地址,强行操作则抛出异常
panic: assignment to entry in nil map [recovered]
panic: assignment to entry in nil map
这是因为这些复杂类型跟nil的比较不单是值的比较
再对nil进行类型转化demo
func TestNiltoAnotherType(t *testing.T) {
type B struct{ name string }
var a *B //a是一个指针
t.Log(a == nil) //零值指针 true
t.Log(a == (*B)(nil)) //true
}
需要注意的是当我们直接写出来nil时,它是没有类型的,或者说编译器没法确定它的类型,这是不合法的nil类型demo
func TestStateNil(t *testing.T) {
a := nil
t.Log(a) // use of untyped nil in assignment
}
nil是Go中唯一没有默认类型的非类型值。必须有足够的信息让编译器从上下文中推断出nil的类型。
合法的nil类型就是上面这种
var a *B
那么不同的nil就有不同的长度demo
func TestSizeofNil(t *testing.T) {
var p *struct{} = nil
fmt.Println(unsafe.Sizeof(p)) // 8
var s []int = nil
fmt.Println(unsafe.Sizeof(s)) // 24
var m map[int]bool = nil
fmt.Println(unsafe.Sizeof(m)) // 8
var c chan string = nil
fmt.Println(unsafe.Sizeof(c)) // 8
var f func() = nil
fmt.Println(unsafe.Sizeof(f)) // 8
var i interface{} = nil
fmt.Println(unsafe.Sizeof(i)) // 16
}
还需要注意的是,既然它是用var声明的就意味着和js的undefined的一样是可以被用户自己覆盖的
func TestChangeNil(t *testing.T) {
var nil int = 1
a := 1
t.Log(a == nil) //此时就能通过
}
最后不同类型的nil类型比较时也需要注意,这篇博客贴了一个因不同类型nil产生的坑
参考
1.go101-nil
2.没有人比我更懂Nil
网友评论