Golang nil 解析

2017-06-10 language golang network

简介

一般来说,无论是直接声明一个变量,或者通过 new() make() 创建,默认都会将对应的值设置为 0 ,可以参考 Zero Value ,也就是 false 0 "" nil 等,每个类型对应的零值如下。

bool       -> false
numbers    -> 0
string     -> ""

pointers   -> nil
slices     -> nil
maps       -> nil
channels   -> nil
functions  -> nil
interfaces -> nil

在 go 语言中,nil 是一个比较特殊的变量,其定义在 builtin/builtin.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

也即 nil 并不是一个关键字,可将 nil 定义成其它,例如 var nil = errors.New("hi") ,只是不建议这么做。

另外,需要注意,结构体 struct 的零值不是 nil ,因为 struct 的零值跟其属性相关。

类型

nil 没有默认的类型,尽管它可以是多个类型的零值,此时必须显式或隐式指定每个 nil 用法的明确类型。

package main

func main() {
	_ = (*struct{})(nil)
	_ = []int(nil)
	_ = map[int]bool(nil)
	_ = chan string(nil)
	_ = (func())(nil)
	_ = interface{}(nil)
	
	var _ *struct{} = nil
	var _ []int = nil
	var _ map[int]bool = nil
	var _ chan string = nil
	var _ func() = nil
	var _ interface{} = nil
}

其实 nil 是一个预定义的变量,而不是关键字,这样就意味着,你可以直接自己定义一个 nil 变量将预定义的变量覆盖。

package main

import "fmt"

func main() {
    nil := 123
    fmt.Println(nil) // 123
    var _ map[string]int = nil // error
}

虽然是预定义的变量,但是它没有具体的类型,不像 iota 定义为 int 类型。

地址

因为 nil 实际上是预定义的变量,那么其对应的地址是相同的。

package main

import (
    "fmt"
)

func main() {
	var m map[int]string
	var ptr *int
	var sl []int
	fmt.Printf("%p\n", m)       //0x0
	fmt.Printf("%p\n", ptr )    //0x0
	fmt.Printf("%p\n", sl )     //0x0
}

大小

不同的 nil 值,其大小是不同的。

package main

import (
	"fmt"
	"unsafe"
)

func main() {
	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
}