Golang之nil

Golang之nilGolang 之 nilgolang 中的 nil 很多人都误以为与 Java PHP 等编程语言中的 null 一样 但是实际上 Golang 的 niu 复杂得多了 如果不信 那我们继续往下阅读 nil 为预声明的标示符 定义在 builtin builtin go nilisapredec

Golang之nil

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的零值

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) } 

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

package main import ( "fmt" ) func main() { 
    type IntPtr *int fmt.Println(IntPtr(nil) == (*int)(nil)) //true fmt.Println((interface{ 
   })(nil) == (*int)(nil)) //false } 
  1. 同一类型的两个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 } 
  1. 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[0xc0:1 
   
     :99] 
    } 

总结

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

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/200670.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月20日 上午10:21
下一篇 2026年3月20日 上午10:21


相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号