























在 Go 语言中,new 和 make 都是用于内存分配的内建函数,但它们的适用类型、行为方式和返回值有本质区别。
| 特性 | new |
make |
|---|---|---|
| 适用类型 | 任意类型(包括结构体、整数、数组等) | 仅限 slice、map、channel 三种内置引用类型 |
| 返回值 | 指向该类型的指针(*T) |
返回该类型本身(T,不是指针),且已初始化 |
| 内存行为 | 分配内存并置零值,不初始化内部结构 | 分配内存并初始化内部结构(如 slice 的底层数组、map 的哈希桶、channel 的缓冲区等) |
| 使用场景 | 获取一个可指向零值对象的指针 | 创建可立即使用的 slice、map、channel |
new 函数func new(Type) *TypeType 值的内存,将其所有位初始化为零值(zero value),并返回该内存地址的指针。p := new(int) // p 是 *int,指向 0
fmt.Println(*p) // 输出 0
*p = 42
type Person struct {
Name string
Age int
}
p := new(Person) // p 是 *Person,Name="", Age=0
make 函数make(Type, size ...) Type(具体参数依类型而定)slice、map、channel,不仅分配内存,还会初始化它们的内部数据结构(如 slice 的 len、cap 和底层数组,map 的哈希桶等),返回已初始化的该类型值(非指针)。// slice: 类型, len, cap
s := make([]int, 3, 5) // len=3, cap=5,底层数组有5个元素,前3个为0
// map: 类型, 可指定容量(hint)
m := make(map[string]int) // 等价于 map[string]int{}
// channel: 类型, 可指定缓冲区大小
ch := make(chan int, 10) // 缓冲区容量为10
new 只负责分配内存并返回指针,不关心类型的内部结构。对于 slice、map、channel 这种需要复杂初始化的类型,仅仅置零(例如 nil slice、nil map、nil channel)是不够的,必须调用 make 完成内部数据结构的初始化才能正常使用。var m map[string]int 或 new(map[string]int) 得到的 nil map 无法直接写入数据(会 panic),而 make(map[string]int) 得到的空 map 可以安全写入。var m1 map[string]int // nil map
m1["a"] = 1 // panic: assignment to entry in nil map
m2 := make(map[string]int)
m2["a"] = 1 // 正常
使用 new(map[string]int) 得到的是一个指向 nil map 的指针(*map[string]int 类型)。这个指针本身不是 nil,但它指向的 map 是 nil,因此无法直接读写(会 panic)。要让它变得可用,必须让该指针指向一个已初始化的 map。
如下示例:
package main
import "fmt"
func main() {
fmt.Println("Hello, 世界")
v := new(int)
fmt.Println(v, *v)
m1 := new(map[string]int)
fmt.Println("m1:", m1, "| *m1:", *m1)
fmt.Println("m1 == nil:", m1 == nil) // false(指针本身不是 nil)
fmt.Println("*m1 == nil:", *m1 == nil) // true(指向的 map 是 nil)
// `m1`是一个指向nil的map的指针,这个指针本身不是 nil,但它指向的 map 是 nil,因此无法直接读写(会 panic)。要让它变得可用,必须让该指针指向一个已初始化的 map。
// 当你写 *m1["key"] 时,编译器会先执行 m1["key"],但 m1 的类型是 *map[string]int(指向 map 的指针),而指针类型不支持索引操作,所以会编译报错:invalid operation: cannot index m1 (variable of type *map[string]int)
// m1["a"] = 1 // 写操作会引发错误:invalid operation: cannot index m1 (variable of type *map[string]int)
// fmt.Println(m1["a"]) // 读操作也会引发错误,原因同上。
// 为了让编译器先解引用指针得到 map,再对 map 进行索引,必须用括号改变运算顺序:`(*m1)["key"]`
// 类似的情况也出现在通过指针访问结构体字段时:(*p).field,但 Go 提供了语法糖 p.field,会自动解引用。可惜 map 索引没有这种语法糖,所以必须显式写括号。
// (*m1)["a"] = 1 // panic: assignment to entry in nil map
fmt.Println("未初始化m1 | (*m1)[\"a\"] = ", (*m1)["a"]) // 可以读
// 必须初始化
*m1 = make(map[string]int) // 现在 *m1 是一个空 map
(*m1)["a"] = 1 // 可以进行读写操作
fmt.Println("已初始化m1 | (*m1)[\"a\"] =", (*m1)["a"])
}
输出:
Hello, 世界
0x14000098030 0
m1: &map[] | *m1: map[]
m1 == nil: false
*m1 == nil: true
未初始化m1 | (*m1)["a"] = 0
已初始化m1 | (*m1)["a"] = 1
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。