在学习 Go 语言时,我们经常会遇到 new 和 make 关键字。两者虽然都与内存分配相关,但功能和适用场景却截然不同。本笔记旨在整理它们的使用场景及区别,以帮助更好地理解和运用。
new 的使用
概念
-
new(T)用于分配内存,返回指向类型T的指针,并将内存初始化为零值(Zero Value)。 -
例如数字类型初始化为
0,字符串类型初始化为""。
使用示例
package main
import "fmt"
func main() {
foo := new(int) // 分配内存,返回 *int 类型指针
fmt.Println(foo) // 打印内存地址
fmt.Println(*foo) // 打印零值 0
fmt.Printf("%#v", foo) // 打印变量类型和地址
}
执行结果:
0xc00001a110
0
(*int)(0xc00001a110)
new常见应用场景
初始化结构体
new 常用于初始化结构体,可以快速为结构体分配内存并将字段初始化为零值:
package main
import (
"bytes"
"fmt"
"sync"
)
type SyncedBuffer struct {
lock sync.Mutex
buffer bytes.Buffer
foo int
bar string
}
func main() {
p := new(SyncedBuffer) // 返回 *SyncedBuffer 类型指针
fmt.Println("foo:", p.foo) // 输出零值 0
fmt.Println("bar:", p.bar) // 输出零值 ""
fmt.Printf("%#v\n", p)
}
自定义初始化
new 不能直接设置自定义初始值。如果需要初始化特定值,建议使用以下两种方式:
-
直接初始化:
p := &SyncedBuffer{ foo: 100, bar: "foobar", } -
通过工厂函数:
func NewSynced(foo int, bar string) *SyncedBuffer { return &SyncedBuffer{ foo: foo, bar: bar, } } func main() { p := NewSynced(100, "foobar") fmt.Println("foo:", p.foo) fmt.Println("bar:", p.bar) }
注意:使用 new 初始化slice、map和channel
当 new 用于初始化 slice 、map 和 channel 时,返回值为 nil,无法直接使用。例如:
package main
import "fmt"
func main() {
p := new(map[string]string) // 初始化 map,返回 *map 类型指针
(*p)["foo"] = "bar" // panic: assignment to entry in nil map
}
make 的使用
概念
make用于分配和初始化 slice 、map 和 channel 。- 与
new不同,make返回的不是指针,而是类型本身。
使用示例
以下示例展示了 new 和 make 初始化 map 的区别:
package main
import "fmt"
func main() {
// 使用 new
var p *map[string]string
p = new(map[string]string)
*p = map[string]string{"bar": "foo"} // 手动初始化
(*p)["foo"] = "bar"
fmt.Println(*p) // 输出: map[bar:foo foo:bar]
// 使用 make
m := make(map[string]string) // 自动初始化
m["foo"] = "bar"
m["bar"] = "foo"
fmt.Println(m) // 输出: map[bar:foo foo:bar]
}
对比:
- 使用
new时需要手动分配具体结构(如map[string]string{})。 - 使用
make时可以直接使用初始化后的结构。
new 与 make 的区别
| 特性 | new | make |
|---|---|---|
| 作用 | 分配内存并返回指针 | 分配内存并初始化类型 |
| 返回值 | 指向类型的指针 | 类型本身 |
| 适用类型 | 所有类型 | 仅适用于切片(slice)、映射(map)、通道(channel) |
| 零值初始化 | 是 | 是 |
| 初始化结构体字段值 | 不支持 | 不适用 |
| 性能优化 | 不适用 | 支持通过设置长度和容量优化性能 |
-
使用建议
- 在初始化 slice 、map 或 channel 时,优先使用
make; - 若需要获得指针,或用于结构体初始化,考虑使用
new;但若需要自定义初始化值,建议用&T{}或工厂函数。
- 在初始化 slice 、map 或 channel 时,优先使用