首页/文章/Golang

Golang 内置函数:make()

2024-09-25
7673 分钟
...

make()Go 语言内存分配的内置函数,默认有三个参数。

make(Type, len, cap)
  • Type:数据类型,必要参数,Type 的值只能是 slicemapchannel 这三种数据类型。
  • len:数据类型实际占用的内存空间长度,mapchannel 是可选参数,slice 是必要参数。
  • cap:为数据类型提前预留的内存空间长度,可选参数。所谓的提前预留是当前为数据类型申请内存空间的时候,提前申请好额外的内存空间,这样可以避免二次分配内存带来的开销,大大提高程序的性能。

为了能更好的理解这些参数的含义,我们先来看下 make() 的三种不同用法:

make(map[string]string)

make([]int, 2)

make([]int, 2, 4)
  • 第一种,只传类型,不指定实际占用的内存空间和提前预留的内存空间,适用于 mapchannel
  • 第二种,指定实际占用的内存空间为 2,不指定提前预留的内存空间。
  • 第三种,指定实际占用的内存空间为 2,指定提前预留的内存空间是 4。

看到这里你有没有这样的疑惑,既然在初始化的时候已经指定数据的大小了,那为什么还要指定预留的大小呢?这是因为 make() 使用的是一种动态数组算法,一开始先向操作系统申请一小块内存,这个就是 cap,等 caplen 占用满以后就需要扩容,扩容就是动态数组再去向操作系统申请当前长度的两倍的内存,然后将旧数据复制到新内存空间中。

为了更好的理解动态数组向扩容,这里先写一段演示代码:

data := make([]int, 0)

for i, n := 0, 20; i < n; i++ {
        data = append(data, 1)
        fmt.Printf("len=%d cap=%d\n", len(data), cap(data))
}

运行结果:

len=1 cap=1		
len=2 cap=2		
len=3 cap=4		
len=4 cap=4
len=5 cap=8		
len=6 cap=8
len=7 cap=8
len=8 cap=8
len=9 cap=16	
len=10 cap=16
len=11 cap=16
len=12 cap=16
len=13 cap=16
len=14 cap=16
len=15 cap=16
len=16 cap=16
len=17 cap=32	
len=18 cap=32
len=19 cap=32
len=20 cap=32

结果确实是动态数组每次扩容都是原来的两倍,那么思考一下动态数组会一直这样扩容下去吗?

data := make([]int, 0)

for i, n := 0, 10000; i < n; i++ {
        data = append(data, 1)
        if len(data) == cap(data) {
                fmt.Printf("len=%d cap=%d\n", len(data), cap(data))
        }
}

运行结果:

len=1 cap=1
len=2 cap=2
len=4 cap=4
len=8 cap=8
len=16 cap=16
len=32 cap=32
len=64 cap=64
len=128 cap=128
len=256 cap=256
len=512 cap=512
len=1024 cap=1024
len=1280 cap=1280
len=1696 cap=1696
len=2304 cap=2304
len=3072 cap=3072
len=4096 cap=4096
len=5120 cap=5120
len=7168 cap=7168
len=9216 cap=9216

理论上扩容应该是呈指数增长,但我们从运行结果中可以看到,前 11 次扩容确实是每次扩展一倍的长度,不过在第 12 次扩容并没有按照预期扩展到 2048,这是为什么呢?

要回答这个问题就需要回到动态数组算法上。动态数组刚刚也说了,就是每次内存容量占满就需要扩容。但不同的程序员对扩容多少的理解是不一样的,不同的语言也有不同的算法,不过最后肯定在扩容 n 次后就不能按照倍数来扩了,这是因为有物理内存的限制,避免一次申请过多从而导致内存申请失败和内存浪费的情况。

如果您觉得这篇文章有帮助,请点个赞吧~

分享文章

相关文章

更多文章 →
Golang2026-04-10
golang文档
1\. Go 是什么 Go 是一种 强类型、垃圾回收、原生支持并发 的编程语言。代码由 package 组成;用 module 管理依赖;可执行程序通常从 package main 的 main() 函数开始运行。Go 官方一直强调兼容性,Go 1.26 也继续保持 Go 1 的兼容承诺。 最小可运行程序: 这里你已经看到了 Go 最核心的 3 个语法点: package 声明包、import 导入包、func 声明函数。main 包里...
学习
Golang2025-07-11
Go语言strconv包:字符串和数值类型的相互转换
在实际开发中我们往往需要对一些常用的数据类型进行转换,如 string、int、int64、float 等数据类型之间的转换,Go语言中的 strconv 包为我们提供了字符串和基本数据类型之间的转换功能。 strconv 包中常用的函数包括 Atoi()、Itia()、parse 系列函数、format 系列函数、append 系列函数等,下面就来分别介绍一下。 string 与 int 类型之间的转换 字符串和整型之间的转换是我们平...
学习
Golang2025-06-11
深入理解 Go 中的 new() 和 make() 函数
在 Go 语言中, 和 是两个常用的函数,用于创建和初始化不同类型的变量。本文将深入探讨 和 的区别、使用场景以及底层实现原理。 引言 Go 中的 和 函数是用于创建和初始化变量的重要工具。 用于创建指定类型的零值变量,并返回该变量的指针。 用于创建并初始化引用类型的变量,如切片、映射和通道。 函数 函数的基本语法及用法。 创建的变量是指定类型的零值,并返回该变量的指针。 适用于创建引用类型以外的其他类型变量。 函数 函数的基本语法及用...
学习
Golang2025-06-10
Ubuntu 升级 golang 版本完美步骤
一、删除旧的版本(可选) 二、下载最新版本 三、设置环境变量 1、打开profile: 2、添加以下变量: 3、是环境立即生效 4、将环境立即生效载入脚本 先打开文件这个文件: 加入这个命令: 四、查看 go 环境变量 五、GO111MOUDLE和更改GOPROXY 六、查看 go 版本 ⚙️
学习
Golang2025-06-05
Go 自动升级依赖版本
🚀 Go 自动升级依赖版本 在 Go 语言项目中,依赖管理使用 Go Modules ( 和 )。如果想要 自动升级依赖 ,可以使用以下方法。 1\. 方式 1:升级所有依赖 🔹 作用: :升级所有 Go 依赖到最新的 次要版本(minor)或补丁版本(patch) 。 :升级所有项目内的依赖。 2\. 方式 2:升级指定依赖 🔹 作用: 只升级 这个依赖到最新版本。 如果你需要升级到 最新的主要版本(major version)...
学习
Golang2025-06-02
GO项目的打包和部署
在 Go 语言中,打包和部署应用非常简单,因为 Go 编译生成的可执行文件不依赖外部环境。以下是 打包发布和部署 Go 应用 的几种常见方式,以及一些最佳实践。 1\. 基本的打包和构建 构建可执行文件 你可以通过 命令将你的应用打包成一个可执行文件: 说明: 指定输出的可执行文件名(如果省略,输出名默认为当前目录名)。 如果没有错误,当前目录会生成一个可执行文件 (Windows 系统下是 )。 运行可执行文件: 2\. 打包不同平台...
学习

评论

请登录后发表评论

去登录
加载评论中...