切片

切片(slice)是对数组一个连续片段的引用(该数组我们称之为相关数组,通常是匿名的),所以切片是一个引用类型(因此更类似于 C/C++ 中的数组类型,或者 Python 中的 list 类型)

切片是一个 长度可变的数组。

多个切片如果表示同一个数组的片段,它们可以共享数据;因此一个切片和相关数组的其他切片是共享存储的,相反,不同的数组总是代表不同的存储。数组实际上是切片的构建块。

优点 因为切片是引用,所以它们不需要使用额外的内存并且比使用数组更有效率,所以在 Go 代码中 切片比数组更常用。

声明切片的格式是: var identifier []type(不需要说明长度)。

一个切片在未初始化之前默认为 nil,长度为 0。

切片也可以用类似数组的方式初始化:var x = []int{2, 3, 5, 7, 11}。这样就创建了一个长度为 5 的数组并且创建了一个相关切片。

切片在内存中的组织方式实际上是一个有 3 个域的结构体:指向相关数组的指针,切片 长度以及切片容量。下图给出了一个长度为 2,容量为 4 的切片。——注意看其中的内存共享方式!!!

  • y[0] = 3 且 y[1] = 5
  • 切片 y[0:4] 由 元素 3, 5, 7 和 11 组成。

注意 绝对不要用指针指向 slice。切片本身已经是一个引用类型,所以它本身就是一个指针!!

7.2.2 将切片传递给函数

如果你有一个函数需要对数组做操作,你可能总是需要把参数声明为切片。当你调用该函数时,把数组分片,创建为一个 切片引用并传递给该函数。这里有一个计算数组元素和的方法:

func sum(a []int) int {
s := 0
for i := 0; i < len(a); i++ {
s += a[i]
}
return s
} func main() {
var arr = [5]int{0, 1, 2, 3, 4}
sum(arr[:])
}

7.2.3 用 make() 创建一个切片

当相关数组还没有定义时,我们可以使用 make() 函数来创建一个切片 同时创建好相关数组:var slice1 []type = make([]type, len)

下图描述了使用 make 方法生成的切片的内存结构:

7.2.4 new() 和 make() 的区别

看起来二者没有什么区别,都在堆上分配内存,但是它们的行为不同,适用于不同的类型。

  • new(T) 为每个新的类型T分配一片内存,初始化为 0 并且返回类型为*T的内存地址:这种方法 返回一个指向类型为 T,值为 0 的地址的指针,它适用于值类型如数组和结构体(参见第 10 章);它相当于 &T{}
  • make(T) 返回一个类型为 T 的初始值,它只适用于3种内建的引用类型:切片、map 和 channel(参见第 8 章,第 13 章)。

换言之,new 函数分配内存,make 函数初始化;下图给出了区别:

在图 7.3 的第一幅图中:

var p *[]int = new([]int) // *p == nil; with len and cap 0
p := new([]int)

在第二幅图中, p := make([]int, 0) ,切片 已经被初始化,但是指向一个空的数组。

以上两种方式实用性都不高。下面的方法:

var v []int = make([]int, 10, 50)

或者

v := make([]int, 10, 50)

这样分配一个有 50 个 int 值的数组,并且创建了一个长度为 10,容量为 50 的 切片 v,该 切片 指向数组的前 10 个元素。

转自:https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/07.2.md

7.6.3 字符串和切片的内存结构

字符串 string s = "hello" 和子字符串 t = s[2:3] 在内存中的结构可以用下图表示:

7.6.8 切片和垃圾回收

切片的底层指向一个数组,该数组的实际容量可能要大于切片所定义的容量。只有在没有任何切片指向的时候,底层的数组内层才会被释放,这种特性有时会导致程序占用多余的内存。

示例 函数 FindDigits 将一个文件加载到内存,然后搜索其中所有的数字并返回一个切片。

var digitRegexp = regexp.MustCompile("[0-9]+")

func FindDigits(filename string) []byte {
b, _ := ioutil.ReadFile(filename)
return digitRegexp.Find(b)
}

这段代码可以顺利运行,但返回的 []byte 指向的底层是整个文件的数据。只要该返回的切片不被释放,垃圾回收器就不能释放整个文件所占用的内存。换句话说,一点点有用的数据却占用了整个文件的内存。

想要避免这个问题,可以通过拷贝我们需要的部分到一个新的切片中:

func FindDigits(filename string) []byte {
b, _ := ioutil.ReadFile(filename)
b = digitRegexp.Find(b)
c := make([]byte, len(b))
copy(c, b)
return c
}

go语言笔记——切片底层本质是共享数组内存!!!绝对不要用指针指向 slice切片本身已经是一个引用类型就是指针的更多相关文章

  1. go语言笔记——append底层实现和Cpp vector无异,只是有返回值,double后返回了新的vector地址而已

    切片的复制与追加 如果想增加切片的容量,我们必须创建一个新的更大的切片并把原分片的内容都拷贝过来.下面的代码描述了从拷贝切片的 copy 函数和向切片追加新元素的 append 函数. 示例 7.12 ...

  2. go语言笔记——多值函数,本质上和nodejs的回调很像,不过nodejs是回调的第一个参数是err,而golang里是第二个!

    5.2 测试多返回值函数的错误 Go 语言的函数经常使用两个返回值来表示执行是否成功:返回某个值以及 true 表示成功:返回零值(或 nil)和 false 表示失败(第 4.4 节).当不使用 t ...

  3. go语言中的数组切片:特立独行的可变数组

    go语言中的数组切片:特立独行的可变数组 初看go语言中的slice,觉得是可变数组的一种很不错的实现,直接在语言语法的层面支持,操作方面比起java中的ArrayList方便了许多.但是在使用了一段 ...

  4. 013-结构体-C语言笔记

    013-结构体-C语言笔记 学习目录 1.[掌握]返回指针的函数 2.[掌握]指向函数的指针 3.[掌握]结构体的声明 4.[掌握]结构体与数组 5.[掌握]结构体与指针 6.[掌握]结构体的嵌套 7 ...

  5. 3.9 Go Slice切片

    3.9 Go Slice切片 Go语言切片(Slice) 切片是可动态变化的序列,是对数组的引用,引用类型,遵循引用传递的机制 slice类型写作[ ]T,T是slice元素类型,var s1 []i ...

  6. 【C语言】超详讲解☀️指针是个什么针?(一次性搞定指针问题)

    目录 前言 一. 什么是指针? 引例 计算机是怎么对内存单元编号的呢? 内存空间的地址如何得到 想存地址怎么办? ​ 本质目的不是为了存地址 二.指针和指针类型 为什么有不同类型的指针 1.指针的解引 ...

  7. go语言笔记——指针,和C用法以及本质一样,但不支持指针的+-运算!

    4.4.2 值类型和引用类型 所有像 int.float.bool 和 string 这些基本类型都属于值类型,使用这些类型的变量直接指向存在内存中的值. Go 语言的取地址符是 &,放到一个 ...

  8. go语言笔记——切片函数常见操作,增删改查和搜索、排序

    7.6.6 搜索及排序切片和数组 标准库提供了 sort 包来实现常见的搜索和排序操作.您可以使用 sort 包中的函数 func Ints(a []int) 来实现对 int 类型的切片排序.例如  ...

  9. GoLang笔记-数组和切片,本质是就是长度不可变的可变的区别

    数组 Arrays 数组是内置(build-in)类型,是一组同类型数据的集合,它是值类型,通过从0开始的下标索引访问元素值.在初始化后长度是固定的,无法修改其长度.当作为方法的入参传入时将复制一份数 ...

随机推荐

  1. POJ2152 Fire (树形DP)

    题意:n个城市n-1条边 组成一棵树 在每个城市修建消防站会有一个花费costi 每个城市能防火当且仅当地图上距离他最近的消防站距离小于di   问如何修建消防站 使地图上所有的城市都有预防火灾的能力 ...

  2. 数字图像处理技术在TWaver可视化中的应用

    数字图像处理(Digital Image Processing)又称为计算机图像处理,它是指将图像信号转换成数字信号并利用计算机对其进行处理的过程.常用的图像处理方法有图像增强.复原.编码.压缩等,数 ...

  3. [Git]Please make sure you have the correct access rights and the repository exists

    这个问题是这样,需要在已有github账号的A机器上,再创建一个github账号,新账号创建完毕,将代码通过机器A push上之后,再另一台机器B,clone 这个项目时报出了如下错误: Permis ...

  4. python下载网络文件

    python下载网络文件 制作人:全心全意 下载图片 #!/usr/bin/python #-*- coding: utf-8 -*- import requests url = "http ...

  5. stall and flow separation on airfoil or blade

    stall stall and flow separation Table of Contents 1. Stall and flow separation 1.1. Separation of Bo ...

  6. RAII手法封装互斥锁

    RAII手法是 Resource Acquisition is Initialization 的缩写,意为“资源获取即初始化”,在使用智能指针时也使用,下面是针对互斥量时的实现, #include & ...

  7. hdu 1754 I Hate It(线段树水题)

    >>点击进入原题测试<< 思路:线段树水题,可以手敲 #include<string> #include<iostream> #include<a ...

  8. [bzoj4591][Shoi2015][超能粒子炮·改] (lucas定理+组合计数)

    Description 曾经发明了脑洞治疗仪&超能粒子炮的发明家SHTSC又公开了他的新发明:超能粒子炮·改--一种可以发射威力更加 强大的粒子流的神秘装置.超能粒子炮·改相比超能粒子炮,在威 ...

  9. springcloud(十):熔断监控Hystrix Dashboard

             申明: 这里比较坑爹,大家写的时候要小心,这里和springboot的版本有关系哈,我使用的是2.0 版本,要么调频为1.5 版本,要么使用其他方式 解决错误,我选择了还是用2.0  ...

  10. 负载均衡之nginx+consul(自动更新路由)

    前几篇先是记载了如何通过nginx配置服务负载均衡,后面记载了如何通过 ocelot 配置 服务负载均衡,分别介绍了用webapi注册服务以及配置文件注册服务,通过ocelot webapi + co ...