Skip to content

Go语言中的new和make的区别 #109

Description

@MagicalBridge

概述:

在 Go 语言中,makenew 是两个用于内存分配的内置函数,但它们的使用场景和返回值有所不同,下面我们从基本概念、使用场景、代码示例和源码层面分别介绍他们的区别。

基本概念:

new 函数

  • 作用:new 是一个分配内存的内置函数,它为类型 T 分配零值内存,并返回指向该内存的指针
  • 返回值new(T) 返回的是 *T,即指向类型 T 的指针。
  • 适用类型new 适用于所有类型,包括基本类型、结构体、数组等。
  • 示例
package main
import"fmt"typePersonstruct {
NamestringAgeint
}
funcmain() {
// 使用 new 分配一个 Person 结构体,并返回指针p:=new(Person)
fmt.Println(p) // 输出: &{ 0}fmt.Println(*p) // 输出: { 0}fmt.Println(p.Name) // 输出: ""fmt.Println(p.Age) // 输出: 0// 修改结构体的值p.Name="Alice"p.Age=30fmt.Println(*p) // 输出: {Alice 30}// 使用 new 分配一个 int 类型,并返回指针i:=new(int)
fmt.Println(i) // 输出: 0x... (指针地址)fmt.Println(*i) // 输出: 0*i=10fmt.Println(*i) // 输出: 10
}

make 函数

  • 作用:make 也是一个分配内存的内置函数,但是它仅仅用于创建特定类型的对象,如 slice、map、channel
  • 返回值make 返回的是一个特定类型的初始化后的值,而不是指针。
  • 适用类型make 仅适用于 slicemapchannel 类型。
  • 示例
package main
import"fmt"funcmain() {
// 创建一个长度为 5 的 int 切片s:=make([]int, 5)
fmt.Println(s) // 输出: [0 0 0 0 0]s[0] =10fmt.Println(s) // 输出: [10 0 0 0 0]// 创建一个容量为 10 的切片s2:=make([]int, 5, 10)
fmt.Println(s2) // 输出: [0 0 0 0 0]fmt.Println(len(s2)) // 输出: 5fmt.Println(cap(s2)) // 输出: 10// 创建一个 mapm:=make(map[string]int)
fmt.Println(m) // 输出: map[]m["a"] =1m["b"] =2fmt.Println(m) // 输出: map[a:1 b:2]// 创建一个无缓冲的 channelch:=make(chanint)
gofunc() {
ch<-1
}()
fmt.Println(<-ch) // 输出: 1// 创建一个带缓冲的 channelch2:=make(chanint, 2)
ch2<-10ch2<-20fmt.Println(<-ch2) // 输出: 10fmt.Println(<-ch2) // 输出: 20
}

使用场景:

new 的使用场景

  • 分配结构体:当你需要一个指向结构体的指针时,可以使用 new。
type Person struct {
Name string
Age int
}
p := new(Person)
  • 分配基本类型:当你需要一个指向基本类型的指针时,可以使用 new。
i := new(int)

make 的使用场景:

  • 创建切片:当你需要一个切片时,可以使用 make。
s := make([]int, 5) // 创建一个长度为 5 的 int 切片
  • 创建映射:当你需要一个映射时,可以使用 make。
m := make(map[string]int) // 创建一个 string 到 int 的映射
  • 创建通道:当你需要一个通道时,可以使用 make。
ch := make(chan int) // 创建一个 int 类型的通道

源码层面:

new 的源码实现

new 的实现非常简单,它直接调用了 runtime.newobject 函数。

// src/runtime/malloc.go
func newobject(typ *_type) unsafe.Pointer {
return mallocgc(typ.size, typ, true)
}
  • newobject 函数会根据类型的大小 typ.size 分配内存,并返回一个指向该内存的指针。
  • 分配的内存会被初始化为该类型的零值。

make 的源码实现

make 的实现稍微复杂一些,因为它需要处理不同类型的初始化。

  • 切片:make([]T, len, cap) 会调用 runtime.makeslice 函数。
// src/runtime/slice.go
func makeslice(et *_type, len, cap int) unsafe.Pointer {
// 计算需要的内存大小并分配内存
// 初始化切片结构体
}
  • 映射:make(map[K]V, hint) 会调用 runtime.makemap 函数。
// src/runtime/map.go
func makemap(t *maptype, hint int, h *hmap) *hmap {
// 初始化哈希表结构
}
  • 通道:make(chan T, size) 会调用 runtime.makechan 函数。
// src/runtime/chan.go
func makechan(t *chantype, size int) *hchan {
// 初始化通道结构
}

总结

  • new:用于分配内存并返回指向该内存的指针,适用于任何类型,但不会进行额外的初始化。
  • make:用于创建 slice、map 和 channel,不仅分配内存,还会进行初始化,返回的是值而不是指针。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions