Go 里面 nil 是一个关键字?还是类型?还是变量?

1,216 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第23天,点击查看活动详情

前言

刚接触Go看别人代码的时候,发现全是if xx != nil, 一脸懵,nil到底是个啥?,其实Go 里面很多类型使用 nil 来赋值和做条件判断,Go 里面 nil 是一个关键字?还是类型?还是变量?总是混淆记不住;今天就来写一点自己对于nil的理解。需要的朋友可以参考以下内容,希望对大家有帮助。

nil是个啥?

首先,我们根据源代码看一下官方是怎么定义的nil

// 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 is here for the purposes of documentation only. It is a stand-in
// for any Go type, but represents the same type for any given function
// invocation.
type Type int

从类型定义我们可以得到: 本质上是一个 Type类型的变量而已;而Type类型是基于int定义出来的一个新类型;

根据注释我们可以得到:nil是一个预先声明的标识符,表示指针、通道、函数、接口、映射或切片类型的零值,并不是GO 的关键字之一。

默认值nil

在go语言中:

  • 布尔类型的零值(初始值)为 false
  • 数值类型的零值为 0
  • 字符串类型的零值为空字符串""

除此之外其它类型的默认值为nil,nil可以代表下面这些类型的零值:

  • 指针类型(包括unsafe中的)
  • map类型
  • slice类型
  • function类型
  • channel类型
  • interface类型

nil常见的用法

_,err := funcName(xxx)
//判断err是否等于nil
if err!=nil{
	fmt.Println(err)
}

nil使用注意事项

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

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

package main

import (
"fmt"
"unsafe"
)
func main() {
	var sInt []int = nil
	fmt.Println(unsafe.Sizeof(sInt)) // 24

	var i interface{} = nil
	fmt.Println(unsafe.Sizeof(i)) // 16

	var p *struct{} = nil
	fmt.Println(unsafe.Sizeof(p)) // 8
}

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

//不同类型的nil指针是一样的
package main
import (
   "fmt"
)
func main() {
   var arr []int
   var num *int
   fmt.Printf("%p\n", arr)    //0x0
   fmt.Printf("%p", num)  //0x0
}

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

可以将不可比较类型的空值直接与 nil 标识符进行比较,但是不同类型的 nil 是不能比较的。

package main

import "fmt"

func main() {
	var arr []int
	var num []int
    //可以直接跟nil比较
	if arr == nil {
		fmt.Println("arr is nil")
	}
    //两个不同类型的nil是无法比较的,报错。
	if arr == num {
		fmt.Println("arr == num")
	}
}

总结

Go语言中nil是一个预先声明的标识符,是一个变量,表示指针、通道、函数、接口、映射或切片类型的零值,并不是GO 的关键字之一。

nil只能赋值给指针、channelfuncinterfacemapslice类型的变量 (非基础类型) 否则会引发 panic