在Go语言里,new和make是两个用于内存分配的内置函数,但它们的设计目的和使用方式差别很大。new用来分配任意类型的零值内存,make只用来初始化slice、map和channel这三种引用类型。下面从原理层面说明它们的不同。

函数签名与返回值
new的函数签名非常简单:
// 内置函数new func new(Type) *Type
它接收一个类型,在堆上分配该类型的零值内存,并返回指向这块内存的指针。make的签名则是:
// 内置函数make func make(t Type, size ...IntegerType) Type
make只接受slice、map、channel这三种类型,返回的是初始化后的类型本身,而不是指针。
适用类型对比
| 函数 | 适用类型 | 返回值 |
|---|---|---|
| new | 任意类型(int、struct、slice、map等) | 指向零值的指针 *Type |
| make | 仅slice、map、channel | 初始化后的类型本身 Type |
使用new的问题示例
如果用new创建一个map,得到的是指向nil map的指针,直接赋值会panic:
package main
import "fmt"
func main() {
m := new(map[string]int)
// 下面这行会panic,因为*map是nil
// (*m)["a"] = 1
fmt.Println(m) // 输出:&map[]
}
正确方式是使用make:
package main
import "fmt"
func main() {
m := make(map[string]int)
m["a"] = 1
fmt.Println(m) // 输出:map[a:1]
}
底层分配原理
new在编译阶段会被转换为对runtime.newobject的调用,后者从堆上获取一块内存并清零。make在编译期根据类型分别转为runtime.makeslice、runtime.makemap和runtime.makechan,这些函数不仅分配内存,还会设置长度、容量、哈希桶或缓冲队列等内部结构。
slice的make示例
package main
import "fmt"
func main() {
s := make([]int, 2, 5)
fmt.Println(len(s), cap(s)) // 输出:2 5
fmt.Println(s) // 输出:[0 0]
}
总结建议
- 需要任意类型的零值指针时用new
- 初始化slice、map、channel必须用make
- 不要对map或channel使用new,否则得到的是nil指针
理解这两个函数的差异,能帮助你避免Go语言中常见的初始化错误,也能更清楚地知道内存是在什么时候、以什么结构被分配的。