slice在golang中是最常用的类型,一般可以把它作为数组使用,但是比数组要高效呀。不过,我感觉这个东西用的不好坑太多了。还是需要了解下他底层的实现

slice的结构定义

type slice struct {
array unsafe.Pointer
len int
cap int
}

看结构定义,就三个字段,那个指针指向的就是底层数组,所以说slice的底层结构就是数组。

slice的声明

第一种方式
var s []int #和数组差不多,[]中间不要数字
第二种方式
s :=[]int{}
第三种方式
s :=make([]int,len,cap)
len 表示元素的实际数量
cap 表示可以容纳元素的最大个数

以上三种定义是有区别的,第一种定义的s值为nil slice,其它两种为empty slice.要编码时要加以区分

slice扩容逻辑

  1. 如果slice容量足够容纳新增的元素,那么不会扩容。
  2. 如果新增元素后容量不足,则会扩容为原容量的 2 倍大小,如果原 slice 容量大于1024,那扩容后的容量等于每次扩容前增加 1/4。同时将原来的数据拷贝到新的数组中,所以还是要合理使用扩容。

slice的几个混淆点

一、slice作参数传递

slice作参数传递时,虽然是值传递(拷贝一份),但是slice的内部指针,指向的还是同一个数组是引用,所以会有影响。

func main() {
var s []int
for i := 1; i <= 4; i++ {
s = append(s, i)
}
fmt.Println("原始s的值",s)
changeslice(s)
fmt.Println("s被改变后的值", s) } func changeslice(s []int) {
s[0] = 7
s[1] = 8
fmt.Println("changeslice后的s值", s)
} 输出:
原始s的值 [1 2 3 4]
changeslice后的s值 [7 8 3 4]
s被改变后的值 [7 8 3 4]

二、使用append,无扩容

继续上面的,这次在函数里面加上append;

append后,生成的新的slice。但是因为没有扩容,指向的还是同一个数组,所以还是会影响到原来的slice

func main() {
var s []int
for i := 1; i <= 3; i++ {
s = append(s, i)
} fmt.Println("原始s的值",s)
changeslice(s)
fmt.Println("s被改变后的值", s) } func changeslice(s []int) { s = append(s,777)
s[1] = 999
fmt.Println("changeslice后的s值", s) }
输出:
原始s的值 [1 2 3]
changeslice后的s值 [1 999 3 777]
s被改变后的值 [1 999 3]

三、继续append,有扩容

这次继续使用append,不过这次多appen几个元素,

发现如果扩容后,那么生成的新slice就不会影响到原来的了,因为

指向的不是原来的数组

func main() {
var s []int
for i := 1; i <= 3; i++ {
s = append(s, i)
} fmt.Println("原始s的值",s)
changeslice(s)
fmt.Println("s被改变后的值", s) } func changeslice(s []int) { s = append(s,777,888)
s[1] = 999
fmt.Println("changeslice后的s值", s) }
输出:
原始s的值 [1 2 3]
changeslice后的s值 [1 999 3 777 888]
s被改变后的值 [1 2 3]

通过slice切割生成slice

从slice切割生成的slice,其实还是指向的同一个底层数组,一方有改动,还是会影响到对方。

   var s []int
s = []int{1,2,3,4,5,6}
fmt.Println("原始的s的值", s)
s2 :=s[1:5]
fmt.Println("切割后的:s2", s2)
s2[0] = 99
fmt.Println("s2重新赋值:",s2)
fmt.Println("s的值:", s)
s3 :=s2[0:2]
fmt.Println("s3的值", s3)
s3[1] = 99999
fmt.Println("s2的值",s2)
fmt.Println("s的值",s) 输出:
原始的s的值 [1 2 3 4 5 6]
切割后的:s2 [2 3 4 5]
s2重新赋值: [99 3 4 5]
s的值: [1 99 3 4 5 6]
s3的值 [99 3]
s2的值 [99 99999 4 5]
s的值 [1 99 99999 4 5 6]

总结

平时使用slice时,还是要小心点,主要注意有无扩容这块。不然就会产生很多不可想像的问题了。

golang中,slice的几个易混淆点的更多相关文章

  1. 说说不知道的Golang中参数传递

    本文由云+社区发表 导言 几乎每一个C++开发人员,都被面试过有关于函数参数是值传递还是引用传递的问题,其实不止于C++,任何一个语言中,我们都需要关心函数在参数传递时的行为.在golang中存在着m ...

  2. C#中一些易混淆概念总结

    C#中一些易混淆概念 这几天一直在复习C#基础知识,过程中也发现了自己以前理解不清楚和混淆的概念.现在给大家分享出来我的笔记: 一,.NET平台的重要组成部分都是有哪些 1)FCL (所谓的.NET框 ...

  3. C#中易混淆的知识点

    C#中易混淆的知识点 一.引言 今天在论坛中看到一位朋友提出这样的一个问题,问题大致(问题的链接为:http://social.msdn.microsoft.com/Forums/zh-CN/52e6 ...

  4. lua中易混淆函数

    lua中易混淆的函数 ipairs和pairs: ipairs只能顺序遍历table,遇到key不是数字就会退出 pairs可以遍历table中所有元素 ----------------------- ...

  5. JavaScript中易混淆的DOM属性及方法对比

    JavaScript中易混淆的DOM属性及方法对比 ParentNode.children VS Node.prototype.childNodes ParentNode.children:该属性继承 ...

  6. 【C#小知识】C#中一些易混淆概念总结(七)---------解析抽象类,抽象方法

    目录: [C#小知识]C#中一些易混淆概念总结--------数据类型存储位置,方法调用,out和ref参数的使用 [C#小知识]C#中一些易混淆概念总结(二)--------构造函数,this关键字 ...

  7. 【C#小知识】C#中一些易混淆概念总结(六)---------解析里氏替换原则,虚方法 分类: C# 2014-02-08 01:53 1826人阅读 评论(0) 收藏

    目录: [C#小知识]C#中一些易混淆概念总结--------数据类型存储位置,方法调用,out和ref参数的使用 [C#小知识]C#中一些易混淆概念总结(二)--------构造函数,this关键字 ...

  8. 【C#小知识】C#中一些易混淆概念总结(五)---------继承 分类: C# 2014-02-06 22:05 1106人阅读 评论(0) 收藏

    目录: [C#小知识]C#中一些易混淆概念总结--------数据类型存储位置,方法调用,out和ref参数的使用 [C#小知识]C#中一些易混淆概念总结(二)--------构造函数,this关键字 ...

  9. 【C#小知识】C#中一些易混淆概念总结(五)---------深入解析C#继承

    目录: [C#小知识]C#中一些易混淆概念总结--------数据类型存储位置,方法调用,out和ref参数的使用 [C#小知识]C#中一些易混淆概念总结(二)--------构造函数,this关键字 ...

随机推荐

  1. PHP面试题2019年搜狐面试题及答案解析

    一.单选题(共27题,每题5分) 1.阅读下面PHP代码,并选择输出结果( ) A.0 B.1 C.2 D.3 参考答案:D 答案解析:static属性常驻内存 2.PHP单例模式操作描述错误的是? ...

  2. DataGridView中的rows.Count比实际行数多1的原因以及解决办法

    场景 DataGridView怎样实现添加.删除.上移.下移一行: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/10281414 ...

  3. crm-全总结

    1.什么是crm 客户关系管理系统 ,以客户数据为中心建立一个信息收集.管理.分析和利用的信息系统 2.业务逻辑相关使用crm-app完成 1)路由项目分发到crm-app (别名 传参 命名空间) ...

  4. JavaScript中break、continue和return的区别

    break function myBreak() { for(var i = 0; i < 5; i++) { if(i == 3) { break; } console.log(i); } } ...

  5. QNetworkRequest加Authorization头,适应Rest风格的API

    Rest是无状态的.Rest的请求之间不应该有依赖,在调用一个请求前,不需要一定要去提前调用另外一个请求.Rest里面不应该有 session,特别是Rest请求不应该保存信息在sesssion里,以 ...

  6. 环信即时通讯在工程中的安装——Nusen_Liu

    即时通讯-环信 准备 1.下载SDK http://www.easemob.com/download 2.证书下载上传 后期发送消息 需要推送发送的内容 http://docs.easemob.com ...

  7. iOS中WebSocket的使用

    https://github.com/square/SocketRocket 简单使用如下 1.初始化socket _webSocket = [[SRWebSocket alloc] initWith ...

  8. Saltstack_使用指南08_远程执行-返回程序

    1. 主机规划 salt 版本 [root@salt100 ~]# salt --version salt (Oxygen) [root@salt100 ~]# salt-minion --versi ...

  9. python 逐行读取txt文件

    逐行读取txt文件 path = r'D:\123456\1.txt'with open(path, 'r', encoding='utf-8') as f:    for line in f:   ...

  10. 一些有价值bug产生的思考

    1.异步处理时防止重复点击的逻辑校验 场景 打款请求时,进入异步处理的队列,生成一个任务号,存在如数据库,且状态为未完成.此时,如果并发操作,如重复点击或者重复调用接口,则发出的两条请求可能被分配到不 ...