new返回*T指针,make返回值本身;new([]int)得nil切片无法操作,make([]int,5)得可操作切片;slice/map/channel必须用make;&make()非法因返回不可寻址临时值。

new 返回指针,make 从不返回指针
这是最常被混淆的起点:new 的返回值**总是** *T(指向类型 T 零值的指针),而 make 的返回值**永远是值本身** —— []int、map[string]int 或 chan bool,不是指针。
这意味着:
-
new([]int)返回*[]int,但解引用后是nil切片,不能append、不能索引,毫无实用价值 -
make([]int, 5)返回[]int,长度为 5,底层数组已就位,可直接赋值或追加 - 想拿到
make结果的指针?必须显式取地址:p := &s,但绝大多数情况没必要 —— 因为 slice/map/channel 本身就是引用类型,传参时天然共享底层数据
什么时候必须用 make,而不是 new
对 slice、map、channel 这三种类型,new 完全无效——它只返回一个指向 nil 的指针,而 nil 的 slice/map/channel 无法执行任何操作(append panic、map 写入 panic、chan 发送 panic)。
正确做法只有:make 是唯一合法入口:
立即学习“go语言免费学习笔记(深入)”;
-
s := make([]int, 0, 10)→ 可扩容的空切片 -
m := make(map[string]int, 8)→ 建议初始容量为 8 的 map -
c := make(chan int, 1)→ 带 1 个缓冲的 channel
反例:var p *map[string]int = new(map[string]int)→ *p 是 nil,if p != nil { (*p)["k"] = 1 } 会 panic。
new 的合理使用场景其实很窄
new 真正适合的,是当你明确需要一个指向「干净零值」的指针,且后续会主动赋值 —— 尤其在函数参数、结构体字段初始化等上下文中。
但它远不如字面量 + 取地址直观:
- ✅ 推荐:
u := &User{Name: "Alice", Age: 30}—— 初始化+取址一步到位 - ⚠️ 可用但笨重:
u := new(User); u.Name = "Alice"; u.Age = 30 - ❌ 错误预期:
new([]int)想“初始化一个切片”,结果得到的是不可用的*[]int
注意:new 对结构体不做任何构造逻辑,所有字段都是零值;它也不支持传参初始化,这点和 C++/Java 的 new 完全不同。
为什么 &make() 会编译失败
你不能写 &make([]int, 5),Go 编译器会报错:cannot take the address of make(...)。
原因在于:make 返回的是一个**不可寻址的临时值**(类似函数返回的局部变量),就像你不能写 &len("abc") 一样。
如果真需要指针(极少见),必须分两步:
s := make([]int, 5)-
p := &s→ 此时p是*[]int,指向一个有效切片
但再次强调:这几乎从不必要。Go 的 slice/map/channel 设计初衷就是“值语义 + 引用行为”,传给函数时无需指针就能修改底层数据。
最容易被忽略的一点是:二者底层完全不是一回事 —— new 调用的是通用内存分配器(mallocgc),只负责清零;而 make 是语法糖,对每种类型调用专属运行时函数(如 runtime.makeslice),真正构建了 slice header、hash table、channel queue 等运行时结构。这不是“能不能用”的问题,而是“有没有初始化成功”的根本差异。










