面试题:Go 的 make 和 new 有什么区别

这题几乎是 Go 面试的开场题,但很多人只答出"一个初始化一个不初始化",没说到点子上。核心差异在于返回的类型和适用场景。 标准答案 | | new(T) | make(T, ...) | |---|---|---| | 返回类型 | *T

这题几乎是 Go 面试的开场题,但很多人只答出”一个初始化一个不初始化”,没说到点子上。核心差异在于返回的类型和适用场景。

标准答案

| | new(T) | make(T, ...) | |—|—|—| | 返回类型 | *T(指针) | T(值,不是指针) | | 适用类型 | 任意类型 | 仅 slice / map / channel | | 初始化内容 | 零值 | 按类型构造可用结构 | | 底层行为 | 分配内存并清零 | 分配内存 + 初始化内部字段 |

new 做什么

p := new(int)
fmt.Println(*p)        // 0
fmt.Println(p == nil)  // false

new(int) 分配一个 int 的内存,清零,返回地址。等价于:

var v int
p := &v

注意 new 不初始化任何东西,只保证内存可用且为零值。

make 做什么

s := make([]int, 3)     // len=3, cap=3,元素都是 0
m := make(map[string]int) // 已初始化,可以直接赋值
c := make(chan int, 10)   // 带缓冲的 channel

这三种类型的共同点是内部包含指向底层数据的指针,必须由运行时初始化后才能用:

var m map[string]int
m["a"] = 1              // panic: assignment to entry in nil map

声明一个 map 变量时,它只是个 nil 指针,make 才真正建好哈希表。

为什么 slice 用 make 而不是 new

s := new([]int)         // *[]int,指向一个 nil slice
*s = append(*s, 1)      // 能用,但极其别扭

slice 本身就是个”小结构体”(指针 + len + cap),你要的是这个结构体的值,不是它的地址。所以用 make 直接返回值。

常见追问

Q:make([]int, 3) 和 make([]int, 3, 10) 区别?

前者 cap=3,后者 cap=10。后者预先分配底层数组,append 时在 10 以内不会触发扩容复制。循环里已知元素个数时,用带 cap 的版本能省掉多次扩容。

Q:slice 是值传递还是引用传递?

slice 作为参数传递时,结构体本身是值拷贝,但底层数组是共享的。所以函数内 append 可能修改调用方看到的数据(不超 cap 时),也可能不修改(触发扩容时)。这是经典的坑。

Q:map 和 channel 为什么不能比较?

map 和 channel 内部含指针,只能和 nil 比较。map == map 直接编译报错。

一句话总结

new 给你一块清零的内存地址,make 给你一个能直接用的 slice / map / channel。回答时点出”make 仅适用于三种引用类型”和”返回指针 vs 返回值”,这题就满分了。