什么是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