您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > Go语言

GO 切片实力踩坑

时间:2020-07-18 18:37:07  来源:  作者:

概述

GO 语言的切片这两天用了用, 可以支持切割数组的中间部分. 但今天使用中, 出了 bug, 查了半天, 发现是切片的问题, 简单写个 demo 复现当时的情况:

package main

import "fmt"

func main() {
	a := []int{1, 2, 3, 4, 5}
	b := a[2:4]
	b[0] = 9
	fmt.Println(a)
}

你以为输出的是什么? 来, 看结果:

[1 2 9 4 5]

懵没懵?? 这是怎么回事呢?

(我用个语言怎么老踩坑, 笨的一X)

解惑

看这段 GO 代码的输出, 我们在修改b数组第一个元素值的时候, a数组的第三个元素修改了, 这两个有什么联系吗? 仔细看, b数组在切的时候, 切的不就是a数组第三第四的元素吗? 如此看来, b[0] 不正对应 a[2] 吗?

大胆假设: **GO 中对数组进行切割, 并不会切一个新的数组出来, 而是仍然使用原数组, 只是修改下数组的首地址和长度. **

验证:

package main

import "fmt"

func main() {
	a := []int{1, 2, 3, 4, 5}
	b := a[2:4]
	b[0] = 9
	fmt.Printf("%pn", &a[2])
	fmt.Printf("%pn", &b[0])
}

打印出来的地址完全一致, 印证了之前的猜想, 果然是一个数组. 同时修改a数组, 也会影响到b数组.

那可不可以对b数组越界访问, 访问a数组的值呢? 不行, GO 会对数组进行越界检查.

查看文档后发现, GO 切片的内部实现是这样的包含了三个字段. 其中各字段含义如下:

  1. 数组首地址指针: 指向底层数组的首地址(这个是真正的数组)
  2. 数组长度: 数组当前已经使用的长度
  3. 数组容量: 数组已分配内存的总长度, 比数组长度多出的部分, 是占用内存还没有使用的.

如此看来, 对其进行切割, 并不会整个复制, 对于大切片的操作就显得很友好了, 毕竟共享底层数组, 只需要创建很少的数据就可以了. 只是要注意数组的同步修改问题.

这么看来, 貌似也可以解释为什么叫切片了. **切片就是将底层的数组切出一部分来, 而不会创建新的数组. **

切片是有容量的, 那上面的切片b的容量是多少呢? 我看了一下: 长度是2, 容量是3.

GO 的切片在容量足够的时候, 是不会动态扩容的. (扩容会创建更大容量新的数组并复制原数组数据). 那也就是说, 如果我向b追加数据, 就可以影响到原数组的后面的数据了??

试一发:

package main

import "fmt"

func main() {
	a := []int{1, 2, 3, 4, 5}
	b := a[2:4]
	b = Append(b, 10)
	fmt.Println(b)
	fmt.Println(a)
}

果然, 容量允许的话, 追加操作使用的仍然是原始数组.

所以: 切片的容量其实是底层数组的容量

同时, 有了之前对 GO 的了解, 知道 GO 所有的函数都是以传递副本值的方式进行, 传递切片也一样, 而切片的结构体包含(数组指针, 长度, 容量)三个元素, 底层数组并不属于值本身, 所以切片在函数间传递的复制成本很小, 而且函数对切片的修改也会反应到底层数组上. 同理可得, 如果在函数中对切片执行了扩容操作, 那改动就不会影响原数据, 因为扩容后操作的是新的数组了.

OK. 切片到这里就结束了, 简单说就是数组上面再套一层. 切片的切片共享底层数组.

最后说一句, GO 创建数组和切片的方式(数组和切片是不同的数据结构):

// 方括号为空, 创建的是切片类型
a := []int{1, 2}
// 方括号指定长度, 创建的是真正的数组类型
b := [2]int{}

总结

至此, 对 GO 的切片有了全新的认识. 在使用切片的时候, 需要特别注意, 切片的截取与原对象共享底层数组, 在数据修改时要特别注意.

如果需要一个安全的可修改的切片, 可以使用copy函数复制一个全新的数组出来, 与原数组分离就可以了.



Tags:GO 切片   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
概述GO 语言的切片这两天用了用, 可以支持切割数组的中间部分. 但今天使用中, 出了 bug, 查了半天, 发现是切片的问题, 简单写个 demo 复现当时的情况:package mainimport "...【详细内容】
2020-07-18  Tags: GO 切片  点击:(47)  评论:(0)  加入收藏
▌简易百科推荐
zip 是一种常见的归档格式,本文讲解 Go 如何操作 zip。首先看看 zip 文件是如何工作的。以一个小文件为例:(类 Unix 系统下)$ cat hello.textHello!执行 zip 命令进行归档:$ zip...【详细内容】
2021-12-17  Go语言中文网    Tags:Go语言   点击:(13)  评论:(0)  加入收藏
大家好,我是 polarisxu。前段时间,Russ Cox 明确了泛型相关的事情,原计划在标准库中加入泛型相关的包,改放到 golang.org/x/exp 下。目前,Go 泛型的主要设计者 ianlancetaylor 完...【详细内容】
2021-11-30  Go语言中文网    Tags:slices 包   点击:(24)  评论:(0)  加入收藏
前言最近因为项目需要写了一段时间的 Go ,相对于 Java 来说语法简单同时又有着一些 Python 之类的语法糖,让人大呼”真香“。 但现阶段相对来说还是 Python 写的多一些,偶尔还...【详细内容】
2021-11-25  crossoverJie    Tags:Go   点击:(29)  评论:(0)  加入收藏
go-micro是基于 Go 语言用于开发的微服务的 RPC 框架,主要功能如下:服务发现,负载均衡 ,消息编码,请求/响应,Async Messaging,可插拔接口,最后这个功能牛p安装步骤安装proto...【详细内容】
2021-09-06    石老师小跟班  Tags:go-micro   点击:(197)  评论:(0)  加入收藏
GoLand 2021.2 EAP 5 现已发布。用户可以从工具箱应用程序中获得 EAP 构建,也可以从官方网站手动下载。并且从此 EAP 开始,只有拥有有效的 JetBrains 帐户才能加入该计划。手...【详细内容】
2021-06-29  IT实战联盟  今日头条  Tags:GoLand   点击:(185)  评论:(0)  加入收藏
作者:HDT3213今天给大家带来的开源项目是 Godis:一个用 Go 语言实现的 Redis 服务器。支持: 5 种数据结构(string、list、hash、set、sortedset) 自动过期(TTL) 发布订阅、地理位...【详细内容】
2021-06-18  HelloGitHub  今日头条  Tags:Go   点击:(125)  评论:(0)  加入收藏
统一规范篇合理规划目录本篇主要描述了公司内部同事都必须遵守的一些开发规矩,如统一开发空间,既使用统一的开发工具来保证代码最后的格式的统一,开发中对文件和代码长度的控制...【详细内容】
2021-05-18  1024课堂    Tags:Go语言   点击:(232)  评论:(0)  加入收藏
闭包概述 闭包不是Go语言独有的概念,在很多编程语言中都有闭包 闭包就是解决局部变量不能被外部访问的一种解决方案 是把函数当作返回值的一种应用 代码演示总体思想:在函数...【详细内容】
2021-05-14  HelloGo  今日头条  Tags:Go语言   点击:(223)  评论:(0)  加入收藏
一时想不开,想了解一下Go语言,于是安装了并体验了一下。下载1. 进入golang.google.cn 点击Download Go 2.选择对应的操作系统,点击后开始下载。 安装1. windows下执行傻瓜式安...【详细内容】
2021-05-12  程序员fearlazy  fearlazy  Tags:Go语言   点击:(236)  评论:(0)  加入收藏
1.简介channel是Go语言的一大特性,基于channel有很多值得探讨的问题,如 channel为什么是并发安全的? 同步通道和异步通道有啥区别? 通道为何会阻塞协程? 使用通道导致阻塞的协程...【详细内容】
2021-05-10  程序员麻辣烫  今日头条  Tags:Go通道   点击:(274)  评论:(0)  加入收藏
相关文章
    无相关信息
最新更新
栏目热门
栏目头条