GO基础学习(5)nil

2023-04-24  本文已影响0人  温岭夹糕

写在开头

非原创,知识搬运工,本节介绍了nil的底层结构,nil的类型
demo代码地址

往期回顾

  1. 基本数据类型
  2. slice/map/array
  3. 结构体
  4. 接口

带着问题去阅读

  1. nil可以被篡改吗
  2. 两个nil可能不相等吗
  3. nil可以有哪些类型
  4. 该demo有错误吗?如果有请指明原因
a := nil
  1. 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

上一篇 下一篇

猜你喜欢

热点阅读