go new和make的区别以及为什么new返回的是指针问题分析

 更新时间:2026年01月05日 10:05:04   作者:胡萝卜的兔  
Go语言中new和make的区别在于:new用于分配值类型内存并返回指针,适用于所有值类型;make用于创建并初始化slice、map和channel,只适用于引用类型,new返回指针可以明确表示值是在堆上分配的,避免不必要的值拷贝,并且与零值初始化语义一致

Go 语言中 new 和 make 的区别

在 Go 语言中,new 和 make 都是用于内存分配的内建函数,但它们有不同的用途和行为。

new 函数

new(T) 用于为值类型分配内存并返回指针:

  • 接受一个类型 T 作为参数
  • 返回一个指向新分配的零值 T 的指针 *T
  • 适用于所有值类型(包括结构体、数组等)
p := new(int)   // p 是 *int 类型,指向一个零值的 int
fmt.Println(*p) // 输出 0

type Person struct {
    Name string
    Age  int
}

personPtr := new(Person) // 分配 Person 结构体的零值并返回指针

make 函数

make 专门用于创建 slice、map 和 channel 这三种引用类型:

  • 接受一个类型和可选的容量/长度参数
  • 返回已初始化的(非零值)T 类型(不是指针)
  • 只适用于 slice、map 和 channel
s := make([]int, 10)     // 长度为10的切片
m := make(map[string]int) // 初始化一个map
c := make(chan int, 5)   // 缓冲大小为5的通道

主要区别

当需要初始化 slice、map 或 channel 时使用 make

对于结构体,通常使用 &T{} 语法比 new 更常用

// 通常这样创建结构体实例
p := &Person{Name: "Alice", Age: 30}

// 而不是
p := new(Person)
p.Name = "Alice"
p.Age = 30

new 为什么要返回指针呢

1. 明确表示分配在堆上

返回指针可以明确表示这个值是在堆上分配的,而不是栈上。在 Go 中:

  • 栈上分配的值在函数返回后会被自动回收
  • 堆上分配的值由垃圾回收器管理
  • 通过返回指针,明确告诉使用者这个值会持续存在,不会被自动回收。

2. 避免不必要的值拷贝

如果 new 返回值而不是指针:

v := new(MyStruct) // 假设 new 返回值
funcThatTakesPointer(&v) // 需要显式取地址

这样会导致额外的值拷贝。而返回指针可以直接传递,更高效。

3. 与零值初始化语义一致

new 的语义是"分配并返回零值",如果返回值:

v := new(int) // 假设返回 0

那么就无法区分这是新分配的值还是字面量 0。返回指针 *int 则明确表示了这是一个新分配的值。

4. 与 make 区分明确

Go 语言设计者刻意区分:

  • make 用于创建并初始化 slice/map/channel(返回已初始化的值)
  • new 用于分配内存并返回指针(强调内存分配)

5. 实际使用场景的需要

大多数需要 new 的场景都是需要指针的场景:

// 常见用法
var p *TreeNode
p = new(TreeNode)

// 如果 new 返回值,就需要写成
var p TreeNode
pp := &p
// 这样反而更不直观

返回值的设计问题

如果 new 返回值:

  • 无法表示"分配新内存"的语义
  • 与结构体字面量初始化 T{} 难以区分
  • 需要指针时仍需额外取地址操作

因此,返回指针是更合理的设计选择,它:

  • 明确表达了内存分配语义
  • 避免了不必要的拷贝
  • 与指针使用场景更匹配
  • 保持了语言设计的简洁性和一致性

在 Go 中,如果你不需要指针,完全可以直接声明值类型变量或使用结构体字面量 T{},而不需要使用 new。

总结

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

相关文章

  • 一文吃透Go的内置RPC原理

    一文吃透Go的内置RPC原理

    这篇文章主要为大家详细介绍了Go语言中内置RPC的原理。说起 RPC 大家想到的一般是框架,Go 作为编程语言竟然还内置了 RPC,着实让我有些吃鲸,本文就来一起聊聊吧
    2023-03-03
  • Golang实现Java虚拟机之解析class文件详解

    Golang实现Java虚拟机之解析class文件详解

    这篇文章主要为大家详细介绍了Golang实现Java虚拟机之解析class文件的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2024-01-01
  • 详解go-zero如何使用validator进行参数校验

    详解go-zero如何使用validator进行参数校验

    这篇文章主要介绍了如何使用validator库做参数校验的一些十分实用的使用技巧,包括翻译校验错误提示信息、自定义提示信息的字段名称、自定义校验方法等,感兴趣的可以了解下
    2024-01-01
  • Go语言使用slices包轻松实现排序功能

    Go语言使用slices包轻松实现排序功能

    在 Go 语言开发中,对数据进行排序是常见的需求,Go 1.18 版本引入的 slices包提供了简洁高效的排序解决方案,支持内置类型和用户自定义类型的排序操作,本文将通过具体示例,详细介绍如何使用 slices包实现排序及相关功能,需要的朋友可以参考下
    2025-05-05
  • Golang中channel使用的一些小技巧

    Golang中channel使用的一些小技巧

    这篇文章主要介绍了Golang中channel使用的一些小技巧,本文讲解了关闭2次、读取的时候channel提前关闭了、向已经关闭的channel写数据等技巧及这实例代码,需要的朋友可以参考下
    2015-07-07
  • Golang字符串和数组的相互转换

    Golang字符串和数组的相互转换

    本文主要介绍了Golang字符串和数组的相互转换,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2025-06-06
  • 使用client-go工具调用kubernetes API接口的教程详解(v1.17版本)

    使用client-go工具调用kubernetes API接口的教程详解(v1.17版本)

    这篇文章主要介绍了使用client-go工具调kubernetes API接口(v1.17版本),本文通过图文实例相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-08-08
  • golang 自旋锁的实现

    golang 自旋锁的实现

    这篇文章主要介绍了golang 自旋锁的实现,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-11-11
  • 深入了解Go语言中的作用域和变量重声明

    深入了解Go语言中的作用域和变量重声明

    在 Go 语言中,代码块的嵌套和作用域是程序设计的关键概念之一,本文将探讨如何在 Go 语言中利用代码块的嵌套和作用域来组织代码,并介绍变量重声明的规则,感兴趣的可以了解下
    2023-11-11
  • 一文带你理解Go语言中方法的本质

    一文带你理解Go语言中方法的本质

    我们知道,Go语言从设计伊始,就不支持经典的面向对象语法元素,但 Go 语言仍保留了名为“方法(method)”的语法元素,下面我们就来带大家深入了解一下Go语言中的方法吧
    2023-11-11

最新评论