首页 > 后端开发 > Golang > 正文

Go语言内存分配:深入解析new与make的异同与应用场景

花韻仙語
发布: 2025-08-26 18:22:01
原创
199人浏览过

Go语言内存分配:深入解析new与make的异同与应用场景

在Go语言中,new和make是两种核心的内存分配与初始化机制。new用于为任意类型分配零值内存并返回其指针,而make则专为切片、映射和通道这三种引用类型设计,用于分配并初始化其内部数据结构,返回的是已初始化的值而非指针。理解两者的差异及其适用场景,对于编写高效且符合Go惯例的代码至关重要。

Go语言中的内存分配与初始化方式

go语言提供了多种方式来分配内存和初始化值,包括:

  • 复合字面量(Composite Literals):如 Point{2, 3} 或 &Point{2, 3},后者结合了分配和初始化,并返回一个指向新分配结构的指针。
  • 局部变量地址:&someLocalVar,获取一个已存在局部变量的地址。
  • new 函数:分配零值内存并返回指针。
  • make 函数:为切片、映射、通道分配并初始化内存。

理解这些方式的细微差别,尤其是new和make,是掌握Go语言内存管理的基石。

new 函数:零值分配与指针返回

new是一个内建函数,其主要作用是为指定类型分配内存,并将这块内存清零(即初始化为该类型的零值),然后返回一个指向这块内存的指针。new函数可以应用于Go语言中的任何类型。

语法: new(Type)返回值: *Type (指向新分配的零值内存的指针)

示例:

存了个图
存了个图

视频图片解析/字幕/剪辑,视频高清保存/图片源图提取

存了个图 17
查看详情 存了个图

立即学习go语言免费学习笔记(深入)”;

package main

import "fmt"

type Point struct {
    X, Y int
}

func main() {
    // 1. 分配一个整型并获取其指针
    ptrInt := new(int)
    fmt.Printf("new(int) -> 类型: %T, 值: %v, 指向的值: %v\n", ptrInt, ptrInt, *ptrInt) // 类型: *int, 值: 0xc000018080, 指向的值: 0

    // 2. 分配一个Point结构体并获取其指针
    ptrPoint := new(Point)
    fmt.Printf("new(Point) -> 类型: %T, 值: %v, 指向的值: %v\n", ptrPoint, ptrPoint, *ptrPoint) // 类型: *main.Point, 值: 0xc000018088, 指向的值: {0 0}

    // 3. 对比使用复合字面量获取指针
    // &Point{} 同样分配并返回一个指向Point零值的指针
    // &Point{2, 3} 分配并初始化,然后返回指针
    initializedPointPtr := &Point{2, 3}
    fmt.Printf("&Point{2, 3} -> 类型: %T, 值: %v, 指向的值: %v\n", initializedPointPtr, initializedPointPtr, *initializedPointPtr) // 类型: *main.Point, 值: 0xc000018090, 指向的值: {2 3}

    // 注意:&int 是非法的,不能直接获取类型字面量的地址
    // var i int; &i 是合法的,但不如 new(int) 简洁
    var i int
    ptrI := &i
    fmt.Printf("var i int; &i -> 类型: %T, 值: %v, 指向的值: %v\n", ptrI, ptrI, *ptrI) // 类型: *int, 值: 0xc000018098, 指向的值: 0
}
登录后复制

从示例中可以看出,new为所有类型分配内存并将其初始化为零值,然后返回一个指向该零值的指针。

make 函数:特定类型初始化与值返回

make也是一个内建函数,但它与new有着本质的区别。make仅用于创建并初始化切片(slice)、映射(map)和通道(channel)这三种引用类型。它不仅分配内存,还会初始化这些类型内部的数据结构,使其可以立即使用。make返回的是这些类型的值,而不是指针。

语法:

  • make([]Type, length, capacity) (切片)
  • make(map[KeyType]ValueType) (映射)
  • make(chan Type, capacity) (通道) 返回值: Type (已初始化的切片、映射或通道值)

示例:

立即学习go语言免费学习笔记(深入)”;

package main

import "fmt"

func main() {
    // 1. 使用 make 创建一个切片
    // make([]int, 5) 创建一个长度为5,容量为5的int切片,元素初始化为零值
    slice := make([]int, 5)
    fmt.Printf("make([]int, 5) -> 类型: %T, 值: %v, 长度: %d, 容量: %d\n", slice, slice, len(slice), cap(slice)) // 类型: []int, 值: [0 0 0 0 0], 长度: 5, 容量: 5

    // 2. 使用 make 创建一个映射
    // make(map[string]int) 创建一个空的map
    myMap := make(map[string]int)
    myMap["apple"] = 1
    fmt.Printf("make(map[string]int) -> 类型: %T, 值: %v, 长度: %d\n", myMap, myMap, len(myMap)) // 类型: map[string]int, 值: map[apple:1], 长度: 1

    // 3. 使用 make 创建一个通道
    // make(chan int) 创建一个无缓冲的int通道
    // make(chan int, 3) 创建一个带3个缓冲的int通道
    ch := make(chan int, 2)
    fmt.Printf("make(chan int, 2) -> 类型: %T, 值: %v\n", ch, ch) // 类型: chan int, 值: 0xc0000180c0

    // 错误示例:make 不能用于非切片、映射、通道类型
    // make(Point) // 编译错误: cannot make type Point
    // make(int)   // 编译错误: cannot make type int
}
登录后复制

make函数返回的是一个已经初始化好的值,可以直接使用。它为这些引用类型分配了底层的存储空间,并设置了必要的内部结构(如切片的长度、容量,映射的哈希表,通道的缓冲区等)。

new 与 make 的核心区别与设计哲学

通过上述分析,我们可以总结出new和make的关键差异:

特性 new(Type) make(Type, ...)
用途 为任意类型分配零值内存 仅用于切片、映射、通道的分配与初始化
返回值 返回指向零值内存的指针 (*Type) 返回已初始化的值 (Type)
功能 仅分配内存并清零 分配内存并初始化内部数据结构,使其可立即使用
适用类型 所有类型(基本类型、结构体、数组、指针等) 仅限切片([]T)、映射(map[K]V)、通道(chan T)

为什么Go语言要设计两个不同的函数?

最初,Go语言的开发者也考虑过将new和make合并成一个单一的内建函数。然而,最终决定保持它们的分离,主要出于以下考量:

  1. 语义清晰性
    • new的语义是“分配一块内存并清零,然后给我它的地址”。这对于所有类型都是一致的。
    • make的语义是“创建一个可用的切片/映射/通道,并返回这个值”。这包含了更复杂的初始化逻辑,不仅仅是简单的内存分配。
  2. 操作的本质不同
    • new(T)返回的是*T,它是一个指针。
    • make(T, args)返回的是T,它是一个值。例如,make([]int, 10)返回的是一个切片值,而不是指向切片的指针。
    • 如果只有一个函数,例如ALLOC(Type, ...),那么用户需要记住何时需要加*来获取指针,何时直接返回类型值。
      // 假设只有一个ALLOC函数
      p := ALLOC(*chan int)   // 需要 * 才能得到 *chan int
      c := ALLOC(chan int)    // 直接得到 chan int
      s := ALLOC([]int, 10)   // 直接得到 []int
      p_int := ALLOC(*int)    // 需要 * 才能得到 *int
      登录后复制

      这种方式容易让新手混淆,何时需要*,何时不需要。

  3. 避免混淆:两个函数的设计,使得开发者能够根据所需创建的类型和期望的返回结果(指针或值)直观地选择正确的函数,从而降低了学习曲线和出错的可能性。new用于分配“零值”的内存,而make用于“构建”一个可用的引用类型实例。

总结与实践建议

  • 当你需要为任何类型分配内存,并希望获得一个指向该类型零值的指针时,请使用 new。 例如,new(int)、new(MyStruct)。
  • 当你需要创建并初始化切片、映射或通道这三种引用类型时,请使用 make。 make会为你设置好这些类型所需的内部结构,使其立即可用。例如,make([]int, 10)、make(map[string]string)、make(chan bool)。
  • 对于结构体,如果想同时分配内存并初始化字段,通常使用复合字面量 &MyStruct{Field: value} 更为简洁和常见。 它既分配了内存,又进行了初始化,并返回一个指针。

理解new和make的职责分离是Go语言设计哲学的一部分,它强调了明确性和简洁性,帮助开发者更好地管理内存和编写清晰的代码。

以上就是Go语言内存分配:深入解析new与make的异同与应用场景的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号