《快学 Go 语言》第 5 课 —— 神奇的切片

不用配对数据也能玩转低照度增强?手把手带你复现CVPR 2020的Zero-DCE算法(附PyTorch代码) 本文详细解析了CVPR 2020提出的Zero-DCE算法,这是一种无需配对数据的低照度图像增强方法。通过创新的自监督学习框架和精心设计的损失函数系统,Zero-DCE在手机摄影和安防监控等场景中展现出卓越性能,显著提升低光环境下的图像质量。文章还提供了完整的PyTorch实现代码和部署优化建议。 阅读详情

切片无疑是 Go 语言中最重要的数据结构,也是最有趣的数据结构,它的英文词汇叫 slice。所有的 Go 语言开发者都津津乐道地谈论切片的内部机制,它也是 Go 语言技能面试中面试官最爱问的知识点之一。初级用户很容易滥用它,这小小的切片想要彻底的理解它是需要花费一番功夫的。在使用切片之前,我觉得很有必要将切片的内部结构做一下说明。

学过 Java 语言的人会比较容易理解切片,因为它的内部结构非常类似于 ArrayList,ArrayList 的内部实现也是一个数组。当数组容量不够需要扩容时,就会换新的数组,还需要将老数组的内容拷贝到新数组。ArrayList 内部有两个非常重要的属性 capacity 和 length。capacity 表示内部数组的总长度,length 表示当前已经使用的数组的长度。length 永远不能超过 capacity。

640?wx_fmt=png
图片

上图中一个切片变量包含三个域,分别是底层数组的指针、切片的长度 length 和切片的容量 capacity。切片支持 append 操作可以将新的内容追加到底层数组,也就是填充上面的灰色格子。如果格子满了,切片就需要扩容,底层的数组就会更换。

形象一点说,切片变量是底层数组的视图,底层数组是卧室,切片变量是卧室的窗户。通过窗户我们可以看见底层数组的一部分或全部。一个卧室可以有多个窗户,不同的窗户能看到卧室的不同部分。

切片的创建

切片的创建有多种方式,我们先看切片最通用的创建方法,那就是内置的 make 函数

package main

import "fmt"

func main() {
 var s1 []int = make([]int, 5, 8)
 var s2 []int = make([]int, 8) // 满容切片
 fmt.Println(s1)
 fmt.Println(s2)
}

-------------
[0 0 0 0 0]
[0 0 0 0 0 0 0 0]

package main

import "fmt"

func main() {
 var s1 = make([]int, 5, 8)
 s2 := make([]int, 8)
 fmt.Println(s1)
 fmt.Println(s2)
}

-------------
[0 0 0 0 0]
[0 0 0 0 0 0 0 0]

切片的初始化

使用 make 函数创建的切片内容是「零值切片」,也就是内部数组的元素都是零值。Go 语言还提供了另一个种创建切片的语法,允许我们给它赋初值。使用这种方式创建的切片是满容的。

package main

import "fmt"

func main() {
 var s []int = []int{1,2,3,4,5}  // 满容的
 fmt.Println(s, len(s), cap(s))
}

---------
[1 2 3 4 5] 5 5

空切片

在创建切片时,还有两个非常特殊的情况需要考虑,那就是容量和长度都是零的切片,叫着「空切片」,这个不同于前面说的「零值切片」。

package main

import "fmt"

func main() {
 var s1 []int
 var s2 []int = []int{}
 var s2 []int = make([]int, 0)
 fmt.Println(s1, s2, s3)
 fmt.Println(len(s1), len(s2), len(s3))
 fmt.Println(cap(s1), cap(s2), cap(s3))
}

-----------
[] [] []
0 0 0
0 0 0

切片的赋值

切片的赋值是一次浅拷贝操作,拷贝的是切片变量的三个域,你可以将切片变量看成长度为 3 的 int 型数组,数组的赋值就是浅拷贝。拷贝前后两个变量共享底层数组,对一个切片的修改会影响另一个切片的内容,这点需要特别注意。

package main

import "fmt"

func main() {
 var s1 = make([]int, 5, 8)
 // 切片的访问和数组差不多
 for i := 0; i < len(s1); i++ {
  s1[i] = i + 1
 }
 var s2 = s1
 fmt.Println(s1, len(s1), cap(s1))
 fmt.Println(s2, len(s2), cap(s2))

 // 尝试修改切片内容
 s2[0] = 255
 fmt.Println(s1)
 fmt.Println(s2)
}

--------
[1 2 3 4 5] 5 8
[1 2 3 4 5] 5 8
[255 2 3 4 5]
[255 2 3 4 5]

切片的遍历

切片在遍历的语法上和数组是一样的,除了支持下标遍历外,那就是使用 range 关键字

package main


import "fmt"


func main() {
    var s = []int{1,2,3,4,5}
    for index := range s {
        fmt.Println(index, s[index])
    }
    for index, value := range s {
        fmt.Println(index, value)
    }
}

--------
0 1
1 2
2 3
3 4
4 5
0 1
1 2
2 3
3 4
4 5

切片的追加

文章开头提到切片是动态的数组,其长度是可以变化的。什么操作可以改变切片的长度呢,这个操作就是追加操作。切片每一次追加后都会形成新的切片变量,如果底层数组没有扩容,那么追加前后的两个切片变量共享底层数组,如果底层数组扩容了,那么追加前后的底层数组是分离的不共享的。如果底层数组是共享的,一个切片的内容变化就会影响到另一个切片,这点需要特别注意。

package main

import "fmt"

func main() {
 var s1 = []int{1,2,3,4,5}
 fmt.Println(s1, len(s1), cap(s1))

 // 对满容的切片进行追加会分离底层数组
 var s2 = append(s1, 6)
 fmt.Println(s1, len(s1), cap(s1))
 fmt.Println(s2, len(s2), cap(s2))

 // 对非满容的切片进行追加会共享底层数组
 var s3 = append(s2, 7)
 fmt.Println(s2, len(s2), cap(s2))
 fmt.Println(s3, len(s3), cap(s3))
}

--------------------------
[1 2 3 4 5] 5 5
[1 2 3 4 5] 5 5
[1 2 3 4 5 6] 6 10
[1 2 3 4 5 6] 6 10
[1 2 3 4 5 6 7] 7 10

package main

import "fmt"

func main() {
 var s1 = []int{1,2,3,4,5}
 append(s1, 6)
 fmt.Println(s1)
}

--------------
./main.go:7:8: append(s1, 6) evaluated but not used

package main

import "fmt"

func main() {
 var s1 = []int{1,2,3,4,5}
 _ = append(s1, 6)
 fmt.Println(s1)
}

----------
[1 2 3 4 5]

切片的域是只读的

我们刚才说切片的长度是可以变化的,为什么又说切片是只读的呢?这不是矛盾么。这是为了提醒读者注意切片追加后形成了一个新的切片变量,而老的切片变量的三个域其实并不会改变,改变的只是底层的数组。这里说的是切片的「域」是只读的,而不是说切片是只读的。切片的「域」就是组成切片变量的三个部分,分别是底层数组的指针、切片的长度和切片的容量。这里读者需要仔细咀嚼。

切割切割

到目前位置还没有说明切片名字的由来,既然叫着切片,那总得可以切割吧。切割切割,有些男娃子听到这个词汇时身上会起鸡皮疙瘩。切片的切割可以类比字符串的子串,它并不是要把切片割断,而是从母切片中拷贝出一个子切片来,子切片和母切片共享底层数组。下面我们来看一下切片究竟是如何切割的。

package main

import "fmt"

func main() {
 var s1 = []int{1,2,3,4,5,6,7}
 // start_index 和 end_index,不包含 end_index
 // [start_index, end_index)
 var s2 = s1[2:5] 
 fmt.Println(s1, len(s1), cap(s1))
 fmt.Println(s2, len(s2), cap(s2))
}

------------
[1 2 3 4 5 6 7] 7 7
[3 4 5] 3 5

640?wx_fmt=png
图片

我们注意到子切片的内部数据指针指向了数组的中间位置,而不再是数组的开头了。子切片容量的大小是从中间的位置开始直到切片末尾的长度,母子切片依旧共享底层数组。

子切片语法上要提供起始和结束位置,这两个位置都可选的,不提供起始位置,默认就是从母切片的初始位置开始(不是底层数组的初始位置),不提供结束位置,默认就结束到母切片尾部(是长度线,不是容量线)。下面我们看个例子

package main

import "fmt"

func main() {
 var s1 = []int{1, 2, 3, 4, 5, 6, 7}
 var s2 = s1[:5]
 var s3 = s1[3:]
 var s4 = s1[:]
 fmt.Println(s1, len(s1), cap(s1))
 fmt.Println(s2, len(s2), cap(s2))
 fmt.Println(s3, len(s3), cap(s3))
 fmt.Println(s4, len(s4), cap(s4))
}

-----------
[1 2 3 4 5 6 7] 7 7
[1 2 3 4 5] 5 7
[4 5 6 7] 4 4
[1 2 3 4 5 6 7] 7 7

package main

import "fmt"

func main() {
 var s = make([]int, 5, 8)
 for i:=0;i<len(s);i++ {
  s[i] = i+1
 }
 fmt.Println(s, len(s), cap(s))

 var s2 = s
 var s3 = s[:]
 fmt.Println(s2, len(s2), cap(s2))
 fmt.Println(s3, len(s3), cap(s3))

 // 修改母切片
 s[0] = 255
 fmt.Println(s, len(s), cap(s))
 fmt.Println(s2, len(s2), cap(s2))
 fmt.Println(s3, len(s3), cap(s3))
}

-------------
[1 2 3 4 5] 5 8
[1 2 3 4 5] 5 8
[1 2 3 4 5] 5 8
[255 2 3 4 5] 5 8
[255 2 3 4 5] 5 8
[255 2 3 4 5] 5 8

数组变切片

对数组进行切割可以转换成切片,切片将原数组作为内部底层数组。也就是说修改了原数组会影响到新切片,对切片的修改也会影响到原数组。

package main

import "fmt"

func main() {
    var a = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    var b = a[2:6]
    fmt.Println(b)
    a[4] = 100
    fmt.Println(b)
}

-------
[3 4 5 6]
[3 4 100 6]

copy 函数

Go 语言还内置了一个 copy 函数,用来进行切片的深拷贝。不过其实也没那么深,只是深到底层的数组而已。如果数组里面装的是指针,比如 []*int 类型,那么指针指向的内容还是共享的。

func copy(dst, src []T) int

package main

import "fmt"

func main() {
 var s = make([]int, 5, 8)
 for i:=0;i<len(s);i++ {
  s[i] = i+1
 }
 fmt.Println(s)
 var d = make([]int, 2, 6)
 var n = copy(d, s)
 fmt.Println(n, d)
}
-----------
[1 2 3 4 5]
2 [1 2]

切片的扩容点

当比较短的切片扩容时,系统会多分配 100% 的空间,也就是说分配的数组容量是切片长度的2倍。但切片长度超过1024时,扩容策略调整为多分配 25% 的空间,这是为了避免空间的过多浪费。试试解释下面的运行结果。

s1 := make([]int, 6)
s2 := make([]int, 1024)
s1 = append(s1, 1)
s2 = append(s2, 2)
fmt.Println(len(s1), cap(s1))
fmt.Println(len(s2), cap(s2))
-------------------------------------------
7 12
1025 1344

下一节我们要讲 Go 语言另一个非常重要的数据结构 —— 字典

640?wx_fmt=jpeg

扫一扫二维码阅读《快学 Go 语言》更多章节

已经关注「码洞」的读者请点击下面的广告,就等于是在赞助我了

GO语言基础教程(56)Go切片之len()函数和cap()函数:Go切片精髓:len()和cap()函数深度解密 本文将深入探讨Go语言中切片的len()和cap()函数,通过生动比喻和实用示例,解析长度与容量的概念、区别及实际应用。掌握这些知识,能让你在Go开发中游刃有余,避免常见陷阱,编写出更高效可靠的代码。 阅读详情

相关推荐

OS实验xv6 6.S081 开坑

OS实验xv6 6.S081 开坑 终于把xv6 2019 6.S081做完了,网络部分没有写,因为有计网实验了。怎么说呢,xv6的坑有点多,能做完多亏了:和我同学ljj交流,参考RedemptionC老哥的博客(他写的非常详细,遗憾的是,他好像没有写mmap),再者就是参考这位老哥的做法(没有源码,写得不是特别详细,不过有借鉴作用,mmap就是借鉴了他的思想)……废话不多说了,直接开始。 实验指导 首推RedemptionC的博客 这位老哥的个人博客 xv6 6.S081官网(后面的实验都要参考官网

不定期更新系统使用、编程小技巧,哔哩哔哩账号:https://space.bilibili.com/399455629,欢迎关注 4493

GO语言slice——神奇的切片

切片是个啥 切片无疑是 Go 语言中最重要的数据结构,也是最有趣的数据结构,它的英文词汇叫 slice。所有的 Go 语言开发者都津津乐道地谈论切片的内部机制,它也是 Go 语言技能面试中面试官最爱问的知识点之一。初级用户很容易滥用它,这小小的切片想要彻底的理解它是需要花费一番功夫的。在使用切片之前,我觉得很有必要将切片的内部结构做一下说明。 学过 Java 语言的人会比较容易理解切片,因为它的内部结构非常类似于 ArrayList,ArrayList 的内部实现也是一个数组。当数组容量不够需要扩容时,就会

weixin_43844941的博客 731

STM32驱动AS5600磁性旋转位置传感器模块

文章目录简介引脚定义I2C接口写数据读数据从指定的地址开始读数据驱动程序cubeMX配置驱动程序`bsp_as5600.h``bsp_as5600.c`测试程序参考资料 简介 AS5600 是一款易于编程的磁性旋转位置传感器,具有高分辨率 12 位模拟或 PWM 输出。 这种非接触式系统测量径向磁化轴上磁铁的绝对角度。 该 AS5600 专为非接触式电位计应用而设计,其坚固的设计消除了任何同质外部杂散磁场的影响。 引脚定义 I2C接口 7位设备地址为0x36(二进制表示为0110110) 支持标准模式和

ApexYuan 1万+

golang学习之三:Go语言切片面试真题8连问

Go语言切片面试真题8连问01. 数组和切片有什么区别?02. 拷贝大切片一定比拷贝小切片代价大吗?03. 切片的深浅拷贝04. 零切片、空切片、nil切片是什么零切片nil切片空切片05. 切片的扩容策略07. 参数传递切片和切片指针有什么区别?08. range遍历切片有什么要注意的?总结 原文链接:传送门 01. 数组和切片有什么区别? Go语言中数组是固定长度的,不能动态扩容,在编译期就会确定大小,声明方式如下: var buffer [255]int buffer := [255]int{0}

java_xxxx的博客 813

射频捷变收发器(RF Agile Transceiver) 国内外产品的现状、发展趋势及对比分析2

由于通信系统的研究、设计和实现的巨大进步,RF(射频)设备的新时代正在到来。特别是,随着通信、航空航天和防务以及高速仪器仪表、物联网(IoT)和协作传感器网络(CSN)的发展,以实现宽带高速通信,移动通信中频谱上不断增加的数据量要求而采用新颖的解决方案,以动态方式满足流量交换。然而,当前最先进的技术和设备需要一个合理的成熟时间来满足当前和即将到来的通信标准。

legendddh的博客 2614

KALI虚拟机挂代理教程

需要你本地挂了代理,然后通过虚拟机连接本机代理端口,不说废话了,直接说怎么做了。 1.查看选项设置中自己的小飞机的本地端口是多少:(默认是1080) 2.本机win+r打开cmd,输入ipconfig查看本地ip,我的本地ip是192.168.0.107: 2.打开kali虚拟机,编辑ProxyChains配置文件: vi /etc/proxychains.conf 3.在文件末尾的ProxyList中添加物理机本地ip和1080端口作为代理地址: 4.保存并退出即可: esc :wq 回车 5.

qq_34853514的博客 3万+

Go语言切片面试真题8连问

前言哈喽,大家好,我是asong。最近没事在看八股文,总结了几道常考的切片八股文,以问答的方式总结出来,希望对正在面试的你们有用~本文题目不全,关于切片的面试真题还有哪些?欢迎评论区补充~...

qq_39397165的博客 725

Gazebo手册:【1】gazebo基本操作案例

欢迎来到初学者模块!本模块将引导您了解 Gazebo 的最基本功能。我们将构建一个简单的车辆来演示这些功能。这些教程适用于 Gazebo 新手和/或没有编程或 Linux 经验的人。 每个教程都建立在上一个教程的基础上,因此我们建议您按顺序学习这些教程。 一、何为Gazebo? Gazebo 是一款 3D 动态模拟器,能够准确有效地模拟复杂室内和室外环境中的机器人种群。虽然类似于游戏引擎,但 Gazebo 提供了更高保真度的物理模拟、一套传感器以及用于用户和程序的接口。 1............

gongdiwudu的专栏 2万+

Go 语言切片面试真题 8 连问

前言哈喽,大家好,我是asong。最近没事在看八股文,总结了几道常考的切片八股文,以问答的方式总结出来,希望对正在面试的你们有用~本文题目不全,关于切片的面试真题还有哪些?欢迎评论区补充~...

EDDYCJY的博客 665

第9.3章 从CMake基础到自动驾驶实战:构建跨平台C/C++项目的全能指南

《CMake构建实战:项目开发卷》精要指南 本文系统梳理了许宏旭《CMake构建实战》核心内容,涵盖C++构建全流程: 基础构建:详解MSVC/GCC编译器使用、Makefile编写及静态/动态库构建方法 CMake核心:解析语法结构、变量控制、文件操作等关键命令,阐述项目全生命周期管理 实战应用:通过快速排序、手写数字识别项目演示现代CMake实践,特别针对自动驾驶领域给出多传感器融合、SLAM等场景的CMake配置方案 特色价值:本书填补了官方文档实例不足的空白,提供300+代码示例,是掌握工业级C++

行知SLAM 1344

基于matlab的CNN验证码识别系统(UI界面版)

基于matlab的CNN验证码识别系统(UI界面版),CNN验证码识别系统包括数据集处理、模型搭建、模型训练、界面设计与实现

在职AI算法工程师,擅长计算机视觉,YOLO目标检测、分割等,擅长web、pyqt界面可视化,好内容持续更新中,来这里跟大家一起学习,共同进步 487

GoLang之切片7连问

GoLang之切片7连问

weixin_52690231的博客 546

【即插即用模块】注意力篇 | ECCV 2024 | DPB:高低频动态分离+双提示融合,PSNR直冲40+!

本文提出了一种基于频率提示的图像恢复方法FPro,通过动态滤波器分离输入特征的高低频分量,并采用双提示块(DPB)进行频域专属提示生成与调制。DPB包含低频提示调制器(LPM)和高频提示调制器(HPM),分别处理不同频段信号,通过生成提示组件并融合解码器特征来优化恢复效果。实验表明,该方法在U型结构中表现优异,尤其适用于分割、底层视觉和多模态任务。模块通过动态无监督频率分离、频域专属提示生成和适配性融合机制,有效解决了传统方法对频域信息关注不足的问题,显著提升了图像恢复质量。

主要更新底层视觉(去噪、超分等)相关的科研内容,形式为【论文精读】+【论文复现】 149

Go语言中的切片类型(slice)

GO语言中的切片切片和数组切片的创建切片的长度遍历切片截取部分切片/数组元素添加元素删除元素二维切片和切片的底层 切片和数组 数组长度固定,切片可以添加元素,都是保存多个相同类型的元素,切片的底层由数组实现。 切片的创建 // 方法一 var hello []int // 声明切片 hello = make([]int,10) // 创建一个长度为10的切片,本质上应该是分配内存空间 // 方法二 hello := make([]int,10) // 也可以直接一步完成 // 方法三 hello

weixin_44562809的博客 718

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

【GO语言卵细胞级别教程】09.切片的超能力(含习题)

切片是Go语言中的一项强大功能,它不仅能够灵活地操作数据,还能让你的代码达到令人瞩目的性能。本文将揭示切片的神奇之处,以及如何利用它们在开发中获得无限可能。从切片的定义到高级应用,我们将深入探讨切片与数组的区别,并分享一些独家技巧和最佳实践。无论你是新手还是经验丰富的开发者,本文都将帮助你释放切片的威力,让你的代码瞬间提升到一个全新的水平!准备好迎接切片的变革了吗?

5万+

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

go语言学习(十、数组、切片、字典)

数组是具有相同唯一类型的一组已编号且长度固定的数据项序列,这种类型可以是任意的原始类型例如整形、字符串或者自定义类型 package main import ( "fmt" "reflect" ) func main() { /* 数组声明 */ // 数组声明格式 TypeOf输出变量类型 // 结果输出:[10]int var tArray [10] int fmt.Println(reflect.TypeOf(tArray)) // 不指定数组元素长度 让编译器自己识别 /

纸上得来终觉浅 绝知此事要躬行 407

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

Go 切片面试真题八连问

前言最近没事在看八股文,总结了几道常考的切片八股文,以问答的方式总结出来,希望对正在面试的你们有用~本文题目不全,关于切片的面试真题还有哪些?欢迎评论区补充~01. 数组和切片有什么区别?Go语言中数组是固定长度的,不能动态扩容,在编译期就会确定大小,声明方式如下:varbuffer[255]int buffer:=[255]int{0}切片是对数组的抽象,因为数...

kevin_tech的博客,微信搜「网管叨bi叨」 290

go 切片去掉第一个_Go语言很好很强大,但我有几个问题想吐槽

Go 是一门非常不错的编程语言,并且逐渐取代 Python 成为很多人的首选语言。但它也有一些缺点让很多开发者忍不住吐槽,比如它在函数式编程、通道 / 并行切片处理、内存垃圾回收、错误处理等方面都有一些问题。本文作者将 Go 存在的“硬伤”设计记录了下来,与大家分享、讨论。Go 是一门非常不错的编程语言。然而,我在公司的 Slack 编程频道中对 Go 的抱怨却越来越多(猜到我是做啥了的吧?),因...

weixin_33553272的博客 298

go int 转切片_一文掌握GO语言实战技能(二)

Go 数组Go 切片Go 变量和内存地址Go Map 类型Go 面向对象编程Go 方法的定义GO 数组数组是同一类型的元素集合。Go中的数组下标从0开始,因此长度为n的数组下标范围是[0, n-1]。整数数组中元素默认初始化为0,字符串数组中的元素默认初始化为""。vara[3]inta[0]=10a[1]=20vara[3]int=[3]int{10,20...

weixin_29299807的博客 235

为什么go语言还没有火起来?go语言有哪些劣势?

Go 是一门非常不错的编程语言,并且逐渐取代 Python 成为很多人的首选语言。但它也有一些缺点让很多开发者忍不住吐槽,比如它在函数式编程、通道 / 并行切片处理、内存垃圾回收、错误处理等方面都有一些问题。本文作者将 Go 存在的“硬伤”设计记录了下来,与大家分享、讨论。 Go 是一门非常不错的编程语言。然而,我在公司的 Slack 编程频道中对 Go 的抱怨却越来越多(猜到我是做啥了的吧?)...

xx666zz的博客 9188
上一篇: Go 语言切片的三种特殊状态 —— 90% 的开发者都忽视了
下一篇: 《快学 Go 语言》第 2 课 —— 变量什么的最讨厌了
码洞
博客等级 码龄10年 41粉丝 137原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值