Go语言 nil:空值/零值

nil 标识符是不能比较的

package main

import "fmt"

func main() {
    fmt.Println(nil == nil)
}
.\nil.go:6:18: invalid operation: nil == nil (operator == not defined on nil)

nil 不是关键字或保留字

nil 并不是Go语言的关键字或者保留字,也就是说我们可以定义一个名称为 nil 的变量,比如下面这样:

var nil = errors.New("my god")

虽然上面的声明语句可以通过编译,但是并不提倡这么做。

不同类型 nil 的指针是一样的

package main

import "fmt"

func main() {

    //fmt.Println(nil == nil)

    var arr []int
    var num *int
    var string *string
    fmt.Printf("%p\n", arr)
    fmt.Printf("%p\n", num)
    fmt.Printf("%p\n", string)

}
0x0
0x0
0x0

通过运行结果可以看出 arr 、num、 string 的指针都是 0x0。

不同类型的 nil 是不能比较的

package main

import "fmt"

func main() {

    var arr []int
    var num *int
    var string *string
    fmt.Printf("%p\n", arr)
    fmt.Printf("%p\n", num)
    fmt.Println(arr == num)

}
.\nil.go:14:18: invalid operation: arr == num (mismatched types []int and *int)

两个相同类型的 nil 值也可能无法比较

在Go语言中 map、slice 和 function 类型的 nil 值不能比较,比较两个无法比较类型的值是非法的,下面的语句无法编译。

var s1 []int
    var s2 []int
    fmt.Printf("\n", s1 == s2)
.\nil.go:16:22: invalid operation: s1 == s2 (slice can only be compared to nil)

nil 是 map、slice、pointer、channel、func、interface 的零值

package main

import "fmt"

func main() {

  

    var m map[int]string
    var ptr *int32
    var c chan int32
    var slice []int32
    var f func()
    var i interface{}

    fmt.Printf("%#v \n", m)
    fmt.Printf("%#v \n", ptr)
    fmt.Printf("%#v \n", c)
    fmt.Printf("%#v \n", slice)
    fmt.Printf("%#v \n", f)
    fmt.Printf("%#v \n", i)
}
map[int]string(nil)
(*int32)(nil)
(chan int32)(nil)
[]int32(nil)
(func())(nil)
<nil>

零值是Go语言中变量在声明之后但是未初始化被赋予的该类型的一个默认值。

不同类型的 nil 值占用的内存大小可能是不一样的

一个类型的所有的值的内存布局都是一样的,nil 也不例外,nil 的大小与同类型中的非 nil 类型的大小是一样的。但是不同类型的 nil 值的大小可能不同。

package main

import (
    "fmt"
    "unsafe"
)

func main() {

    // var arr []int
    // var num *int
    // var string *string
    // fmt.Printf("%p\n", arr)
    // fmt.Printf("%p\n", num)
    // fmt.Println(arr == num)

    // var s1 []int
    // var s2 []int
    // fmt.Printf("\n", s1 == s2)

    var m map[int]string
    var ptr *int32
    var c chan int32
    var slice []int32
    var f func()
    var i interface{}

    fmt.Printf("%#v \n", m)
    fmt.Printf("%#v \n", ptr)
    fmt.Printf("%#v \n", c)
    fmt.Printf("%#v \n", slice)
    fmt.Printf("%#v \n", f)
    fmt.Printf("%#v \n", i)

    fmt.Println(unsafe.Sizeof(m))
    fmt.Println(unsafe.Sizeof(ptr))
    fmt.Println(unsafe.Sizeof(c))
    fmt.Println(unsafe.Sizeof(slice))
    fmt.Println(unsafe.Sizeof(f))
    fmt.Println(unsafe.Sizeof(i))
}
map[int]string(nil)
(*int32)(nil)
(chan int32)(nil)
[]int32(nil)
(func())(nil)
<nil>
8
8
8
24
8
16

具体的大小取决于编译器和架构,上面打印的结果是在 64 位架构和标准编译器下完成的,对应 32 位的架构的,打印的大小将减半。