深入理解go slice结构
作者:好吃吗 发布时间:2024-04-26 17:27:07
文章
slice介绍
append的机制
slice tricks
go data
slice
array的语法: [4]int{1,2,3,4}, [...]int{1,2,3}
。在go中array是值类型,这就意味着一个类型为array的变量名并不是一个指针,当传递值是,array总是被复制。
slice的语法: []int{1,2,3,4}
, make([]int), make([]int,10)
当make只有两个参数时,cap和len相同。
slice本质上是array的一个片段的描述,它包含3部分:[ptr, len, cap]
通过make([]int, 5)
创建的slice,其内存布局如下:
对这个slice进行截断之后,形成新的slice:
使用cap可以对slice进行扩增: s[:cap(s)]
.
copy(dst, src []T) int
的用法: copy会复制dst和src中长度最小的元素所对应的数量(所以如果dst==nil, 则copy返回0)。并且,copy还能处理dst和src存在重叠的情况。
用法:
t := make([]byte, len(s), (cap(s)+1)*2)
copy(t, s)
s = t
append(dst []T, element...T) []T
的实现,与下面的AppendByte
类似:
首先检查容量,容量不足则使用make构造一个新的slice并将原来的元素移动。
func AppendByte(slice []byte, data ...byte) []byte {
m := len(slice)
n := m + len(data)
if n > cap(slice) { // if necessary, reallocate
// allocate double what's needed, for future growth.
newSlice := make([]byte, (n+1)*2)
copy(newSlice, slice)
slice = newSlice
}
slice = slice[0:n]
copy(slice[m:n], data)
return slice
}
slice对gc的影响(gotcha)
如果对一个很大的数组,取其中很小的一段切片,都会造成这个数组不会被gc回收。
gc使用mark-and-sweep
(标记清除)。gc维护一个已分配的堆对象表,在标记阶段,它将寄存器,堆栈上的指针作为root进行遍历标记。
为什么部分slice会导致整体的array不会回收呢?设想下面的slice:
a := [4]int{0,1,2,3}
s := a[1:2] // {1}
return s
a会不会被回收呢?答案是不会,因为gc遍历s时,通过data指针找到对应的array切片,它发现这个地址在之前分配的一个array对象的范围内,从而标记这个array为可到达对象,避免其被整个清除。(这里所要理解的就是array是按范围标记的,并不是按指针头标记的,因为一个内存块对象是有范围的,如果被部分引用,说明整个对象仍然是可达的。)
如何解决?如果是这种情况,一个较大array返回较小切片,可以使用复制:
before:
var digitRegexp = regexp.MustCompile("[0-9]+")
func FindDigits(filename string) []byte {
b, _ := ioutil.ReadFile(filename)
return digitRegexp.Find(b)
}
after:
func CopyDigits(filename string) []byte {
b, _ := ioutil.ReadFile(filename)
b = digitRegexp.Find(b)
c := make([]byte, len(b))
copy(c, b)
return c
}
reflect.SliceHeader
package reflect
// SliceHeader和StringHeader具有相同的Data和Len,这导致[]byte可以直接转换成string,而不需要任何复制
type SliceHeader struct {
Data uintptr
Len int
Cap int
}
type StringHeader struct {
Data uintptr
Len int
}
转换代码:
func SliceByteAsString(b []byte) string {
return *(*string)(unsafe.Pointer(&b))
}
func StringAsSliceByte(s string) []byte {
p := unsafe.Pointer(&d)
var b []byte
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&b))
hdr.Data = uintptr(p)
hdr.Cap = len(s)
hdr.Len = len(s)
return b
}
func Int64AsByteSlice(d int64) []byte {
p := unsafe.Pointer(&d)
var b []byte
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&b))
hdr.Data = uintptr(p)
hdr.Cap = 8
hdr.Len = 8
return b
}
来源:https://blog.csdn.net/xhdxhdxhd/article/details/120275771
猜你喜欢
- 前几天,酋长同学在日志里提到了关于Google宽松的管理制度,一个产品任务下来是没有时间限制的,Google深信在有时间限制下的产品肯定是不
- 首先,需要简单的了解一下爬虫,尽可能简单快速的上手,其次,需要了解的是百度的API的接口,搞定这个之后,最后,按照官方给出的demo,然后写
- 技术背景lambda表达式本身是一个非常基础的python函数语法,其基本功能跟使用def所定义的python函数是一样的,只是lambda
- torch.nn.Modules 相当于是对网络某种层的封装,包括网络结构以及网络参数和一些操作torch.nn.Module 是所有神经网
- Python 的 httpx 包是一个复杂的 Web 客户端。当你安装它后,你就可以用它来从网站上获取数据。像往常一样,
- 1.简介当一个表数据量很大时候,很自然我们就会想到将表拆分成很多小表,在执行查询时候就到各个小表去查,最后汇总数据集返回给调用者加快查询速度
- Python进程池是Python标准库中multiprocessing模块提供的一种用于管理进程的方式。它可以使Python程序以并行的方式
- defer关键字defer关键字可以让函数或语句延迟到函数语句块的最结尾时,即即将退出函数时执行,即便函数中途报错结束、即便已经panic(
- 前言需要导入以下包,没有的通过pip安装import matplotlib.pyplot as pltimport cv2from PIL
- 导言DataList的编辑界面由EditItemTemplate里的标记语言和web控件定义。在目前为止所做的DataList编辑功能的例子
- 一、提前准备为了大家学习方便,我在这里面建立两张表并为其添加一些数据。一张水果表,一张供应商表。水果表 fruits表f_idf_namef
- 【ThinkPHP版本查询】dump(THINK_VERSION);模板获取get参数{$Think.get.pageNumber}或者$R
- 这篇文章主要介绍了Python FTP文件定时自动下载实现过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价
- 本文实例讲述了Python实现读取txt文件中的数据并绘制出图形操作。分享给大家供大家参考,具体如下:下面的是某一文本文件中的数据。6.11
- QUICKSORT(A, p, r)是快速排序的子程序,调用划分程序对数组进行划分,然后递归地调用QUICKSORT(A, p, r),以完
- 很荣幸您能看到这篇文章,相信通过标题打开这篇文章的都是对tensorflow感兴趣的,特别是对卷积神经网络在mnist手写识别这个实例感兴趣
- 前言:之前,我写笔记的工具一直都是 notion,而且没有写博客的习惯。但是一是由于 notion 的服务器在
- 介绍还记得你在小学时学习如何加减数字吗?现在,你也可以对图像做同样的事情!输入图像可以进行算术运算,例如加法、减法和按位运算(AND、OR、
- 有时候网站会收到一些投稿文章,或者也会转载别人的文章,新创建一个用户又有些麻烦,但在作者名称那里显示自己的名字,总不是那么和谐。今天倡萌推荐
- 本文实例讲述了Python列表list内建函数用法。分享给大家供大家参考,具体如下:#coding=utf8'''&