加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

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

发布时间:2020-12-16 19:08:45 所属栏目:大数据 来源:网络整理
导读:golang中的 nil ,很多人都误以为与Java、PHP等编程语言中的null一样。但是实际上Golang的niu复杂得多了,如果不信,那我们继续往下阅读。 nil 为预声明的标示符,定义在 builtin/builtin.go , //nilisapredeclaredidentifierrepresentingthezerovaluefora/

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

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

//nilisapredeclaredidentifierrepresentingthezerovaluefora//pointer,channel,func,interface,map,orslicetype.//Typemustbeapointer,orslicetypevarnilType

//Typeishereforthepurposesofdocumentationonly.Itisastand-in//foranyGotype,butrepresentsthesametypeforanygivenfunction//invocation.typeTypeint

nil的零值

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

PS:这里没有说结构体struct的零值为nil,因为struct的零值与其属性有关

nil没有默认的类型,尽管它是多个类型的零值,必须显式或隐式指定每个nil用法的明确类型。

packagemain

funcmain(){

	//明确.
	_=(*struct{})(nil)
	_=[]int(nil)
	_=map[int]bool(nil)
	_=chanstring(nil)
	_=(func())(nil)
	_=interface{}(nil)

	//隐式.
	var_*struct{}=nil
	var_[]int=nil
	var_map[int]bool=nil
	var_chanstring=nil
	var_func()=nil
	var_interface{}=nil
}

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

packagemainimport"fmt"funcmain(){	
nil:=123
	fmt.Println(nil)//123
	var_map[string]int=nil//cannotusenil(typeint)astypemap[string]intinassignment
}

nil类型的地址和值大小

nil类型的所有值的内存布局始终相同,换一句话说就是:不同类型nil的内存地址是一样的。

packagemain
import(
	"fmt"
)
funcmain(){
	varmmap[int]string
	varptr*int
	varsl[]int
	fmt.Printf("%pn",m)//0x0
	fmt.Printf("%pn",ptr)//0x0
	fmt.Printf("%pn",sl)//0x0
}

业务中一般将nil值表示为异常。nil值的大小始终与其类型与nil值相同的non-nil值大小相同。因此,表示不同零值的nil标识符可能具有不同的大小。

packagemain

import(
	"fmt"
	"unsafe"
)

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

	vars[]int=nil
	fmt.Println(unsafe.Sizeof(s))//24

	varmmap[int]bool=nil
	fmt.Println(unsafe.Sizeof(m))//8

	varcchanstring=nil
	fmt.Println(unsafe.Sizeof(c))//8

	varffunc()=nil
	fmt.Println(unsafe.Sizeof(f))//8

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

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

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

nil值比较

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

packagemain
import(
	"fmt"
)
funcmain(){
	varmmap[int]string
	varptr*int
	fmt.Printf(m==ptr)//invalidoperation:m==ptr(mismatchedtypesmap[int]stringand*int)
}

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

  • 两个值之一的类型是另一个的基础类型。

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

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

packagemain
import(
	"fmt"
)
funcmain(){
	typeIntPtr*int
	fmt.Println(IntPtr(nil)==(*int)(nil))			//true
	fmt.Println((interface{})(nil)==(*int)(nil))	//false
}

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

packagemain
import(
	"fmt"
)
funcmain(){
	varv1[]int=nil
	varv2[]int=nil
	fmt.Println(v1==v2)
	fmt.Println((map[string]int)(nil)==(map[string]int)(nil))
	fmt.Println((func())(nil)==(func())(nil))
}

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

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

3.两nil值可能不相等

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

packagemain
import(
	"fmt"
)
funcmain(){
	fmt.Println((interface{})(nil)==(*int)(nil))//false
}

常见问题

1.函数返回

funcnilReturn()(string,error){

	returnnil,nil//cannotusenilastypestringinreturnargument
}

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

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

packagemain
import(
	"fmt"
)
funcmain(){
	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类型,希望各位同学细细品味其中区别。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读