不得不知道的golang知识点之nil


声明:本文转载自https://my.oschina.net/u/553243/blog/1558062,转载目的在于传递更多信息,仅供学习交流之用。如有侵权行为,请联系我,我会及时删除。

插播一条广告→2021 ByteDance字节跳动内推←各城市、各方向的岗位都有,大量招人!


golang中的nil,很多人都误以为与Java、PHP等编程语言中的null一样。但是实际上Golang的niu复杂得多了,如果不信,那我们继续往下阅读。

nil 为预声明的标示符,定义在builtin/builtin.go

// nil is a predeclared identifier representing the zero value for a // pointer, channel, func, interface, map, or slice type. // Type must be a pointer, channel, func, interface, map, or slice type var nil 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 

nil的零值

按照Go语言规范,任何类型在未初始化时都对应一个零值:布尔类型是false,整型是0,字符串是"",而指针、函数、interface、slice、channel和map的零值都是nil。

PS:这里没有说结构体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 } 

如果关注过golang关键字的同学就会发现,里面并没有nil,也就是说nil并不是关键字,那么就可以在代码中定义nil,那么nil就会被隐藏。

package main  import "fmt"  func main() { 	nil := 123 	fmt.Println(nil) // 123 	var _ map[string]int = nil //cannot use nil (type int) as type map[string]int in assignment } 

nil类型的地址和值大小

nil类型的所有值的内存布局始终相同,换一句话说就是:不同类型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值表示为异常。nil值的大小始终与其类型与nil值相同的non-nil值大小相同。因此, 表示不同零值的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 } 

大小是编译器和体系结构所依赖的。以上打印结果为64位体系结构和正式 Go 编译器。对于32位体系结构, 打印的大小将是一半。

对于正式 Go 编译器, 同一种类的不同类型的两个nil值的大小始终相同。例如, 两个不同的切片类型 ( []int和[]string) 的两个nil值始终相同。

nil值比较

1.不同类型的nil是不能比较的。

package main import ( 	"fmt" ) func main() { 	var m map[int]string 	var ptr *int 	fmt.Printf(m == ptr) //invalid operation: m == ptr (mismatched types map[int]string and *int) } 

在 Go 中, 两个不同可比较类型的两个值只能在一个值可以隐式转换为另一种类型的情况下进行比较。具体来说, 有两个案例两个不同的值可以比较:

  • 两个值之一的类型是另一个的基础类型。
  • 两个值之一的类型实现了另一个值的类型 (必须是接口类型)。

nil值比较并没有脱离上述规则。

package main import ( 	"fmt" ) func main() { 	type IntPtr *int 	fmt.Println(IntPtr(nil) == (*int)(nil))			//true 	fmt.Println((interface{})(nil) == (*int)(nil))	//false } 

2.同一类型的两个nil值可能无法比较 因为golang中存在map、slice和函数类型是不可比较类型,它们有一个别称为不可比拟的类型,所以比较它们的nil亦是非法的。

package main import ( 	"fmt" ) func main() { 	var v1 []int = nil 	var v2 []int = nil 	fmt.Println(v1 == v2) 	fmt.Println((map[string]int)(nil) == (map[string]int)(nil)) 	fmt.Println((func())(nil) == (func())(nil)) } 

不可比拟的类型的值缺是可以与“纯nil”进行比较。

package main import ( 	"fmt" ) func main() { 	fmt.Println((map[string]int)(nil) == nil)  //true 	fmt.Println((func())(nil) == nil)		   //true } 

3.两nil值可能不相等

如果两个比较的nil值之一是一个接口值, 而另一个不是, 假设它们是可比较的, 则比较结果总是 false。原因是在进行比较之前, 接口值将转换为接口值的类型。转换后的接口值具有具体的动态类型, 但其他接口值没有。这就是为什么比较结果总是错误的。

package main import ( 	"fmt" ) func main() { 	fmt.Println( (interface{})(nil) == (*int)(nil) ) // false } 

常见问题

1.函数返回

func nilReturn() (string,error)  {  	return nil,nil  //cannot use nil as type string in return argument } 

因为error是接口类型所以error类型没有报错。

2.map的nil key map的key为指针、函数、interface、slice、channel和map,则key可以为nil。

package main import ( 	"fmt" ) func main() { 	mmap := make(map[*string]int,4) 	a:="a" 	mmap[&a] = 1 	mmap[nil] = 99 	fmt.Println(mmap)   //map[0xc042008220:1 <nil>:99] } 

总结

nil之所以比较难以理解因为我们经常混淆了nil值和nil类型,希望各位同学细细品味其中区别。

本文发表于2017年10月30日 12:39
(c)注:本文转载自https://my.oschina.net/u/553243/blog/1558062,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如有侵权行为,请联系我们,我们会及时删除.

阅读 852 讨论 0 喜欢 0

抢先体验

扫码体验
趣味小程序
文字表情生成器

闪念胶囊

你要过得好哇,这样我才能恨你啊,你要是过得不好,我都不知道该恨你还是拥抱你啊。

直抵黄龙府,与诸君痛饮尔。

那时陪伴我的人啊,你们如今在何方。

不出意外的话,我们再也不会见了,祝你前程似锦。

这世界真好,吃野东西也要留出这条命来看看

快捷链接
网站地图
提交友链
Copyright © 2016 - 2021 Cion.
All Rights Reserved.
京ICP备2021004668号-1