按照Go语言规范,任何类型在未初始化时都对应一个零值:布尔类型是false,整型是0,字符串是"",而指针,函数,interface,slice,channel和map的零值都是nil。
很重要:指针为空是nil,但nil不是空指针。下面说一说哦不同类型的nil的用法和坑
1、指针
指针对象的方法来说,就算指针的值为nil
也是可以调用
type Student struct {}
func (s *Student) sayHi() {
fmt.Println("hi")
}
func main(){
var s *Student
s.sayHi() // hi
}
2、interface
一个接口值实际上是包含了动态类型和实际值和两个部分的。它并不是一个指针,它的底层实现由两部分组成,一个是类型,一个值,也就是类似于:(Type, Value)。只有当类型和值都是nil
的时候,才等于nil
如下例子
:
type Student interface {
Introduce()
}
type Boy struct {} //boy继承Student
func (s *Boy) Introduce() {
fmt.Println("I am 谁谁谁")
}
func checkNil(s Student) {
if s != nil { //传过来后s!= nil,s为<*Boy,nil>
fmt.Println("传过来后s!=nil")
}else{
fmt.Println("传过来后s==nil")
}
}
func main(){
var s *Boy //此时s==nil,s为<nil,nil>
checkNil(s)
}
3、slice
它的空值并不是一个空指针,空的slice也占内存,大小是三个机器字长
下面的s1==nil,因为s1是切片
var s1 []int //0 0 (len,cap)Zero value for slice is nil
而下面的arr1!=nil,因为arr1是数组
var arr1 [5]int //0 0 0 0 0
4、map
map,function,channel都是特殊的指针,指向各自特定的实现,所以map也是指针,实际数据在堆中,未初始化的值是nil
对于nil
的map,我们可以简单把它看成是一个只读的map,不能进行写操作,否则就会panic.
func checkMapNil(str string,mappers map[int]int) {
for k,v := range mappers{
fmt.Printf("helo,tcy,%d,%d",k,v)
}
}
func main(){
//可以传空值
checkMapNil("I am tcy", map[int]int{})
checkMapNil("I am tcy", nil)
}
5、channel在栈上只是一个指针,实际的数据都是由指针所指向的堆上面
- 读或者写一个nil的channel的操作会永远阻塞。
- 关闭一个
nil
的channel会导致程序panic
- 读一个关闭的channel会立刻返回一个channel元素类型的零值。
- 写一个关闭的channel会导致panic。
// nil channels
var c chan
<- c // 永远阻塞
c <- x // 永远阻塞
close(c) // panic: close of nil channel
在知道channel关闭后,将channel的值设为nil,
nil
的channel是永远阻塞