详解Go语言中make和new的区别

不晚
不晚
发布时间:2026-10-15| 6人浏览过
原创

在 Go 语言开发中,make 和 new 虽然都用于内存相关的初始化操作,但适用场景截然不同。make 仅适用于内置引用类型,负责初始化数据结构本身及关联的容量元数据;而 new 则面向所有类型,执行简单的零值内存分配并返回指针。本文将从语法规范、语义行为及底层实现原理三个维度,详细剖析两者的核心差异。

使用场景与语义差异

make 在 Go 语言中具有唯一性,它只能用于初始化三种内置类型:slice(切片)、map(映射)和 chan(通道)。这些类型属于引用类型,在使用前必须经过初始化过程来建立其底层结构。

slice := make([]int, 0, 100)
hash := make(map[int]bool, 10)
ch := make(chan int, 5)

调用 make 时,它会返回一个初始化好的实例。值得注意的是,这三种返回值的数据形态各不相同:

  • slice 是一个包含 data(数组指针)、cap(容量)和 len(长度)字段的结构体;
  • hash 是一个指向内部 hmap 结构体的指针;
  • ch 是一个指向内部 hchan 结构体的指针。

相比之下,new 的作用非常简单且通用。它接收任意类型作为参数,为该类型在堆上分配空间(若大小大于0),填充零值后,返回一个指向该新分配对象指针。例如:

i := new(int)
// 等价于以下写法
var v int
i := &v

上述代码通过 new(int) 创建了一个指向 int 零值(即 0)的指针。因此,new(T) 的行为等同于 &(*T)(new(T)) 或更直接的地址取用逻辑,它不涉及对数据结构内部的复杂组装。

底层实现原理

理解这两者的区别,需要从编译期和运行时的角度进行分析。Go 编译器在预处理阶段会将这些关键字转换为特定的控制流和操作码。

make 的编译处理

在编译器的类型检查阶段,代表 make 的关键字会被转化为专门的中间表示节点:OMAKESLICE、OMAKEMAP 和 OMAKECHAN。这些节点随后会触发对特定运行时函数的调用,以完成相应类型的底层结构初始化。对于 slice 而言,即便请求初始长度为 0,运行时也会为其分配一个专用的空数组以避免空指针问题,这解释了为何 make([]int, 0) 可以正常调用 append 方法。

new 的编译与堆栈分配

new 函数在 SSA(静态单赋值形式)代码生成阶段,会通过 callnew 进行处理:

func callnew(t *types.Type) *Node {
    if t.NotInHeap() {
        yyerror("%v is go:notinheap; heap allocation disallowed", t)
    }
    dowidth(t)

    // 如果大小为 0,返回 zerobase 变量以避免无效指针
    if t.Size() == 0 {
        z := newname(Runtimepkg.Lookup("zerobase"))
        z.SetClass(PEXTERN)
        z.Type = t
        return typecheck(nod(OADDR, z, nil), ctxExpr)
    }

    // 否则转换为 newobject 调用
    fn := syslook("newobject")
    fn = substArgTypes(fn, t)
    v := mkcall1(fn, types.NewPtr(t), nil, typename(t))
    v.SetNonNil(true)
    return v
}

newobject 的核心逻辑是调用 mallocgc 进行堆内存分配:

func newobject(typ *_type) unsafe.Pointer {
    return mallocgc(typ.size, typ, true)
}

虽然 new 暗示了堆分配,但在实际编译器优化中,变量是在堆上还是栈上运行取决于逃逸分析。如果变量不需要在当前作用域外生存,编译器会将其分配到栈帧中,随函数结束自动销毁;只有被判定为“逃逸”的变量才会调用 mallocgc 申请堆内存。这一机制显著提升了局部变量的访问效率。

总结

make 专为内置引用类型(slice、map、chan)设计,负责构建完整的内部数据结构及其元数据;new 则用于任何类型的零值内存分配,仅仅返回指向分配的指针。在实际编码中,除非需要显式获取某类型的指针副本,否则应优先遵循 Go 的习惯用语:初始化集合类型使用 make,普通类型直接使用 var 声明配合地址操作即可。

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。