go语言中,到底nil是个什么货?

什么是nil?go中关于nil的定义为(buildin.go中):

var nil Type 
type Type int

go定义变量时,会默认分配一个零值,如:bool->false、numbers -> 0 、string -> “” 。而nil则为指针、切片、map、通道、函数对象、接口对象的零值,nil可以代表多种对象,如下代码:

var a = nil       //此代码报错,因为编译器无法确定a的数据类型
var a = (int*)(nil)   //代码正确

指针nil:表示指向一个地址为0的地址,如下我们手动构造一个指针nil:

var a = (*int)(unsafe.Pointer(uintptr(0x0)))
fmt.Println(a == nil)  //true

切片nil:
切片的底层结构为:

type slice struct {
	array unsafe.Pointer
	len   int
	cap   int
}///runtime包的slice.go中

切片由三部分构成,当指针为nil,切片就为nil。

type MySlice struct {
   pointer unsafe.Pointer
   len int
   cap int
}

由于我们不能直接控制切片底层数据,创建MySlice对象映射slice结构,控制切片地址数据。

func main() {
   var sli1 = []int{}
   var sli2 []int
   var sli3 = make([]int, 4)
   fmt.Println(1,sli1 == nil) //false,因为做了初始化,pointer不为nil
   fmt.Println(2,sli2 == nil) //true,未做初始化,pointer为nil
   //此时,s1指向了切片底层的slice对象(src/runtime/slice.go中定义)
   s1 := (*MySlice)(unsafe.Pointer(&sli1))
   s1.pointer = nil
   fmt.Println(3,sli1 == nil) //true, 即使len和cap不为0,pointer为nil
   s2 := (*MySlice)(unsafe.Pointer(&sli2))
   s3 := (*MySlice)(unsafe.Pointer(&sli3))
   s2.pointer = s3.pointer//s2和s3共用底层数组
   s2.cap = 2
   s2.len = 2
   fmt.Println(4,sli2 == nil) //false,pointer不为nil,指向sli3的底层数组
   sli3[0] = 500
   fmt.Println(5,sli2[0], sli3[0]) //500 500  共用底层数组
   sli2[1] = 700
   fmt.Println(6,sli2[1], sli3[1]) //700 700  共用底层数组
}

以上示例通过底层模拟构造,访问切片底层数据,实际编码勿用。

chanel & map & func的nil:这三位大哥非常复杂,简单来说就是指向一个固定结构的指针。

interface的nil:接口也是一种数据类型,每种数据类型都有类型(Type)和值(Value),通过reflect.TypeOf()和reflect.ValueOf()可以获取类型和值。接口是一种虚拟的数据类型,任何实现了接口的对象,都可以给接口赋值。因此,当获取接口的类型和值时,获取的是实际赋值的类型的值和对象。当比较接口时,只有当接口变量的数据类型和值都相等时,接口才相等。当给接口赋值为nil时,实际上相当于将接口的Type设置为nil,没有设置Value。示例代码如下:

func main() {
   type Interface interface{}
   type Struct struct{}
   var s *Struct = nil
   var i Interface

   fmt.Println(1, s == nil)                         //true,指针
   fmt.Println(2, s == (*Struct)(nil))              //true,相同类型指针比较
   fmt.Println(3, (Interface)(s) == (*Struct)(nil)) //true,接口的Type为实际数据类型的Type,他们的值都是nil

   fmt.Println(4, (Interface)(s) == nil)     //false,接口为nil时,表示Type类型为nil,value未设置,比较的前值Type为*Struct
   fmt.Println(5, nil == i)              //true,接口为赋值,初始化Type为nil,Value未赋值
   fmt.Println(reflect.TypeOf(i), reflect.ValueOf(i))// (表示未赋值)
   i = s
   fmt.Println(reflect.TypeOf(i), reflect.ValueOf(i))//*main.Struct 
}

参考:https://zhuanlan.zhihu.com/p/151140497

你可能感兴趣的:(golang,开发语言,后端)