★ make、new 与零值有什么区别?
一句话结论:new(T) 分配一块零值内存并返回 *T(对任意类型可用);make 只服务于 slice/map/channel 三种引用类型,返回初始化好的非指针值;而“零值”指声明后不赋值时的默认值,slice/map/channel 的零值是 nil——三者最易踩的坑是 nil map 写入会 panic、nil channel 会永久阻塞。
三种机制对比
| 项目 | new(T) | make(T) | 零值声明 |
|---|---|---|---|
| 适用范围 | 任意类型 | 仅 slice/map/channel | 任意类型 |
| 返回值 | *T(指针) | T(已初始化) | 直接使用变量 |
| 内部结构 | 仅置零,无底层结构 | 分配底层数组/哈希表/缓冲 | nil |
| 典型写法 | p := new(int) | s := make([]int, 0, 8) | var s []int |
var s []int 得到 nil 切片;make([]int, 0, 8) 得到非 nil 的空切片。两者都能 append,但判 nil、序列化结果不同。
零值可用性速查
- nil 切片:可 append(会新分配)、可 len/cap、可 range;等于 nil。
- nil map:可读(返回零值)、可 len、可 range;写入 panic。
- nil channel:收发都会永久阻塞;close(nil) 会 panic。
- 结构体零值:字段各自为零值,指针/slice/map 字段为 nil,多数场景可直接使用。
var m map[string]int
m["a"] = 1 // panic: assignment to entry in nil map
m2 := make(map[string]int)
m2["a"] = 1 // 正常
var c chan int
// <-c // 若执行会永久阻塞,只能靠收发配对解开
p := new(int) // *int 类型,*p == 0
*p = 42
s := make([]int, 0, 8) // 空但非 nil
var s2 []int // nil
fmt.Println(s == nil, s2 == nil) // false true
常见追问 / 记忆点
- 追问:什么时候用 new?——很少;需要零值指针时 new(T) 与 &T{} 等价,后者更常见。
- 追问:nil 切片与空切片序列化一样吗?——encoding/json 中 nil 切片编码为 null,空切片为 [],二者不等价。
- 记忆点:make 只给 slice/map/channel 用;零值背三句——slice 能用、map 能读不能写、channel 会堵。