go切片截取细节分析

 切片截取,有没有很迷?

目录

典型截取(两参数、三参数)及分析

迷之append参与截取及细节分析

关于截取时的初始索引是否从第一个位置开始的影响

修改原切片细节分析


典型截取(两参数、三参数)及分析

先看一个例子来表示一下切片的基础截取操作(仅截取,无append):

    a := []int{1, 2}a0 := a[0:1]fmt.Printf("a0=%v, a0=%p, cap=%v\n", a0, a0, cap(a0)) // a0=[1], a0=0xc00009e070, cap=2fmt.Printf("a=%v, a=%p, cap=%v\n", a, a, cap(a))      // a=[1 2], a=0xc00009e070, cap=2b := a[0:1:1]fmt.Printf("a=%v, a=%p, cap=%v\n", a, a, cap(a))fmt.Printf("b=%v, b=%p, cap=%v\n", b, b, cap(b))    // b=[1], b=0xc00009e070, cap=1c := a[1:1]fmt.Printf("a=%v, a=%p, cap=%v\n", a, a, cap(a))fmt.Printf("c=%v, c=%p, cap=%v\n", c, c, cap(c))    // c=[], c=0xc00009e078, cap=1d := a[0:2:2]fmt.Printf("d=%v, d=%p, cap=%v\n", d, d, cap(d))e := a[1:2:2]fmt.Printf("e=%v, e=%p, cap=%v\n", e, e, cap(e))f := a[0:1]fmt.Printf("f=%v, f=%p, cap=%v\n", f, f, cap(f))fmt.Println(reflect.DeepEqual(f, b))     // trueg := a[0:2]fmt.Printf("g=%v, g=%p, cap=%v\n", g, g, cap(g))    // g=[1 2], g=0xc00009e070, cap=2fmt.Println(reflect.DeepEqual(d, g))     // true fmt.Println(reflect.DeepEqual(a, b))     // falsefmt.Println(reflect.DeepEqual(a, d))     // true

结果:

a0=[1], a0=0xc00009e070, cap=2
a=[1 2], a=0xc00009e070, cap=2
a=[1 2], a=0xc00009e070, cap=2
b=[1], b=0xc00009e070, cap=1
a=[1 2], a=0xc00009e070, cap=2
c=[], c=0xc00009e078, cap=1
d=[1 2], d=0xc00009e070, cap=2
e=[2], e=0xc00009e078, cap=1
f=[1], f=0xc00009e070, cap=2
true
g=[1 2], g=0xc00009e070, cap=2
true
false
true

以上分析中包含了海量信息,能总结出方便理解的规律、能看透才是王道。 

从上面结果可以得到以下信息:
1,c和e的地址和其它的不同;
2,b虽然是一个新搞出来的切片,但b和a都是相同的地址,a底层的数组b也在用,同时b是从a切片第一个位置开始(首地址);
3,c截取的也是a的元素组成的切片,但c是从第2个位置开始的,c和a的地址不同;
4,d截取的也是a的元素组成的切片,且d是从数组第1个位置开始的,d和a的地址相同;
5,e截取的也是a的元素组成的切片,是从数组第二个位置开始的,e和a的地址不同,e和c的地址相同;
6,d的容量之所以为2,是因为截取时指定了截取的第三个参数为2,起始索引为0,则2-0=2,以此类推e的容量必然是1,因为2-1=1;
7,d := a[0:2:2]这个操作包含了a的所有元素,是从首地址开始,同时长度和容量都和a相同,因此DeepEqual的结果为true;
8,f和b容量不同,f和b的区别是截取的第三个参数一个指定了另一个是默认,b是1,f是2,那么容量不同为啥还能深度相等?
因为对于切片,类型相同、相同索引处元素相等,则深度相等;
9,对于a0 := a[0:1]这样的仅截取操作,根本不会影响a切片,但截取操作在append中就可能有影响了。

这个代码例子虽然看起来简单没什么复杂操作,但却包罗万象不乏细节,下面是结论:

1,截取之后得到的切片是不是和原切片指向的地址相同,主要是看截取的首地址是否是从原切片的第一个元素开始,因为都是基于a来截取,底层数组一直在那放着,就看不同的人要截取多少(即东西是同一份东西,不同的人看到的视图不一样而已);
2,以a[d:e]为例,是指基于a切片,从其索引为d的位置开始、索引为e的位置(不包括)结束所获得的切片,所得切片的容量是a切片的原有容量
3,以a[d:e:f]为例,是指基于a切片,从其索引为d的位置开始、索引为e的位置(不包括)结束所获得的切片,所得切片的容量是f-d。

基于纯截取而没有其它操作的情形下,原切片不会被修改,基于以上案例和分析,我们可以得到切片截取时的地址、容量等变化。

迷之append参与截取及细节分析

下面是四个典型案例,需要仔细观察。

函数1:

func cut95() {fmt.Println("cut95 start")a := []int{1, 2}b := append(a[0:1], 3)//fmt.Println(a[1:2]) // [3]fmt.Printf("a= %v, %v, %p\n", a, cap(a), a) // a= [1 3], 2, 0xc00001a140fmt.Printf("b= %v, %v, %p\n", b, cap(b), b) // b= [1 3], 2, 0xc00001a140c := append(a[1:2], 4)fmt.Printf("a= %v, %v, %p\n", a, cap(a), a) // a= [1 3], 2, 0xc00001a140fmt.Printf("b= %v, %v, %p\n", b, cap(b), b) // b= [1 3], 2, 0xc00001a140fmt.Printf("c= %v, %v, %p\n", c, cap(c), c) // c= [3 4], 2, 0xc00001a170d := append(a[1:2], 4, 5, 6)                // 有人说扩容了就会怎么怎么样,来让它扩容一下fmt.Printf("a= %v, %v, %p\n", a, cap(a), a) // [1 3], 2, 0xc00001a140fmt.Printf("b= %v, %v, %p\n", b, cap(b), b) // [1 3], 2, 0xc00001a140fmt.Printf("c= %v, %v, %p\n", c, cap(c), c) // [3 4], 2, 0xc00001a170fmt.Printf("d= %v, %v, %p\n", d, cap(d), d) // [3 4 5 6], 4, 0xc0000121c0/*第1处截取操作:会导致a被修改第2处截取操作:a没有被修改第3处截取操作:a没有被修改注意除过第三次截取外其余截取之后的新切片均未发生扩容,和扩容无关;第三次截取扩容后地址发生了变化这是肯定了,因为容量需要扩充,原数组已不能满足要求、数组定长不能扩容,此时需要申请新数组(指针已变),再将数据拷贝到新切片;发生了扩容,但依旧原切片a没有变化。*/
}

函数2:

func cut96() {fmt.Println("cut96 start")a := []int{1, 2}b := append(a[1:2], 3)//fmt.Println(a[1:2]) // [3]fmt.Println("a= ", a) // [1 2]fmt.Println("b= ", b) // [2 3]c := append(a[1:2], 4)fmt.Println("a= ", a) // [1 2]fmt.Println("b= ", b) // [2 3]fmt.Println("c= ", c) // [2 4]d := append(a[0:1], 5)fmt.Println("a= ", a) // [1 5]fmt.Println("b= ", b) // [2 3]fmt.Println("c= ", c) // [2 4]fmt.Println("d= ", d) // [1 5]/*第1处截取操作:a没有被修改第2处截取操作:a没有被修改第3处截取操作:会导致a被修改,并且a原本是1,2 其中2也没有了,截取的1和拼接的5组成了新的a*/
}

 函数3:

  fmt.Println("cut97 start")a := []int{1, 2}b := append(a[0:1:1], 3)//fmt.Println(a[1:2]) // [2]fmt.Println("a= ", a) // [1 2]fmt.Println("b= ", b) // [1 3]c := append(a[1:2:2], 4)fmt.Println("a= ", a) // [1 2]fmt.Println("b= ", b) // [1 3]fmt.Println("c= ", c) // [2 4]/*第1处截取操作:a没有被修改第2处截取操作:a没有被修改*/

函数4:

func cut98() {fmt.Println("cut98 start")a := []int{1, 2}b := append(a[0:1:2], 3) // 这里如果改为a[0:1:1] a不会被修改//fmt.Println(a[1:2]) // [2]fmt.Println("a= ", a) // [1 3]fmt.Println("b= ", b) // [1 3]c := append(a[1:2], 4)fmt.Println("a= ", a) // [1 3]fmt.Println("b= ", b) // [1 3]fmt.Println("c= ", c) // [3 4]d := append(a[0:2], 5)fmt.Println("a= ", a) // [1 3]fmt.Println("b= ", b) // [1 3]fmt.Println("c= ", c) // [3 4]fmt.Println("d= ", d) // [1 3 5]/*第1处截取操作:a被修改第2处截取操作:a没有被修改第3处截取操作:a没有被修改*/
}

现象不细说了,一看代码便知,这里汇总一下结论:

给原切片做截取操作同时套append时,原切片会不会被跟着被修改(注意这里讨论的是还有append的情况下,并不是单纯截取),按下面步骤。

首先看截取后的切片是否和原切片相等(如长度为2容量也为2),如果相等则当然不变,如:
a := []int{1, 2}
d := append(a[0:2], 5)
上面换成  d := append(a[0:2:2], 5) 或  d := append(a[0:], 5) 或  d := append(a[:], 5) 效果都和a本身一样
这样的情况原切片a必然不变。

如果不满足1的情况,则此时看截取时所得的切片长度和容量:

【下述的“所得切片”指的是截取后但未套append时的代称】

如果 所得切片的容量=原切片容量(注意没有套append时不牵扯扩容)
    a) 如果 数据长度<原切片数据长度(即仅截取了部分数据),则原切片会被修改; 
    如cut98()中的第一处截取append(a[0:1:2]、cut95()中的第一处截取append(a[0:1]
    但要注意的是,此时如果套了append并发生扩容时,即使满足此情况也不会修改原切片a,因为发生了扩容,原切片不能再满足,不会再做什么修改,cut95()中已说明。
    
    b) 如果 数据长度=原切片数据长度(即截取了所有数据,此时视图是原切片全部),则原切片不会被修改; 和上面第一个情况相同;
    
如果 所得切片的容量<原切片容量
    a) 数据长度<原切片数据长度(即仅截取了部分数据),则原切片不会被修改; 
    如cut97()中的第2处截取append(a[1:2:2],或cut96()中的第1、2处截取append(a[1:2]
    
    b) 数据长度=原切片数据长度(即截取了所有数据),这种情况不存在,编译都不能通过:Invalid index values, must be low <= high <= max

关于截取时的初始索引是否从第一个位置开始的影响

如cut98()中的第一处截取append(a[0:1:2]会修改原切片a;(和append(a[0:1]一样),但如果改为a[0:1:1] a则不会被修改。
这两者都是从第一个位置开始截取,但结果显然不同,因此不能单以“截取时的初始索引是否从第一个位置开始”来定结果。

原切片会不会被修改,和截取时写成a[low:high]还是a[low:high:max]并无直接关系,和截取的初始索引是否从第一个位置开始也无直接关系。

修改原切片细节分析

那为什么所得切片的容量=原切片容量、且数据长度<原切片数据长度时,原切片就会被修改呢?

拿cut95的第一处来说:

    a := []int{1, 2}b := append(a[0:1], 3)

首先创建a切片,长度容量都是2,对应的底层数组是两个元素;

第二步分解,上面已经说明a[0:1]操作时不会修改a,a[0:1]相当于a[0:1:2],a[0:1]取得的值是1,对应的正是底层数组的第一个元素,a[0:1]这个切片本身地址和a一致,只是仅截取第一个元素;

清楚了上面后接着做append操作,在第一个元素位置的基础上,拼接了元素3,相当于3这个元素覆盖掉了第二个位置上的2这个元素,因此底层数组变成了1、3

那你有没有这样想过:1后面拼接了3,为什么不变为1,3,2(即2被挤压了)   而是1,3呢? 为什么把2没有挤到后面去而是覆盖操作?

如果2被挤压、下标变化,意味着切片a的容量发生变化,但实际上拼接没有发生在a上,拼接是给b做了拼接,既然没给a拼接,a本身数据长度就不应该发生变化。

相反如果做了b := append(a[0:1], 3, 4, 5, 6)操作后,情形是a原本的容量已不足支撑新数据,系统经过计算申请了新数组,新数组适配b,然后才会塞数据,而且一样拼接不是发生在a上,a切片就不会再动了,不会再影响原切片。

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

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

相关文章

看懂基本的电路原理图(入门)

文章目录 前言一、二极管二、电容三、接地一般符号四、晶体振荡器五、各种符号的含义六、查看原理图的顺序总结 前言 电子入门&#xff0c;怎么看原理图&#xff0c;各个图标都代表什么含义&#xff0c;今天好好来汇总一下。 就比如这个电路原理图来说&#xff0c;各个符号都…

文件监控-IT安全管理软件

文件监控和IT安全管理软件是用于保护企业数据和网络安全的工具。这些工具可以帮助企业监控文件的变化&#xff0c;防止未经授权的访问和修改&#xff0c;并确保数据的安全性和完整性。 一、具有哪些功能 文件监控软件可以实时监控文件系统的活动&#xff0c;包括文件的创建、修…

L1-076:降价提醒机器人

题目描述 小 T 想买一个玩具很久了&#xff0c;但价格有些高&#xff0c;他打算等便宜些再买。但天天盯着购物网站很麻烦&#xff0c;请你帮小 T 写一个降价提醒机器人&#xff0c;当玩具的当前价格比他设定的价格便宜时发出提醒。 输入格式&#xff1a; 输入第一行是两个正整数…

条款13:以对象管理资源

文章目录 没有管理的情况解决办法之unique_ptr智能指针解决办法之shared_ptr智能指针总结 没有管理的情况 资源是指一旦你使用完它&#xff0c;就需要返回系统的东西。 class Investment { ... }; // 投资类型层次结构的基类 Investment* createInvestment(); // 工厂函数&…

2022–2023学年2021级计算机科学与技术专业数据库原理 (A)卷

一、单项选择题&#xff08;每小题1.5分&#xff0c;共30分&#xff09; 1、构成E—R模型的三个基本要素是&#xff08; B &#xff09;。 A&#xff0e;实体、属性值、关系 B&#xff0e;实体、属性、联系 C&#xff0e;实体、实体集、联系 D&#xff0e;实体、实体…

html-css-js移动端导航栏底部固定+i18n国际化全局

需求&#xff1a;要做一个移动端的仿照小程序的导航栏页面操作&#xff0c;但是这边加上了i18n国家化&#xff0c;由于页面切换的时候会导致国际化失效&#xff0c;所以写了这篇文章 1.效果 切换页面的时候中英文也会跟着改变&#xff0c;不会导致切换后回到默认的语言 2.实现…

oracle 9i10g编程艺术-读书笔记1

根据书中提供的下载代码链接地址&#xff0c;从github上找到源代码下载地址。 https://github.com/apress下载好代码后&#xff0c;开始一段新的旅行。 设置 SQL*Plus 的 AUTOTRACE 设置 SQL*Plus 的 AUTOTRACE AUTOTRACE 是 SQL*Plus 中一个工具&#xff0c;可以显示所执行…

Python-Selenium 调用 JavaScript

当前环境&#xff1a; Windows 10 Python 3.7 selenium3.141.0 urllib31.26.2 Chromium 65.0.3312.0 &#xff08;32 位&#xff09; 在 WebDriver 中提供了执行 JavaScript 的方法&#xff1a; execute_script(script, *args)&#xff0c;JavaScript 代码以字符串的形式…

分布式数据库事务故障恢复的原理与实践

关系数据库中的事务故障恢复并不是一个新问题&#xff0c;自70年代关系数据库诞生之后就一直伴随着数据库技术的发展&#xff0c;并且在分布式数据库的场景下又遇到了一些新的问题。本文将会就事务故障恢复这个问题&#xff0c;分别讲述单机数据库、分布式数据库中遇到的问题和…

Java Bean Validation API

API 默认包&#xff1a;javax.validation。 Validator 基础接口&#xff1a;javax.validation.Validator。 public interface Validator {/** 验证 object*/<T> Set<ConstraintViolation<T>> validate(T object, Class<?>... groups);/** 验证属性…

华为商城秒杀时加密验证 device_data 的算法研究

前言 之前华为商城放出 Mate60 手机时, 想给自己和家人抢购一两台&#xff0c;手动刷了好几天无果后&#xff0c;决定尝试编写程序&#xff0c;直接发送 POST 请求来抢。通过抓包和简单重放发送后&#xff0c;始终不成功。仔细研究&#xff0c;发现 Cookie 中有一个名为 devic…

启动gazebo harmonic

ros2 launch ros_gz_sim gz_sim.launch.py gz_version:8 如果不输入gz_version:8,默认就是6&#xff0c;启动的就是默认版本ign版本 左边那个是8&#xff0c;右边那个是6

基于EPICS modbus模块的单通道电压监测项目

先介绍在本项目中使用到的硬件&#xff1a; 1&#xff09;开发板&#xff1a;为香橙派Zero2&#xff0c;安装系统如下&#xff1a; Distributor ID: Ubuntu Description: Ubuntu 22.04.2 LTS Release: 22.04 Codename: jammy 2&#xff09; USB转485模块&…

深入探索MongoDB集群模式:从高可用复制集

MongoDB复制集概述 MongoDB复制集主要用于实现服务的高可用性&#xff0c;与Redis中的哨兵模式相似。它的核心作用是数据的备份和故障转移。 复制集的主要功能 数据复制&#xff1a;数据写入主节点&#xff08;Primary&#xff09;时&#xff0c;自动复制到一个或多个副本节…

react ant tree节点没有children也会显示展开框 节点有children却不显示展开框

1.背景 最近处理树状结构时遇到了一个诡异问题&#xff0c;后端返回了组织树&#xff0c;组织树里面可能有组织&#xff0c;也可能有用户&#xff0c;很奇怪的是所有用户都会显示展开图标&#xff0c;而组织有些会显示展开图标&#xff0c;有些不会显示 2.分析 一开始找到了用…

【Java 进阶篇】Linux 常用命令使用详解:玩转命令行的魔法世界

在计算机的世界里&#xff0c;Linux是一个强大而富有魅力的操作系统。对于很多小白用户来说&#xff0c;刚接触Linux时可能感觉有些陌生&#xff0c;尤其是在命令行界面下。然而&#xff0c;正是这个看似晦涩的命令行&#xff0c;才是Linux系统最为强大和灵活的地方。本文将围绕…

论文阅读——SG-Former

SG-Former: Self-guided Transformer with Evolving Token Reallocation 1. Introduction 方法的核心是利用显著性图&#xff0c;根据每个区域的显著性重新分配tokens。显著性图是通过混合规模的自我关注来估计的&#xff0c;并在训练过程中自我进化。直观地说&#xff0c;我们…

分布式【雪花算法】

雪花算法 背景&#xff1a;在分布式系统中&#xff0c;需要使用全局唯一ID&#xff0c;期待ID能够按照时间有序生成。 **原理&#xff1a;**雪花算法是 64 位 的二进制&#xff0c;一共包含了四部分&#xff1a; 1位是符号位&#xff0c;也就是最高位&#xff0c;始终是0&am…

【教学类-43-11】 20231231 3*3宫格数独提取单元格坐标数字的通用模板(做成2*2=4套、3*2=6套)

背景需求&#xff1a; 1、以前做单元格填充&#xff0c;都是制作N个分开的单元格 &#xff08;表格8&#xff09; 2、这次做五宫格数独的Word模板&#xff0c;我图方便&#xff0c;就只用了一个大表格&#xff0c;第六行第六列隐藏框线&#xff0c;看上去就是分开的&#xff…

剑指offer题解合集——Week2day6

文章目录 剑指offerWeek2周六&#xff1a;表示数值的字符串AC代码思路&#xff1a; 周六&#xff1a;调整数组顺序使奇数位于偶数前面AC代码思路&#xff1a; 剑指offerWeek2 周六&#xff1a;表示数值的字符串 题目链接&#xff1a;表示数值的字符串 请实现一个函数用来判…