make 与 new用法简析
在 Go 语言中,make 和 new 都是用于内存分配的内建函数,但它们的适用类型、返回值和初始化行为有本质区别。
make 与 new 的共同点
- 都是内建函数:无需导入任何包即可直接使用。
- 都在堆上分配内存:用于创建变量并返回一个(或指向)已初始化的值。
- 均用于初始化:但初始化的“深度”不同。
make 与 new 的核心区别
| 特性 |
new |
make |
| 适用类型 |
所有类型(包括结构体、基本类型等) |
仅限 slice、map、channel 三种引用类型 |
| 返回值 |
返回指向该类型的 指针(*T) |
返回该类型 本身(非指针,但内部引用) |
| 初始化行为 |
只将内存置零(zero value),不初始化内部结构 |
对 slice、map、channel 进行深层初始化,分配底层数据结构 |
| 典型用途 |
创建值类型的指针,或需要显式指针的场景 |
创建 slice、map、channel,并准备好使用 |
详细用法举例
new 的用法
var p *int p = new(int) fmt.Println(*p)
type Person struct { Name string Age int } p2 := new(Person) p2.Name = "Alice" fmt.Println(p2)
|
make 的用法
s := make([]int, 5, 10) fmt.Println(len(s), cap(s))
m := make(map[string]int) m["key"] = 100
ch := make(chan int)
|
数组的声明、初始化与用法
数组(Array)
- 长度固定,在编译时确定,属于类型的一部分(
[3]int 和 [5]int 是不同类型)。
- 值类型:赋值或传参会复制整个数组。
声明与初始化
var arr1 [3]int
arr2 := [3]int{1, 2, 3}
arr3 := [...]int{4, 5, 6, 7}
arr4 := [5]int{0: 10, 4: 50}
arrPtr := new([3]int) arrPtr[1] = 20
|
基本用法
arr2[0] = 100
for i, v := range arr2 { fmt.Println(i, v) }
func modify(arr [3]int) { arr[0] = 999 } modify(arr2) fmt.Println(arr2[0])
|
匿名用法
场景1:匿名数组作为临时数据
直接使用数组字面量而不赋值给变量,常用于传递或返回。
func sum(arr [3]int) int { ... } total := sum([3]int{10, 20, 30})
type Bundle struct { [3]int Name string } b := Bundle{[3]int{1,2,3}, "test"} fmt.Println(b[0])
|
忽略元素(匿名变量 _)遍历
在遍历时使用 _ 忽略索引或值。
arr := [3]int{1,2,3} for _, v := range arr { fmt.Println(v) }
|
切片(Slice)的声明、初始化与用法
- 长度可变,是对底层数组的一个连续片段的引用。
- 引用类型:本身是一个结构体(指向底层数组的指针、长度、容量)。
- 使用
make 或切片表达式创建。
声明与初始化
var s1 []int
s2 := make([]int, 5) s3 := make([]int, 3, 10)
s4 := []int{1, 2, 3}
arr := [5]int{1,2,3,4,5} s5 := arr[1:3] s6 := s4[1:]
s7 := []int{}
|
用法详解
追加元素(自动扩容)
s := []int{1, 2} s = append(s, 3) s = append(s, 4, 5, 6) s = append(s, []int{7,8}...)
|
复制切片
src := []int{1,2,3} dst := make([]int, len(src)) copy(dst, src)
|
切片是引用类型
a := []int{1,2,3} b := a b[0] = 99 fmt.Println(a[0])
|
获取长度与容量
fmt.Println(len(s), cap(s))
|
遍历
for i, v := range s { fmt.Println(i, v) }
|
匿名用法
场景1:匿名切片作为函数参数
直接传递切片字面量。
func printSlice(s []int) { fmt.Println(s) } printSlice([]int{10, 20, 30})
|
场景2:嵌套匿名切片
在结构体中直接使用匿名切片字段。
type Matrix struct { [][]int Rows, Cols int } m := Matrix{[][]int{{1,2},{3,4}}, 2, 2} fmt.Println(m[0][1])
|
场景3:快速构建临时数据
例如在函数返回时直接构造切片。
func getIds() []int { return []int{101, 102, 103} }
|
Map(映射)
Map 是无序的键值对集合,键必须可比较(如整数、字符串、结构体等),值可以是任意类型。
声明、定义与初始化
var m1 map[string]int
m2 := make(map[string]int) m2["age"] = 30
m3 := map[string]int{ "a": 1, "b": 2, }
m4 := make(map[string]int, 100)
|
基本操作
m := map[string]int{"one": 1, "two": 2}
m["three"] = 3
v := m["two"] v, ok := m["four"]
delete(m, "one")
for k, v := range m { fmt.Println(k, v) }
len(m)
|
匿名用法
场景1:匿名 map 作为函数参数
直接传入 map 字面量。
func printMap(m map[string]int) { for k, v := range m { fmt.Println(k, v) } } printMap(map[string]int{"x": 10, "y": 20})
|
场景2:匿名 map 作为结构体字段
常用于配置、上下文等。
type Config struct { map[string]string Version string } cfg := Config{map[string]string{"host": "localhost"}, "v1.0"} fmt.Println(cfg["host"])
|
场景3:一次性使用的临时 map
例如在排序或统计时临时创建。
func countWords(text []string) map[string]int { freq := make(map[string]int) return freq }
func getDefaultConfig() map[string]string { return map[string]string{"timeout": "30s"} }
|
用法总结
匿名
“匿名”在 Go 中通常指:
- 匿名类型:不显式定义类型名,直接用类型字面量。例如
[]int{1,2,3} 就是匿名切片类型。
- 匿名变量:使用下划线
_ 忽略不需要的返回值或迭代元素。
- 匿名字段:在结构体中直接嵌入类型,不指定字段名,从而获得字段提升。
常见使用场景
- 临时数据传递:函数参数或返回值直接使用字面量,避免定义不必要的新类型。
- 减少命名冲突:当类型仅用于局部且语义清晰时,用匿名类型更简洁。
- 结构体内嵌:利用匿名字段实现类似继承的复用(组合)。
- 忽略值:用
_ 明确表示忽略,增强代码可读性。
结合 make 与 new 看数组/切片
数组:一般用 new 得到指针,但更常用字面量或 [size]Type{}。
arrPtr := new([3]int) arrPtr[0] = 10
|
切片:必须使用 make 来初始化内部结构(底层数组、len、cap)。
如果只用 new([]int),得到的是指向 nil 切片的指针,无法直接使用 append(会 panic 或产生未定义行为)。
对比与选择建议
| 类型 |
适用场景 |
声明方式 |
初始化要求 |
| 数组 |
固定长度,值语义,极少变长 |
[n]T 或 [...]T |
长度固定,编译时确定 |
| 切片 |
动态数组,最常用 |
[]T |
必须初始化底层数组(make、字面量、切割) |
| Map |
键值对,快速查找 |
map[K]V |
必须初始化(make 或字面量) |
提示:
- 对切片和 map,直接使用
var 声明的变量是 nil,不能直接添加元素,必须先用 make 或字面量分配内存。
- 匿名用法在 Go 中非常普遍,善用字面量可以简化代码,但注意不要过度使用导致可读性下降。
通过以上示例,希望你能熟练掌握数组、切片和 map 的各种用法,并在实际开发中灵活运用匿名方式提升编码效率。
new和make总结
new:分配内存,返回指针,内容为零值,适用于所有类型。
make:仅用于 slice、map、channel,返回已初始化的可直接使用的值,内部维护了引用结构。
- 数组:定长、值类型,直接存储元素。
- 切片:变长、引用类型,通过
make 或字面量创建,是实际开发中最常用的序列类型。