golang的切片使用总结二

如果没看golang切片的第一篇总结博客 golang的切片使用总结一-CSDN博客 ,请浏览之

举例9:make([]int, a, b)后访问下标a的元素

s := make([]int, 10, 12)
v := s[10]
fmt.Printf("v:%v", v)

打印结果:

panic: runtime error: index out of range [10] with length 10

goroutine 1 [running]:
main.main()

结论:capacity(容量)是物理意义上的,空间归切片s所有;但len(长度)是逻辑意义上的,访问元素时是根据逻辑意义为准,因为s[10]认为是越界访问

举例10:make([]int, a, b)后截取新切片,再对新切片append

s := make([]int, 10, 12)
s1 := s[8:]
s1 = append(s1, []int{10, 11, 12}...)
v := s[10]
fmt.Printf("v:%v", v)

打印结果:

panic: runtime error: index out of range [10] with length 10

goroutine 1 [running]:
main.main()
结论:虽然s1从s截取得到,二者共享同一块内存数据。但是后面的s1 = append(s1)操作会让s1发生扩容,s1扩容后就跟s完全分开了,内存完全独立。所以,s还是原来的len为10,访问s[10]会发生panic

举例11:切片在函数中是值传递还是引用传递

func main() {
    s := make([]int, 10, 12)
    s1 := s[8:]
    changeSlice(s1)
    fmt.Printf("s: %v", s)
}

func changeSlice(s1 []int) {
    s1[0] = -1
}

打印结果:s: [0 0 0 0 0 0 0 0 -1 0]

结论:切片s1是从切片s截取得到,传入函数后,由于切片是引用传递,函数内的s1[0]和函数外的s[8]是同一个元素,所以原切片s会被修改

举例12:切片传递到函数内后进行修改,且append

func main() {
    s := make([]int, 10, 12)
    s1 := s[8:]
    changeSlice(s1)
    fmt.Printf("s:%v,  len of s:%v,  cap of s:%v  \n", s, len(s), cap(s))
    fmt.Printf("changeSlice函数后, s1:%v, len of s1:%v, cap of s1:%v \n", s1, len(s1), cap(s1))
}
func changeSlice(s1 []int) {
    s1[0] = -1
    s1 = append(s1, 10, 11, 12, 13, 14, 15)
    fmt.Printf("changeSlice函数内, s1:%v, len of s1:%v, cap of s1:%v \n", s1, len(s1), cap(s1))
}

打印结果

changeSlice函数内, s1:[-1 0 10 11 12 13 14 15], len of s1:8, cap of s1:8 
s:[0 0 0 0 0 0 0 0 -1 0],  len of s:10,  cap of s:12
changeSlice函数后, s1:[-1 0], len of s1:2, cap of s1:4

结论:虽然切片是引用传递,实际指的是元素数据存储为引用,但切片参数仍然是不同的slice header。有点儿像C++的指针,两个指针指向的数据是同一份地址,但是两个指针本身是不同的。

所以函数changeSlice()内的s1,函数外的s1,旧切片s,三者指向的是同一块数据,一处修改即生效。但是函数changeSlice()内的s1,函数外的s1代表的是两个不同的slice header,函数执行只是修改函数内s1的slice header,函数外面s1的slice header不受影响,长度仍然是2,capacity仍然是4

举例13:多次截取切片后赋值

s := []int{0, 1, 2, 3, 4}
s = append(s[:2], s[3:]...)
fmt.Printf("s:%v, len(s)=%v, cap(s)=%v \n", s, len(s), cap(s))
v := s[4]
fmt.Printf("v=%v", v)

打印结果:

s:[0 1 3 4], len(s)=4, cap(s)=5 
panic: runtime error: index out of range [4] with length 4

goroutine 1 [running]:
main.main()
结论:执行append(s[:2],s[3:]...)后,s中有4个元素,capacity仍然为5,使用下标访问s时使用的是逻辑长度,认为是越界

举例14:切片超过256时,扩容时的公式

s := make([]int, 512)
s = append(s, 1)
fmt.Printf("len(s)=%v,cap(s)=%v", len(s), cap(s))

打印结果:len(s)=513,cap(s)=848

结论:切片中元素超过512时,扩容公式不是直接翻倍,而是每次递增(N/4 + 192),直到值达到需求,其中的192=(3*256)/4

按照上面的公式,512 +(512/4+192) = 832个元素

但是为什么这里容量显示是848呢?这关联到golang的内存对齐

为了更好地进行内存空间对齐,golang 允许产生一些有限的内部碎片,对拟申请空间的 object 进行大小补齐,最终 6656 byte 会被补齐到 6784 byte 的这一档次,各个档次表如下所示:

// class  bytes/obj  bytes/span  objects  tail waste  max waste  min align
//     1          8        8192     1024           0     87.50%          8
//     2         16        8192      512           0     43.75%         16
//     3         24        8192      341           8     29.24%          
// ...
//    48       6528       32768        5         128      6.23%        128
//    49       6784       40960        6         256      4.36%        128

刚才计算出来的832元素,每个int占8个字节,所以832 * 8字节 = 6656字节

所以我们需要6656字节时,根据上面表格,落在6784这一档,golang帮我们申请了6784个字节,

6784字节 / 8字节 = 848个int元素

最终计算得到capacity为848

本篇总结:

1. 切片的capacity可以认为是物理意义上的空间;而len是罗辑意义上的元素个数

2. 根据下标访问切片时,golang的执行的是逻辑判断,不能大于或等于len的值,否则会认为是越界,发生panic

3. 切片在函数参数中传递时是引用传递,但这里的引用指的是存储的数据指向同一份。但函数内外的参数仍然是不同的slice header,就像两个指针一样

4. 切片元素超过256时,切片扩容不再是简单的翻倍,而是有个递增公式,每次增加为N/4+192。但golang申请内存时还有内存对齐的问题,有个档次表。申请内存时,在哪个档则采用这个档的值

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

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

相关文章

老卫带你学---leetcode刷题(76. 最小覆盖子串)

76. 最小覆盖子串 问题: 给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。 注意: 对于 t 中重复字符,我们寻找的子字符串中该字符数量必…

分接开关接触不良导致变压器绕组的不平衡率超标

有载和无载分接开关接触不良的缺陷, 是主变压器各类缺陷中数量最多的一种, 约占 40%。 给变压器安全运行带来很大威胁。 例如:                                                      …

字符串常量池位于JVM哪里

Java6 和6之前,常量池是存放在方法区(永久代)中的。Java7,将常量池是存放到了堆中。Java8 之后,取消了整个永久代区域,取而代之的是元空间。运行时常量池和静态常量池存放在元空间中,而字符串常…

c语言:通讯录管理系统(增删查改)

前言:在大多数高校内,都是通过设计一个通讯录管理系统来作为c语言课程设计,通过一个具体的系统设计将我们学习过的结构体和函数等知识糅合起来,可以很好的锻炼学生的编程思维,本文旨在为通讯录管理系统的设计提供思路和…

雷达散射截面(RCS)相关概念

一、雷达散射截面(RCS) RCS被指定为直径为1.128 m的完美导电球体的倍数。该球体的可见表面为1 m,但仅具有较小的反向散射有效面积。因此,更好的反射表面可以具有比其几何尺寸大得多的RCS。 雷达截面积 二、简单目标的RCS 简单目标的RCS如下表所示: 三、瑞利、米氏和光学…

基于SSM的家庭财务管理系统设计与实现

末尾获取源码 开发语言:Java Java开发工具:JDK1.8 后端框架:SSM 前端:采用JSP技术开发 数据库:MySQL5.7和Navicat管理工具结合 服务器:Tomcat8.5 开发软件:IDEA / Eclipse 是否Maven项目&#x…

SpringBoot banner 样式 自动生成

目录 SpringBoot banner 样式 自动生成 图案网站: 1.第一步创建banner.txt文件 2.访问网站Ascii艺术字实现个性化Spring Boot启动banner图案,轻松修改更换banner.txt文件内容,收集了丰富的banner艺术字和图,并且支持中文banner下…

echarts

1 type值汇总 不同的type的值对应的图表类型如下: type: ‘bar’:柱状/条形图 type: ‘line’:折线/面积图 type: ‘pie’:饼图 type: ‘scatter’:散点(气泡)图 type: ‘effectScatter’&…

react嵌套路由

react嵌套页面 先从路由身上导出 import { HashRouter, Routes, Route, Navigate } from react-router-dom; //引入页面; import Home from ./view/Home; import About from ./view/About; import Integrated from ./view/Integrated; import Sidebar from ./vie…

(高阶) Redis 7 第20讲 数据类型 源码篇

🌹 以下分享 Redis 源码分析,如有问题请指教。🌹🌹 如你对技术也感兴趣,欢迎交流。🌹🌹🌹 如有对阁下帮助,请👍点赞💖收藏🐱‍🏍分享😀 面试题 Redis 跳表了解吗?这个数据结构有啥缺点?Redis 项目中如何使用?Redis数据结构里哦阿姐哪些?布隆过滤…

ansible - Role

1、简介: Ansible 中的角色(Role)是一种组织和封装Playbook的方法,用于管理和组织 Ansible代码。它可以将任务和配置逻辑模块化,以便在不同的Playbook中共享和重用。 2、通过 role 远程部署并配置 nginx (1) 准备目…

数组(数据结构)

优质博文:IT-BLOG-CN 一、简介 数组Array是一种线性表数据结构,它用一组连续的内存空间,存储一组具有相同类型的数据。 数组因具有连续的内存空间的特点,数据拥有非常高效率的“随机访问”,时间复杂度为O(1)。但因要保…

#力扣:9. 回文数@FDDLC

9. 回文数 - 力扣&#xff08;LeetCode&#xff09; 一、Java class Solution {public boolean isPalindrome(int x) {String s Integer.toString(x); // 转换成字符串return new StringBuilder(s).reverse().toString().equals(s);} } 二、C #include <string> #i…

ubuntu使用whisper和funASR-语者分离-二值化

文章目录 一、选择系统1.1 更新环境 二、安装使用whisper2.1 创建环境2.1 安装2.1.1安装基础包2.1.2安装依赖 3测试13测试2 语着分离创建代码报错ModuleNotFoundError: No module named pyannote报错No module named pyannote_whisper 三、安装使用funASR1 安装1.1 安装 Conda&…

黑豹程序员-架构师学习路线图-百科:Database数据库

文章目录 1、什么是Database2、发展历史3、数据库排行网4、总结 1、什么是Database 当今世界是一个充满着数据的互联网世界&#xff0c;各处都充斥着大量的数据。即这个互联网世界就是数据世界。 支撑这个数据世界的基石就是数据库&#xff0c;数据库也可以称为数据的仓库。 …

typescript开发环境搭建

typescript是基于javascript的强类型标记性语言&#xff0c;使用typescript语言可开发出不同规模的、易于扩展的web前端页面应用&#xff0c;本文主要描述typescript的开发环境搭建。 npm install -g typescript 如上所示&#xff0c;在本地开发环境中&#xff0c;使用nodejs…

基于Redis实现消息队列的实践

为什么要基于Redis实现消费队列&#xff1f; 消息队列是一种典型的发布/订阅模式&#xff0c;是专门为异步化应用和分布式系统设计的&#xff0c;具有高性能、稳定性及可伸缩性的特点&#xff0c;是开发分布式系统和应用系统必备的技术之一。目前&#xff0c;针对不同的业务场…

群狼调研(长沙物业第三方评优)开展房地产市场调查内容设计

湖南房地产市场近年来表现出多元化的发展趋势。为了在竞争激烈的市场中获得更好的发展&#xff0c;房地产企业需要密切关注市场变化&#xff0c;合理规划开发项目&#xff0c;同时提高产品质量和服务水平&#xff0c;以满足消费者的需求和期望。群狼调研(长沙神秘顾客调查)在房…

localhost和127.0.0.1都可以访问项目,但是本地的外网IP不能访问

使用localhost和127.0.0.1都可以访问接口&#xff0c;比如&#xff1a; http://localhost:8080/zhgl/login/login-fy-list或者 http://127.0.0.1:8080/zhgl/login/login-fy-list返回json {"_code":10000,"_msg":"Success","_data":…

数组在TypeScript中是如何工作的

在 TypeScript 中&#xff0c;数组是一种特殊的数据类型&#xff0c;用于存储多个相同类型的值。它提供了一种有效的方式来组织和访问多个数据。 数组声明和初始化&#xff1a;可以使用以下方式声明和初始化数组。 // 声明并初始化一个字符串数组 let fruits: string[] [&quo…