不灭的焱

加密类型:SHA/AES/RSA下载Go
复合类型:切片(slice)、映射(map)、指针(pointer)、函数(function)、通道(channel)、接口(interface)、数组(array)、结构体(struct) Go类型+零值nil
引用类型:切片(slice)、映射(map)、指针(pointer)、函数(function)、通道(channel) Go引用

作者:AlbertWen  添加时间:2025-11-25 15:54:02  修改时间:2025-11-25 20:38:13  分类:02.Go语言编程  编辑

在 Go 语言中,make() 和 new() 都是用于内存分配的内置函数,但它们的设计目标、适用类型、返回值和内存初始化行为有本质区别。下面从核心维度对比,并结合示例说明各自的使用场景。

一、核心区别总览

维度 make() new()
适用类型 仅支持 3 种引用类型:slicemapchan 支持所有类型(值类型 / 引用类型)
内存初始化 分配内存 + 初始化(创建 “可用” 的实例) 仅分配内存(零值填充),不初始化结构
返回值 返回类型本身(T) 返回类型的指针(*T)
底层行为 为引用类型分配底层数组 / 哈希表 / 通道缓冲区,并初始化关联字段(如 slice 的 len/cap),注:关联字段用零值填充 仅开辟一块内存,填充对应类型的零值,返回内存地址

二、详细解析 & 示例

1. make ():专为引用类型初始化设计

make() 只能用于 slicemapchan,因为这三种类型是 “动态结构”,仅 分配内存(零值)无法直接使用(比如空 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) 的行为非常简单:

  1. 为类型 T 分配一块内存;
  2. 将内存填充为 T 的零值(如 int→0,bool→false,指针→nil);
  3. 返回这块内存的地址(即 *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),更简洁

三、关键总结

  1. 何时用 make ()

    • 需要创建可用的 slicemapchan 实例时;
    • 需指定 slice 的长度 / 容量、map 的初始容量、chan 的缓冲区大小时。
  2. 何时用 new ()

    • 需要为值类型(如 int、struct)分配内存并返回指针时;
    • 需显式获取 “零值指针”,且无需复杂初始化时;
    • 注意:引用类型几乎不用 new (),因为返回的 nil 实例无法直接使用。
  3. 简化记忆

    • 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 + 赋值