该用 new 时仅需分配零值内存并返回指针;必须用 make 初始化 slice、map、chan,因其完成内存分配与类型特定初始化(如设置 len/cap、哈希桶、缓冲区),避免 nil 操作 panic。

什么时候该用 new,什么时候必须用 make
new 只做一件事:分配零值内存并返回指针;make 只用于内置引用类型(slice、map、chan),它不仅分配内存,还完成初始化(比如为 slice 设置 len 和 cap)。如果你写了 new([]int),得到的是一个指向 nil 切片的指针,后续对它的操作会 panic;而 make([]int, 5) 才真正创建出可读写的切片。
常见错误现象:panic: runtime error: index out of range 或 assignment to entry in nil map,往往是因为误用 new 初始化了 map 或 slice。
-
new(T)返回*T,T可以是任意类型,但返回值所指内存全是零值 -
make(T, args...)返回T本身,且T只能是[]T、map[K]V、chan T - 结构体字段含 slice/map/chan 时,
new(Struct)不会初始化这些字段——它们仍是 nil,需单独make
make 的三个参数怎么配,为什么第二个参数不是容量
make 对不同类型的参数含义不同:对 slice 是 make([]T, len, cap),其中 cap 是可选的;对 map 和 chan,第二个参数是 hint(预估大小或缓冲区长度),不是强制容量。
使用场景:想预分配足够空间避免频繁扩容,就传第三个参数;但传太大会浪费内存,传太小会触发多次底层数组复制。比如 make([]int, 0, 100) 创建一个长度为 0、容量为 100 的切片,追加 100 个元素也不会 realloc。
立即学习“go语言免费学习笔记(深入)”;
-
make(map[int]string, 10)中的10是哈希桶初始数量提示,Go 会向上取整到 2 的幂,实际可能分配 16 个桶 -
make(chan int, 5)创建带缓冲的 channel,5是缓冲区长度,超过会阻塞 - 省略第三个参数时,
make([]int, 5)等价于make([]int, 5, 5)
new 出来的指针和直接取地址有什么区别
两者都返回 *T,但语义和生命周期完全不同:new(T) 分配堆内存,返回的指针可安全返回函数外;而 &localVar 取的是栈上变量地址,如果该变量是局部变量且函数返回后被回收,再用这个指针就是悬垂指针(Go 编译器通常会自动将其移到堆上,但行为不透明)。
性能影响:过度使用 new 会增加 GC 压力;而编译器对栈变量逃逸分析很敏感,有时看似安全的 &struct{} 也会被升到堆上。
-
new(int)和new(struct{ x int })都返回初始化为 0 的堆内存指针 -
p := &x中若x是局部变量,是否逃逸取决于后续是否被外部引用,无法靠肉眼判断 - 用
go tool compile -gcflags="-m" file.go可查看逃逸分析结果,确认new是否真必要
嵌套类型初始化时最容易漏掉的 make
结构体字段是 map 或 slice 时,new 或字面量初始化不会递归调用 make。这是最常踩的坑:代码编译通过,运行时却 panic。
示例:type User struct { Posts []string; Meta map[string]interface{} },无论是 new(User) 还是 User{},Posts 和 Meta 都是 nil,不能直接 append 或赋值。
- 正确做法是在构造后手动
u.Posts = make([]string, 0, 10)、u.Meta = make(map[string]interface{}) - 更稳妥的方式是提供构造函数,如
func NewUser() *User { return &User{Posts: make([]string, 0), Meta: make(map[string]interface{})} } - JSON 解码到结构体时,
nilslice/map 字段不会被自动初始化,解码空数组或对象仍保持 nil —— 这和make无关,但容易混淆
复杂点在于,有些初始化逻辑藏在第三方库或方法内部,你以为它帮你 make 了,其实没有。别信文档,跑起来看 panic。









