Tony Bai · Go 语言第一课
Tony Bai
资深架构师,tonybai.com 博主
21492 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 59 讲
开篇词 (1讲)
结束语 (1讲)
Tony Bai · Go 语言第一课
15
15
1.0x
00:00/00:00
登录|注册

15|同构复合类型:从定长数组到变长切片

解除绑定关系
新数组长度规律
动态分配新数组
cap
len
array
稀疏数组初始化
忽略长度
显式初始化
append操作
基于切片创建切片
数组切片化
make函数
三元组结构
动态扩容
不定长
逐维拆解
初始化方式
var arr [N]T
由同构类型元素组成的连续序列
长度固定
动态扩容
创建方式
实现原理
特性
多维数组
声明与初始化
特性
切片
数组
复合数据类型

该思维导图由 AI 生成,仅供参考

你好,我是 Tony Bai。
在前面的学习中,我们详细讲解了 Go 基本数据类型,主要包括数值类型与字符串类型。但是,仅仅学习这些基本数据类型建立的抽象概念,还远不足以让我们应对真实世界的各种问题。
比如,我们要表示一组数量固定且连续的整型数,建立一个能表示书籍的抽象数据类型,这个类型中包含书名、页数、出版信息等;又或者,我们要建立一个学号与学生姓名的映射表等。这些问题基本数据类型都无法解决,所以我们需要一类新类型来建立这些抽象,丰富 Go 语言的表现力。
这种新类型是怎么样的呢?我们可以通过这些例子总结出新类型的一个特点,那就是它们都是由多个同构类型(相同类型)或异构类型(不同类型)的元素的值组合而成的。这类数据类型在 Go 语言中被称为复合类型。
从这一节课开始,我们就来讲解 Go 语言的复合类型。Go 语言原生内置了多种复合数据类型,包括数组、切片(slice)、map、结构体,以及像 channel 这类用于并发程序设计的高级复合数据类型。那么这一节课,我们先来学习一下最简单的复合类型:数组,以及与数组有着密切关系的切片。
下面我们就先从最基础的复合数据类型,数组开始。

数组有哪些基本特性?

我们先来看数组类型的概念。Go 语言的数组是一个长度固定的、由同构类型元素组成的连续序列。通过这个定义,我们可以识别出 Go 的数组类型包含两个重要属性:元素的类型数组长度(元素的个数)。这两个属性也直接构成了 Go 语言中数组类型变量的声明:
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

Go语言中的切片类型是一种灵活的复合数据类型,它弥补了数组固定长度和传值开销较大的不足。切片与数组类似,但具有动态长度和动态扩容的特性,使其成为Go语言中最常用的同构复合类型之一。切片在运行时实际上是一个三元组结构,包含指向底层数组的指针、切片长度和切片容量。通过内置函数append和切片切片化语法,可以动态地向切片中添加元素或基于已存在的数组创建切片。切片的灵活性使其成为数组的“描述符”,在函数参数传递时避免较大性能开销。此外,多个切片可以共享同一底层数组,对底层数组的操作会反映到其他切片中。切片的动态扩容机制使其成为Go语言中重要的数据类型,为开发者提供了更为便利和高效的数据处理方式。 文章还介绍了切片的动态扩容过程,以及动态扩容可能导致的“绑定关系”解除问题。此外,文章还对数组和切片的特点进行了对比,强调了切片作为数组“描述符”的轻量性和动态扩容的优势。最后,文章提出了一个思考题,期待读者对两个切片变量的差异进行描述。 总之,本文通过深入浅出的方式介绍了Go语言中切片类型的特点和优势,对于想要快速了解切片在Go语言中的应用和特性的读者来说,是一篇值得阅读的文章。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Tony Bai · Go 语言第一课》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(43)

  • 最新
  • 精选
  • Darren
    课后题测试代码如下: var sl1 []int var sl2 = []int{} fmt.Print("========基本区别=========\n") fmt.Printf("%v,len:%d,cap:%d,addr:%p\n", sl1, len(sl1), cap(sl1), &sl1) fmt.Printf("%v,len:%d,cap:%d,addr:%p\n", sl2, len(sl2), cap(sl2), &sl2) fmt.Printf("sl1==nil:%v\n", sl1 == nil) fmt.Printf("sl2==nil:%v\n", sl2 == nil) a1 := *(*[3]int)(unsafe.Pointer(&sl1)) a2 := *(*[3]int)(unsafe.Pointer(&sl2)) fmt.Print("========底层区别=========\n") fmt.Println(a1) fmt.Println(a2) type SliceDemo struct { Values []int } var s1 = SliceDemo{} var s2 = SliceDemo{[]int{}} bs1, _ := json.Marshal(s1) bs2, _ := json.Marshal(s2) fmt.Print("========序列化区别=========\n") fmt.Println(a1) fmt.Println(string(bs1)) fmt.Println(string(bs2)) ========基本区别========= [],len:0,cap:0,addr:0xc0000a6018 [],len:0,cap:0,addr:0xc0000a6030 sl1==nil:true sl2==nil:false ========底层区别========= [0 0 0] [18601168 0 0] ========序列化区别========= [0 0 0] {"Values":null} {"Values":[]} 可以看到,日常的使用基本是没有区别的,只不过与nil的比较,以及底层数据结构和序列化还是有一定的区别的。 同时go官方推荐使用 var sl1 []int The former declares a nil slice value, while the latter is non-nil but zero-length. They are functionally equivalent—their len and cap are both zero—but the nil slice is the preferred style. 在goland开发时,第二种声明方式会出现黄色下划线,提示需要改动。

    作者回复: 思考题完成的很细致,很全面。手动点赞!

    2021-11-15
    7
    86
  • 在下宝龙、
    var sl1 []int var sl2 = []int{} s1是声明,还没初始化,是nil值,底层没有分配内存空间。 s2初始化了,不是nil值,底层分配了内存空间,有地址。 我是这么理解的。

    作者回复: 正确✅

    2021-11-15
    49
  • trietree
    sl1未初始化,值为nil,和nil比较返回true sl2初始化为empty slice,和nil比较返回false

    作者回复: 正确✅

    2021-11-15
    15
  • 风铃
    个人感觉,在初始化切片的时候,最好的分配项目的需求,分配一定的容量(cap),要不在切片里面的数据多了,每次进行扩容,会消耗大量的内存性能

    作者回复: 👍。

    2022-02-23
    3
    9
  • 有个疑问,切片的底层数组,在切片发生自动扩容后,在物理空间上还是连续的吗?

    作者回复: 扩容是新分配一段连续的大点的内存,原先的内存块不要了。所以依旧是连续的。

    2022-01-28
    5
    8
  • 笑忘日月星辰
    老师好,关于扩容,麻烦解惑下 问题:扩容当小于1024时候,是扩容为当前的2倍;当大于1024小于1280时候扩容为1.25倍,当大于1280小于1696时候,扩容为1.325倍吗?这个扩容的规则是什么? func main() { var s []int for i := 0; i < 2048; i++ { s = append(s, i) fmt.Println(len(s), cap(s)) } } 打印结果 --------------------------------------------------- 512 512 513 1024 ... 1024 1024 1025 1280 ... 1280 1280 1281 1696 ... 1696 1696 1697 2304 ...

    作者回复: 你用的是什么版本的go编译器,go 1.18,如果是go 1.18,那么可以看https://mp.weixin.qq.com/s/4wYrwBwnuylSvTaxBMXUgg ,go 1.18对slice的扩容算法了调整。

    2022-06-25
    5
  • 左卫康
    思考题:var sl1 []int 仅声未初始化,值为对应类型的零值,nil var sl2 = []int{} 声明并初始化,是个空切片

    作者回复: ✅

    2022-08-05归属地:北京
    3
  • 和白白
    var sl1 []int 不显示初始化,所以 sl1 对应 slice 的零值 nil,并且此时没有 ptr、len 和 cap var sl2 = []int{} 显示初始化,sl2 对应 [] 空数组,ptr 指定空数组的地址,len 和 cap 都是 0

    作者回复: ✅

    2022-03-07
    3
  • 布凡
    老师,请问下为什么go没有class 这个类型呢?是因为想要开发者多用组合少用继承的设计理念吗?还是有其它原因呢?

    作者回复: class是面向对象语言的专有名词。go定位就不是oo语言,所以没有class。

    2021-11-15
    3
    3
  • Aeins
    sl1 自身有分配内存(能取地址),底层数组没有分配内存。为什么值会是 nil 呢?

    作者回复: 首先是否为nil与自身是否能取地址无关。 var p *int = nil println(&p) // 这取地址也是可以的。 切片是复合数据类型,如果其没有被初始化,那么go语言规定其默认的零值就为nil。

    2022-05-26
    2
收起评论
显示
设置
留言
43
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部