在 Go 语言中,make() 和 new() 都是用于内存分配的内置函数,但它们的设计目标、适用类型、返回值和内存初始化行为有本质区别。下面从核心维度对比,并结合示例说明各自的使用场景。
一、核心区别总览
| 维度 | make() | new() |
|---|---|---|
| 适用类型 | 仅支持 3 种引用类型:slice、map、chan |
支持所有类型(值类型 / 引用类型) |
| 内存初始化 | 分配内存 + 初始化(创建 “可用” 的实例) | 仅分配内存(零值填充),不初始化结构 |
| 返回值 | 返回类型本身(T) | 返回类型的指针(*T) |
| 底层行为 | 为引用类型分配底层数组 / 哈希表 / 通道缓冲区,并初始化关联字段(如 slice 的 len/cap),注:关联字段用零值填充 | 仅开辟一块内存,填充对应类型的零值,返回内存地址 |
二、详细解析 & 示例
1. make ():专为引用类型初始化设计
make() 只能用于 slice、map、chan,因为这三种类型是 “动态结构”,仅 分配内存(零值)无法直接使用(比如空 slice 指针为 nil,空 map 指针为 nil),必须通过 make() 完成底层结构的 初始化,返回可用的实例(非指针)。
- 用途:用于 slice、map、channel 的创建和初始化
- 参数:接受类型和可选的容量/长度参数
- 返回值:返回类型本身,而不是指针
- 初始化:进行完整的初始化,使其立即可用
示例 1:slice
// 错误:new返回*[]int,且底层数组未初始化,len/cap均为0 s1 := new([]int) *s1 = append(*s1, 1) // 可以用,但需要解引用,且初始是nil slice // 正确:make分配并初始化底层数组,返回[]int类型(直接可用) s2 := make([]int, 5, 10) // len=5, cap=10,底层数组已分配 fmt.Println(s2) // 输出: [0 0 0 0 0] s2 = append(s2, 1) // 直接使用,无需解引用
示例 2:map
// 错误:new返回*map[string]int,且map本身是nil,无法赋值 m1 := new(map[string]int) // *m1["a"] = 1 // 编译错误:invalid operation: *m1["a"] (type *map[string]int does not support indexing) // 修正:先初始化map实例 *m1 = make(map[string]int) (*m1)["a"] = 1 // 繁琐,无意义 // 正确:make直接返回可用的map实例 m2 := make(map[string]int) m2["a"] = 1 // 直接赋值,无需解引用
示例 3:chan
// 错误:new返回*chan int,chan本身是nil,无法收发 c1 := new(chan int) // <-*c1 // 运行时panic:nil channel receive // 正确:make初始化通道缓冲区(可选),返回可用的chan c2 := make(chan int, 1) // 带缓冲区的通道 c2 <- 1 // 直接发送,无panic
2. new ():通用内存分配(零值指针)
new(T) 的行为非常简单:
- 为类型
T分配一块内存; - 将内存填充为
T的零值(如 int→0,bool→false,指针→nil); - 返回这块内存的地址(即
*T类型)。
new() 适用于需要显式指针且仅需零值初始化的场景,尤其适合值类型(如 int、struct)。
- 用途:用于值类型的内存分配,返回指向该类型的指针
- 参数:接受一个类型参数
- 返回值:返回指向该类型的指针
- 初始化:将内存清零(零值初始化)
示例 1:值类型(int)
i := new(int) fmt.Println(*i) // 输出:0(int的零值) *i = 10 fmt.Println(*i) // 输出:10
示例 2:自定义结构体
type User struct {
Name string
Age int
}
// new返回*User,字段为零值(Name="",Age=0)
u := new(User)
u.Name = "张三" // 直接通过指针修改字段
fmt.Println(u) // 输出:&{张三 0}
示例 3:引用类型(无意义,不如 make)
// new用于引用类型:返回*[]int,但其指向的[]int是nil(零值) s := new([]int) // 要使用必须先初始化: *s = make([]int, 0) // 等价于直接用make([]int, 0),更简洁
三、关键总结
-
何时用 make ():
- 需要创建可用的
slice、map、chan实例时; - 需指定 slice 的长度 / 容量、map 的初始容量、chan 的缓冲区大小时。
- 需要创建可用的
-
何时用 new ():
- 需要为值类型(如 int、struct)分配内存并返回指针时;
- 需显式获取 “零值指针”,且无需复杂初始化时;
- 注意:引用类型几乎不用 new (),因为返回的 nil 实例无法直接使用。
-
简化记忆:
make(T, ...)→ 返回 T(仅 slice/map/chan),初始化底层结构;new(T)→ 返回 *T(所有类型),仅分配零值内存。
四、常见误区
- ❌ 用
new(map[string]int)创建 map 后直接赋值:会 panic,因为 map 本身是 nil; - ❌ 认为
make()返回指针:实际上返回的是类型本身(如[]int),只是 slice/map/chan 本质是引用类型,底层指向动态结构; - ✅ 替代方案:值类型的指针也可通过
&T{}创建(如&User{}),比new(User)更灵活(可直接初始化字段)。
使用建议
- 对于 slice、map、channel,总是使用
make() - 对于其他需要分配内存并获取指针的情况,使用
new() - 大多数情况下,直接 使用字面量 或 变量声明 更简洁
// 通常更推荐的方式
p := &Person{} // 而不是 new(Person)
m := map[string]int{} // 而不是 make(map[string]int)
s := []int{1, 2, 3} // 而不是 make + 赋值