Go语言中new与make的使用解读

 更新时间:2025年07月07日 16:49:21   作者:言之。  
这篇文章主要介绍了Go语言中new与make的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

在 Go 语言中,newmake 是两个用于内存分配的内置函数,但它们的作用和使用场景有显著区别。

理解它们的核心在于:

  • new(T): 为类型 T 分配内存,并将其初始化为零值,然后返回一个指向该内存的指针 (*T)。
  • make(T, args...): 仅用于 slice、map 和 channel 这三种引用类型的创建和初始化。它返回的是类型 T 本身(而不是指针 *T),并且这个 T 已经被正确初始化,可以直接使用。

下面详细解释:

new

作用

  • 分配内存。
  • 将分配的内存清零(设置为相应类型的零值)。
  • 返回一个指向新分配的零值 T 的指针 (*T)。

语法

p := new(T)

其中 T 可以是任何类型。

返回值

  • 一个指向类型 T 的指针 (*T)。

适用类型

  • 几乎所有类型,包括基本类型(int, string, bool等)、结构体(struct)、数组(array)等。
  • 也可以用于 slice, map, channel,但通常不这么用,因为 new 返回的是指向这些引用类型本身的指针,而引用类型本身在未初始化时是 nil

示例

// 为 int 分配内存,i 是 *int 类型,*i 的值是 0
var i *int = new(int)
fmt.Printf("i: %p, *i: %d\n", i, *i) // *i 是 0

// 为自定义结构体 MyStruct 分配内存,s 是 *MyStruct 类型
type MyStruct struct {
    Field1 int
    Field2 string
}
var s *MyStruct = new(MyStruct)
fmt.Printf("s: %p, s.Field1: %d, s.Field2: '%s'\n", s, s.Field1, s.Field2) // s.Field1 是 0, s.Field2 是 ""

// 对于引用类型(不推荐,但可以这么做)
var sl *[]int = new([]int) // sl 是 *[]int 类型,*sl 的值是 nil
fmt.Printf("sl: %p, *sl == nil: %t\n", sl, *sl == nil) // *sl 是 nil,无法直接使用 append 等操作

var m *map[string]int = new(map[string]int) // m 是 *map[string]int 类型,*m 的值是 nil
fmt.Printf("m: %p, *m == nil: %t\n", m, *m == nil) // *m 是 nil,无法直接赋值

make

作用

  • 仅用于 slice、map 和 channel 这三种内置引用类型的创建。
  • 它不仅分配内存,还会初始化这些类型的内部数据结构,使其立即可用。

语法

  • Slice: make([]T, length, capacity)make([]T, length) (capacity 默认为 length)
  • Map: make(map[K]V, initialCapacity)make(map[K]V) (initialCapacity 可选)
  • Channel: make(chan T, bufferCapacity)make(chan T) (bufferCapacity 可选,默认为0,即无缓冲)

返回值

  • 返回的是初始化后的类型 T 本身(例如 []int, map[string]int, chan int),而不是指针。

适用类型

  • 只能是 slice, map, channel。

示例

// 创建一个长度为 5,容量为 10 的 int 切片
s1 := make([]int, 5, 10)
fmt.Printf("s1: %v, len: %d, cap: %d\n", s1, len(s1), cap(s1)) // s1: [0 0 0 0 0], len: 5, cap: 10
s1[0] = 100 // 可以直接使用

// 创建一个初始容量(可选)的 map
m1 := make(map[string]int)
m1["age"] = 30 // 可以直接使用
fmt.Printf("m1: %v\n", m1)

// 创建一个带缓冲的 channel
ch1 := make(chan int, 1)
ch1 <- 1 // 可以直接使用
fmt.Printf("Received from ch1: %d\n", <-ch1)

核心区别总结

特性new(T)make(T, args...)
目的分配内存,初始化为零值创建并初始化 slice, map, channel 的内部结构
返回类型指针 *T类型 T 本身 (slice, map, or channel)
适用类型任何类型仅 slice, map, channel
初始化内存被清零 (zero value)初始化内部数据结构,使其立即可用 (non-nil)
常见用途获取指向零值变量的指针,尤其是结构体指针创建可用的 slice, map, channel

为什么make只用于 slice, map, channel?

这三种类型在 Go 中是引用类型,它们内部不仅仅是一块简单的内存区域,还包含了更复杂的数据结构:

  • Slice: 内部包含指向底层数组的指针、长度(len)和容量(cap)。make 会分配底层数组并设置这些元数据。
  • Map: 内部通常是哈希表的实现。make 会初始化这个哈希表结构。
  • Channel: 内部包含用于 goroutine 间同步和通信的机制,可能还有缓冲区。make 会设置这些。

如果对这些类型使用 new

var s *[]int = new([]int) // *s 是一个 nil slice
var m *map[string]int = new(map[string]int) // *m 是一个 nil map
var c *chan int = new(chan int) // *c 是一个 nil channel

你会得到一个指向 nil slice/map/channel 的指针。这个 nil 状态的引用类型是不能直接使用的(例如,不能向 nil map 添加键值对,不能向 nil slice append 元素,不能向 nil channel 发送数据)。你需要先用 make 来初始化它们。

何时使用哪个?

使用 new(T)

当你需要一个指向某个类型 T 的变量,并且希望它被初始化为其零值时。

最常见的场景是为结构体分配内存并获取其指针,然后填充字段:

type Point struct{ X, Y int }
p := new(Point) // p 是 *Point,p.X 和 p.Y 都是 0
p.X = 10

这等价于:

var p Point // p 是 Point,p.X 和 p.Y 都是 0
ptr := &p   // ptr 是 *Point
ptr.X = 10

或者更简洁的复合字面量:

p := &Point{} // p 是 *Point,p.X 和 p.Y 都是 0
p.X = 10

使用 make(T, args...)

  • 当你需要创建一个 slice、map 或 channel,并希望它们被正确初始化以便立即使用时。这是创建这三种类型的标准方式

记住这个简单的规则:

  • 要创建 slice, map, channel,请使用 make
  • 要为其他类型(如 int, string, struct, array)分配内存并获取指向其零值的指针,请使用 new(或者更常见的,使用复合字面量 &T{})。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • 详解Go语言如何进行Http调用

    详解Go语言如何进行Http调用

    无论是微服务还是单体架构等,服务间都有相互通信的时候,而最直接的通信方法就是 HTTP 调用,本文将会介绍在 Go 语言里,如何进行 HTTP 调用,需要的可以参考一下
    2022-12-12
  • Go语言开发k8s之Deployment操作解析

    Go语言开发k8s之Deployment操作解析

    这篇文章主要为大家介绍了Go语言开发k8s之Deployment操作解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-06-06
  • Linux系统下Go语言开发环境搭建

    Linux系统下Go语言开发环境搭建

    这篇文章主要介绍了Linux系统下Go开发环境搭建,需要的朋友可以参考下
    2022-04-04
  • Go语言常见数据结构的实现详解

    Go语言常见数据结构的实现详解

    这篇文章主要为大家学习介绍了Go语言中的常见数据结构(channal、slice和map)的实现,文中的示例代码简洁易懂,需要的可以参考一下
    2023-07-07
  • Go  iota 常量基本语法介绍

    Go  iota 常量基本语法介绍

    这篇文章主要介绍了Go 为什么要设计 iota 常量,我们介绍了 Go 中 iota 的基本语法。同时基于历史资料针对 iota 到底是什么,为什么要这么叫,又有什么用进行了一番研究,需要的朋友可以参考下
    2022-06-06
  • 这些关于Go中interface{}的注意事项你都了解吗

    这些关于Go中interface{}的注意事项你都了解吗

    这篇文章主要为大家详细介绍了学习Go语言时需要了解的interface{}注意事项,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起了解一下
    2023-03-03
  • Go中的字典Map增删改查、排序及其值类型

    Go中的字典Map增删改查、排序及其值类型

    本文详细介绍了Go语言中Map的基本概念、声明初始化、增删改查操作、反转、排序以及如何判断键是否存在等操作,Map是一种基于键值对的无序数据结构,键必须是支持相等运算符的类型,值可以是任意类型,初始化Map时推荐指定容量以提高性能
    2024-09-09
  • 使用go mod导入本地自定义包问题

    使用go mod导入本地自定义包问题

    这篇文章主要介绍了使用go mod导入本地自定义包问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-07-07
  • GO语言make()分配用法实例

    GO语言make()分配用法实例

    这篇文章主要介绍了GO语言make()分配用法,实例分析了make()的功能及使用技巧,需要的朋友可以参考下
    2015-02-02
  • golang通过gorm操作sqlite设置主键自增的步骤

    golang通过gorm操作sqlite设置主键自增的步骤

    这篇文章主要介绍了golang通过gorm操作sqlite设置主键自增的详细步骤,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-09-09

最新评论