切片底层数组

news/2025/2/27 11:03:09/文章来源:https://www.cnblogs.com/maidongdong/p/18740539

如何理解下面的代码,数据被追加到原底层数组。如超出cap限制,则为新切片对象重新分配数组。
s := make([]int, 0, 10)
s1 := s[:2:4]
s2 := append(s1, 1)
s2 = append(s2, 1)
s2 = append(s2, 1) // 数据被追加到原底层数组。如超出cap限制,则为新切片对象重新分配数组。
fmt.Println(s)
fmt.Println(s1, len(s1), cap(s1))

fmt.Printf("s1: %p: %v\n", &s1[0], s1)
fmt.Printf("s2: %p: %v\n", &s2[0], s2)s2[0] = 1
fmt.Printf("s1: %p: %v\n", &s1[0], s1)
fmt.Printf("s2: %p: %v\n", &s2[0], s2)s1[0] = 2
fmt.Printf("s1: %p: %v\n", &s1[0], s1)
fmt.Printf("s2: %p: %v\n", &s2[0], s2)

我来一步步帮你分析这段代码,解释切片的行为、底层数组的共享以及 append 的机制。代码中有一些关键点需要理解,比如切片的长度、容量、底层数组的共享,以及超出容量时的重新分配。

初始代码和含义

s := make([]int, 0, 10)  // 创建一个长度为 0、容量为 10 的切片
s1 := s[:2:4]            // 通过切片操作生成新切片,长度为 2,容量为 4
  • s:长度 0,容量 10,底层数组有 10 个元素的空间,初始值为 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],但 s 看不到任何元素,因为长度是 0。
  • s1:通过 s[start:end:cap] 操作生成。
    • start = 0:从底层数组的第 0 个元素开始。
    • end = 2:长度为 2(end - start),所以 s1[0, 0]
    • cap = 4:容量为 4(cap - start),从第 0 个元素到第 4 个元素(不含第 4 个)。
    • 底层数组仍然是 s 的底层数组,只是 s1 的视图是前 4 个元素。

此时:

  • s[](长度 0,容量 10)
  • s1[0, 0](长度 2,容量 4)
  • 两者共享同一个底层数组。

append 操作

s2 := append(s1, 1)      // 在 s1 后追加 1
s2 = append(s2, 1)       // 在 s2 后追加 1
s2 = append(s2, 1)       // 在 s2 后追加 1
  • append 的逻辑
    • append 会将新元素追加到切片的长度(len)之后。
    • 如果追加后总长度不超过容量(cap),数据会直接写到共享的底层数组中。
    • 如果超过容量,会分配一个新的底层数组,复制原有数据,再追加新元素。

第一次 append

  • s1[0, 0],长度 2,容量 4。
  • append(s1, 1):在长度 2 的位置写入 1。
    • 新长度 = 3,小于容量 4。
    • 数据写入底层数组,变成 [0, 0, 1, 0, ...]
  • s2 = [0, 0, 1](长度 3,容量 4),与 s1 共享底层数组。

第二次 append

  • s2[0, 0, 1],长度 3,容量 4。
  • append(s2, 1):在长度 3 的位置写入 1。
    • 新长度 = 4,等于容量 4。
    • 数据写入底层数组,变成 [0, 0, 1, 1, ...]
  • s2 = [0, 0, 1, 1](长度 4,容量 4),仍与 s1 共享底层数组。

第三次 append

  • s2[0, 0, 1, 1],长度 4,容量 4。
  • append(s2, 1):试图在长度 4 的位置写入 1。
    • 新长度 = 5,大于容量 4。
    • 容量不足,Go 会重新分配一个新的底层数组(通常容量翻倍,比如 8),复制原有数据 [0, 0, 1, 1],再追加 1。
  • s2 = [0, 0, 1, 1, 1](长度 5,容量 8 或更大),此时 s2 有自己的新底层数组,不再与 s1s 共享。

输出分析

fmt.Println(s)                   // 输出: []
fmt.Println(s1, len(s1), cap(s1)) // 输出: [0 0] 2 4
  • s:长度仍为 0,所以是 [],它看不到底层数组的变化。
  • s1:长度 2,容量 4,内容是 [0, 0],因为 append 操作影响的是 s2,而 s1 的长度没变,只显示前两个元素。但底层数组已被 s2 的前两次 append 修改为 [0, 0, 1, 1, ...]
fmt.Printf("s1: %p: %v\n", &s1[0], s1) // 输出: s1: 0x...: [0 0]
fmt.Printf("s2: %p: %v\n", &s2[0], s2) // 输出: s2: 0x... (不同地址): [0 0 1 1 1]
  • &s1[0]:底层数组的起始地址。
  • &s2[0]:第三次 append 后,s2 的底层数组是新分配的,地址与 s1 不同。

修改操作

s2[0] = 1
fmt.Printf("s1: %p: %v\n", &s1[0], s1) // 输出: s1: 0x...: [0 0]
fmt.Printf("s2: %p: %v\n", &s2[0], s2) // 输出: s2: 0x...: [1 0 1 1 1]
  • s2[0] = 1:修改 s2 的第一个元素。因为 s2 已有独立底层数组,修改不会影响 s1
s1[0] = 2
fmt.Printf("s1: %p: %v\n", &s1[0], s1) // 输出: s1: 0x...: [2 0]
fmt.Printf("s2: %p: %v\n", &s2[0], s2) // 输出: s2: 0x...: [1 0 1 1 1]
  • s1[0] = 2:修改 s1 的第一个元素,影响的是原始底层数组,但不影响 s2,因为 s2 已独立。

总结理解

  1. 底层数组共享
    • ss1s2 最初共享同一个底层数组。
    • 前两次 append 在容量范围内,直接修改共享的底层数组。
  2. 超出容量
    • 第三次 append 超出 s2 的容量 4,触发新数组分配,s2s1 分离。
  3. 数据的可见性
    • 切片的长度决定可见元素,s 长度 0 看不到变化,s1 长度 2 只看到前两个。
  4. 地址变化
    • 新数组分配后,s2 的底层数组地址与 s1 不同,修改互不影响。

这段代码很好地展示了 Go 切片的动态性和底层数组的管理机制。希望这个分析帮你彻底理解了代码的运行逻辑!有什么疑问可以继续问我。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/890537.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第2章 磁盘与文件管理

磁盘管理 windows和Linux磁盘管理的区别 windows资源管理方式系统一般安装在C盘 C盘下的"Windows"目录是操作系统的核心 C盘下的"Program Files"目录下安装软件 C盘下的"用户"目录是所有的用户,包括超级管理员也在其中 windows操作系统分为C盘、…

第3章 系统命令

系统当前时间 date命令:切换用户 su 用户名sudo 命令:表示使用超级管理员身份执行该命令,如果你当前不是管理员,希望以管理员身份执行某个命令时,使用sudo,需要输入超级管理员的密码: ​​ echo命令 输出字符串 echo "Hello, world!"这将会输出 Hello, world!…

https://avoid.overfit.cn/post/bad10ed894bd43c086e3ef9de7478bea

特征选择作为机器学习工作流程中的关键环节,对模型性能具有决定性影响。Featurewiz是一个功能强大的特征选择库,具备以下核心能力:高度自动化的特征选择,仅需少量代码即可完成。 全面的特征工程功能,不仅能够选择特征,还能生成数百个衍生特征并自动筛选最优特征组合。 实…

Redis复习-五种数据类型

String String是Redis中最常见的数据存储类型: 1.其基本编码方式是RAW,基于简单动态字符串(SDS)实现,存储上限为512mb。 2.如果存储的SDS长度小于44字节,则会采用EMBSTR编码,此时object head与SDS是一段连续空间。申请内存时只需要调用一次内存分配函数,效率更高。 3.如…

cfWGBS揭示与年龄和肌萎缩侧索硬化相关cfDNA甲基化变化及组织/细胞溯源

大家好,这里是专注表观组学十余年,领跑多组学科研服务的易基因。 游离细胞DNA (Cell-free DNA,cfDNA)是血浆中游离的DNA片段,通常来源于正常细胞更新或病理状态下的细胞死亡。cfDNA已被广泛应用于癌症早期检测、胎儿遗传病诊断、器官移植评估等领域。然而,cfDNA在神经退行…

低代码加速智能制造,兰之天的选择是 NocoBase

兰之天借力 NocoBase,破解中小制造企业数字化转型困局,实现智能制造系统开发周期从数月压缩至数周。智能制造的挑战:数字化转型的必然趋势 在全球制造业加速迈向数字化、智能化的背景下,智能制造已成为提升企业竞争力的关键战略。根据财富商业洞察(Fortune Business Insig…

纷享销客CRM全面评测:纷享销客比销售易差异化对比

企业数字化转型热潮中,CRM是众多企业迈向数字化管理的里程碑。近年来,国产CRM在政策推动下成为大中型企业的首选,也有很多企业选择国产CRM替代国外供应商。国产CRM第一梯队中,纷享销客以其卓越的表现脱颖而出,稳坐头把交椅。IDC发布了最新数据报告《IDC China Semiannual …

ABB机器人平衡缸维修

在现代工业生产中,工业机器人扮演着至关重要的角色。其中,ABB机器人以其高精度、高可靠性而被广泛应用。然而,如同所有机械设备一样,ABB机器人也会出现故障,这就需要专业的维修。一、ABB机器人故障与平衡缸维修的重要性ABB机器人故障的出现会严重影响生产进程。机器人平衡…

python打包工具-Nuitka

nuitka将python源码转成C++(这里得到的是二进制的pyd文件,防止了反编译),然后再编译成可执行文件。提高安全性和运行速度。 github:https://github.com/2267770481/cython_test 安装 pip install nuitka pip install ordered-set # 加速编译 pip install zstandard # onef…

Javaweb中Vue指令的详细解析与应用

在现代Web开发中,Vue.js已经成为了一个非常流行且强大的前端框架,尤其是在JavaWeb项目中,它通过简化DOM操作,提高响应式交互的能力,大大加快了开发速度和提高了用户体验。Vue的核心之一是其指令系统,通过一系列预定义或自定义的指令,开发者可以更加方便地控制页面渲染和…

Svelte 最新中文文档教程(21)—— 自定义元素

前言 Svelte,一个语法简洁、入门容易,面向未来的前端框架。 从 Svelte 诞生之初,就备受开发者的喜爱,根据统计,从 2019 年到 2024 年,连续 6 年一直是开发者最感兴趣的前端框架 No.1:Svelte 以其独特的编译时优化机制著称,具有轻量级、高性能、易上手等特性,非常适合构…

AXI总线学习

AXI 总线概述 AXI协议是一种高性能、高带宽、低延迟的片内总线,具有如下特点: 1、总线的地址/控制和数据通道是分离的; 2、支持不对齐的数据传输; 3、支持突发传输,突发传输过程中只需要首地址; 4、具有分离的读/写数据通道; 5、支持显著传输访问和乱序访问; 6、更加容…