Golang中make与new及数组切片详解

世界真的很小,好像一转身,就不知道会遇见谁;世界真的很大,好像一转身,就不知道谁会消失

Posted by yishuifengxiao on 2026-03-21

makenew用法简析

在 Go 语言中,makenew 都是用于内存分配的内建函数,但它们的适用类型、返回值和初始化行为有本质区别。

makenew 的共同点

  1. 都是内建函数:无需导入任何包即可直接使用。
  2. 都在堆上分配内存:用于创建变量并返回一个(或指向)已初始化的值。
  3. 均用于初始化:但初始化的“深度”不同。

makenew 的核心区别

特性 new make
适用类型 所有类型(包括结构体、基本类型等) 仅限 slice、map、channel 三种引用类型
返回值 返回指向该类型的 指针*T 返回该类型 本身(非指针,但内部引用)
初始化行为 只将内存置零(zero value),不初始化内部结构 对 slice、map、channel 进行深层初始化,分配底层数据结构
典型用途 创建值类型的指针,或需要显式指针的场景 创建 slice、map、channel,并准备好使用

详细用法举例

new 的用法

// 基本类型
var p *int
p = new(int) // 分配 int 内存,p 指向零值 0
fmt.Println(*p) // 输出 0

// 结构体
type Person struct {
Name string
Age int
}
p2 := new(Person) // 返回 *Person,字段为零值
p2.Name = "Alice"
fmt.Println(p2) // &{Alice 0}

make 的用法

// slice:长度 5,容量 10
s := make([]int, 5, 10)
fmt.Println(len(s), cap(s)) // 5 10

// map:分配并准备好存储键值对
m := make(map[string]int)
m["key"] = 100

// channel:无缓冲通道
ch := make(chan int)

数组的声明、初始化与用法

数组(Array)

  • 长度固定,在编译时确定,属于类型的一部分([3]int[5]int 是不同类型)。
  • 值类型:赋值或传参会复制整个数组。

声明与初始化

// 声明(零值初始化)
var arr1 [3]int // [0, 0, 0]

// 定义并初始化(字面量)
arr2 := [3]int{1, 2, 3} // [1, 2, 3]

// 自动计算长度
arr3 := [...]int{4, 5, 6, 7} // 长度 4

// 索引初始化,未指定的保持零值
arr4 := [5]int{0: 10, 4: 50} // [10, 0, 0, 0, 50]

// 使用 new 得到指针
arrPtr := new([3]int) // 指向 [0,0,0]
arrPtr[1] = 20 // 语法糖,实际是 (*arrPtr)[1]

基本用法

// 访问
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

匿名用法

场景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 或切片表达式创建。

声明与初始化

// 声明 nil 切片
var s1 []int // nil,len=0, cap=0

// 使用 make(推荐)
s2 := make([]int, 5) // 长度 5,容量 5,元素零值
s3 := make([]int, 3, 10) // 长度 3,容量 10

// 字面量初始化
s4 := []int{1, 2, 3} // 长度=容量=3

// 基于数组或切片切割
arr := [5]int{1,2,3,4,5}
s5 := arr[1:3] // [2,3],长度2,容量4
s6 := s4[1:] // [2,3]

// 空切片(非 nil)
s7 := []int{}

用法详解

追加元素(自动扩容)

s := []int{1, 2}
s = append(s, 3) // [1,2,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]) // 输出 99,两者共享底层数组

获取长度与容量

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]) // 2,访问匿名字段

场景3:快速构建临时数据

例如在函数返回时直接构造切片。

func getIds() []int {
return []int{101, 102, 103} // 匿名切片返回
}

Map(映射)

Map 是无序的键值对集合,键必须可比较(如整数、字符串、结构体等),值可以是任意类型。

声明、定义与初始化

// 声明 nil map(不能直接赋值)
var m1 map[string]int // nil,len=0

// 使用 make 初始化(必须)
m2 := make(map[string]int) // 空 map,可添加元素
m2["age"] = 30

// 字面量初始化
m3 := map[string]int{
"a": 1,
"b": 2,
}

// 使用 make 指定容量(提示性能,不限制大小)
m4 := make(map[string]int, 100)

基本操作

m := map[string]int{"one": 1, "two": 2}

// 添加/修改
m["three"] = 3

// 读取
v := m["two"] // v=2
v, ok := m["four"] // ok=false,v=0(零值)

// 删除
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}) // 匿名 map

场景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"} // 匿名 map 返回
}

用法总结

匿名

“匿名”在 Go 中通常指:

  1. 匿名类型:不显式定义类型名,直接用类型字面量。例如 []int{1,2,3} 就是匿名切片类型。
  2. 匿名变量:使用下划线 _ 忽略不需要的返回值或迭代元素。
  3. 匿名字段:在结构体中直接嵌入类型,不指定字段名,从而获得字段提升。

常见使用场景

  • 临时数据传递:函数参数或返回值直接使用字面量,避免定义不必要的新类型。
  • 减少命名冲突:当类型仅用于局部且语义清晰时,用匿名类型更简洁。
  • 结构体内嵌:利用匿名字段实现类似继承的复用(组合)。
  • 忽略值:用 _ 明确表示忽略,增强代码可读性。

结合 makenew 看数组/切片

  • 数组:一般用 new 得到指针,但更常用字面量或 [size]Type{}

    arrPtr := new([3]int)   // 返回 *[3]int,指向零值数组
    arrPtr[0] = 10 // 语法糖,实际是 (*arrPtr)[0]
  • 切片:必须使用 make 来初始化内部结构(底层数组、len、cap)。

    s := make([]int, 0, 10) // 推荐,避免了 nil 切片与空切片的细微差别

    如果只用 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 或字面量创建,是实际开发中最常用的序列类型。