Go 学习笔记(28)— nil(nil 不能比较、不是关键字或保留字、nil 没有默认类型、不同类型的 nil 指针是一样的、不同类型的 nil 是不能比较的、相同类型的 nil 可能也无法比较)

Go 学习笔记(28)— nil(nil 不能比较、不是关键字或保留字、nil 没有默认类型、不同类型的 nil 指针是一样的、不同类型的 nil 是不能比较的、相同类型的 nil 可能也无法比较)在 Go 语言中 布尔类型的零值 初始值 为 false 数值类型的零值为 0 字符串类型的零值为空字符串 而指针 切片 映射 通道 函数和接口的零值则是 nil nil 是 Go 语言中一个预定义好的标识符 有过其他编程语言开发经验的开发者也许会把 nil 看作其他语言中的 null NULL 其实这并不是完全正确的 因为 Go 语言中的 nil 和其他语言中的 nul

Go 语言中,布尔类型的零值(初始值)为 false ,数值类型的零值为 0,字符串类型的零值为空字符串 "",而指针、切片、映射、通道、函数和接口的零值则是 nil

nilGo 语言中一个预定义好的标识符,有过其他编程语言开发经验的开发者也许会把 nil 看作其他语言中的 nullNULL ),其实这并不是完全正确的,因为 Go 语言中的 nil 和其他语言中的 null 有很多不同点。

下面通过几个方面来介绍一下 Go 语言中 nil

1. nil 标识符是不能比较的

package main import ( "fmt" ) func main() { 
    fmt.Println(nil == nil) } 

输出结果:

# command-line-arguments ./hello.go:8:18: invalid operation: nil == nil (operator == not defined on nil) 

这点和 python 等动态语言是不同的,在 python 中,两个 None 值永远相等。

>>> None == None True >>> 

从上面的运行结果不难看出, == 对于 nil 来说是一种未定义的操作。

2. nil 不是关键字或保留字

nil 并不是 Go 语言的关键字或者保留字,也就是说我们可以定义一个名称为 nil 的变量,比如下面这样:

var nil = errors.New("my god") 

虽然上面的声明语句可以通过编译,但是并不提倡这么做。

3. nil 没有默认类型

package main import ( "fmt" ) func main() { 
    fmt.Printf("%T", nil) print(nil) } 

输出结果:

# command-line-arguments ./hello.go:9:7: use of untyped nil 

4. 不同类型 nil 的指针是一样的

package main import ( "fmt" ) func main() { 
    var arr []int var num *int fmt.Printf("%p\n", arr) fmt.Printf("%p", num) } 

输出结果:

0x0 0x0 

通过运行结果可以看出 arrnum 的指针都是 0x0。

5. 不同类型的 nil 是不能比较的

package main import ( "fmt" ) func main() { 
    var m map[int]string var ptr *int fmt.Printf(m == ptr) } 

输出结果:

# command-line-arguments ./hello.go:10:15: invalid operation: m == ptr (mismatched types map[int]string and *int) 

6. 两个相同类型的 nil 值也可能无法比较

Go 语言中 mapslicefunction 类型的 nil 值不能比较,比较两个无法比较类型的值是非法的,下面的语句无法编译。

package main import ( "fmt" ) func main() { 
    var s1 []int var s2 []int fmt.Printf(s1 == s2) } 

输出结果:

# command-line-arguments ./hello.go:10:16: invalid operation: s1 == s2 (slice can only be compared to nil) 

通过上面的错误提示可以看出,能够将上述不可比较类型的空值直接与 nil 标识符进行比较,如下所示:

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

7. nil 是 map、slice、pointer、channel、func、interface 的零值

package main import ( "fmt" ) func main() { 
    var m map[int]string var ptr *int var c chan int var sl []int var f func() var i interface{ 
   } fmt.Printf("%#v\n", m) // map[int]string(nil) fmt.Printf("%#v\n", ptr) // (*int)(nil) fmt.Printf("%#v\n", c) // (chan int)(nil) fmt.Printf("%#v\n", sl) // []int(nil) fmt.Printf("%#v\n", f) // (func())(nil) fmt.Printf("%#v\n", i) // 
    } 

零值是 Go 语言中变量在声明之后但是未初始化被赋予的该类型的一个默认值。

8. 不同类型的 nil 值占用的内存大小可能是不一样的

一个类型的所有的值的内存布局都是一样的, nil 也不例外, nil 的大小与同类型中的非 nil 类型的大小是一样的。但是不同类型的 nil 值的大小可能不同。

package main import ( "fmt" "unsafe" ) func main() { 
    var p *struct{ 
   } fmt.Println(unsafe.Sizeof(p)) // 8 var s []int fmt.Println(unsafe.Sizeof(s)) // 24 var m map[int]bool fmt.Println(unsafe.Sizeof(m)) // 8 var c chan string fmt.Println(unsafe.Sizeof(c)) // 8 var f func() fmt.Println(unsafe.Sizeof(f)) // 8 var i interface{ 
   } fmt.Println(unsafe.Sizeof(i)) // 16 } 

具体的大小取决于编译器和架构,上面打印的结果是在 64 位架构和标准编译器下完成的,对应 32 位的架构的,打印的大小将减半。

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

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

(0)
上一篇 2026年3月17日 下午8:09
下一篇 2026年3月17日 下午8:09


相关推荐

  • Android界面设计适配不同屏幕的尺寸和密度解读[通俗易懂]

    Android界面设计适配不同屏幕的尺寸和密度解读[通俗易懂]Android是运行在各种提供不同的屏幕尺寸和密度的设备。Android系统提供跨设备的统一开发环境和处理大部分的工作,以调整每个应用程序的用户界面,以在其上显示的画面。 同时,该系统提供了API,允许您控制您的应用程序的UI特定的屏幕尺寸和密度,以优化你的用户界面设计,不同的屏幕配置。作为设计师也要最大程度地为所有设备的体现良好的用户体验或是方便使用阅读,而不是简单地拉伸以适应屏幕上的设备。

    2022年6月17日
    31
  • c++ ostringstream的用法(c语言的sprintf)

    c++ ostringstream的用法(c语言的sprintf)转载: https://blog.csdn.net/qq1987924/article/details/7671154ostringstream的用法【本文来自】http://www.builder.com.cn/2003/0304/83250.shtmlhttp://www.cppblog.com/alantop/archive/2007/07/10/27823.html使用stringstre…

    2022年6月23日
    35
  • 最大公约数和最小公倍数

    最大公约数和最小公倍数最大公约数和最小公倍数

    2022年4月24日
    39
  • mybatis 原理[通俗易懂]

    mybatis 原理[通俗易懂]问题:mybatis是基于mapper接口开发的,mapper接口是执行SQL语句的呢?mybatis对mapper代码的包装主要包含了4个类。1.首先mapper需要将接口进行注册,并且需要获得mapper代理工厂(mapperregistry)mapperregistry类将mapper接口进行注册,并获取mapper代理工厂的工具类(mapperproxyfactory)2.加载mapp…

    2025年6月23日
    5
  • Yii Framework2.0开发教程(2)使用表单Form

    Yii Framework2.0开发教程(2)使用表单Form

    2022年1月26日
    39
  • C++ 内存池介绍与经典内存池的实现

    C++ 内存池介绍与经典内存池的实现内存池 MemoryPool 是一种内存分配方式 通常我们习惯直接使用 new malloc 等 API 申请内存 这样做的缺点在于所申请内存块的大小不定 当频繁使用时会造成大量的内存碎片并进而降低性能

    2026年3月19日
    2

发表回复

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

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