零值机制 Zero Value
Go语言所有类型变量在声明后都会自动初始化,这种默认初始化值称为零值。这是Go语言保证程序安全性的重要设计。
基础类型零值
1
2
3
4
5
6
|
var (
a int // 0
b float64 // 0.0
c string // ""
d bool // false
)
|
符合类型零值
1
2
3
4
5
6
7
8
9
|
var (
e *int // nil(指针)
f []int // nil(切片)
g map[int]int // nil(字典)
h chan int // nil(通道)
i func() // nil(函数)
j interface{} // nil(接口)
k struct{} // 空结构体(所有字段零值)
)
|
nil 是什么
nil
是一个只适用于特定类型的预定义的标识符
1
2
3
4
5
6
7
8
9
10
11
|
// 有效类型
var a *int = nil // 指针
var b []int = nil // 切片
var c map[int]int = nil // 字典
var d chan int = nil // 通道
var e func() = nil // 函数
var f interface{} = nil // 接口
// 无效类型(编译错误)
var g int = nil // 基础类型不允许
var h struct{} = nil // 结构体不允许
|
它在内存层面表现为以下几种情况
类型 |
内存表现 |
可用性 |
切片 |
所有字段为 0 |
可以添加元素但需要先初始化 |
映射 |
所有字段为 0 |
不能直接添加键值对,需要先make |
通道 |
所有字段为 0 |
不能直接收发数据,需要先make |
指针 |
地址为 0 |
不能直接解引用,会panic |
接口 |
类型和值均为 0 |
可以调用方法但会panic |
函数 |
函数地址为 0 |
不能直接调用,会panic |
空值(nil)在切片的特殊处理
可以对nil切片进行append操作
我们可以对 nil
切片执行append
操作,对于nil
函数、字典、接口的操作是会panic
的
1
2
3
4
5
6
7
|
var s []int // nil切片
s = append(s, 1) // 合法操作,自动初始化底层数组
// 安全操作建议:
if len(s) > 0 { // 推荐使用长度判断
fmt.Println(s[0])
}
|
nil 切片append时候的生命历程
初始的时候是len=0
且cap=0
的一个切片
1
|
var s []int // 底层结构:{array: nil, len: 0, cap: 0}
|
在进行append
的时候会自动完成以下步骤
1
|
s = append(s, 1) // 自动完成以下步骤:
|
- 检查当前容量是否足够 → 0 < 1 → 需要扩容
- 分配新数组(最小初始容量为1)
- 复制元素(此时无旧元素)
- 返回新切片:{
array: 新地址
, len:1
, cap:1
}
与普通切片相比
操作 |
nil 切片 |
空切片(make([]int,0)) |
预分配切片(make([]int,0,5)) |
初始容量 |
0 |
0 |
5 |
首次append扩容策略 |
分配容量1 |
分配容量1 |
直接使用现有容量 |
内存分配时机 |
首次append时 |
首次append时 |
创建时已分配 |
底层数组地址 |
动态变化 |
动态变化 |
固定直到扩容 |
最优实践
- 随意对
nil
切片进行append
- 对于
nil
字典必须先进行make
- 利用
nil
切片表示无数据,利用空切片表示空集