内容简介:在Golang中,数组与切片是两个概念,数组用的比较少,切片用的比较多,也更为常见,理解了它们之间的关系,能够帮助我们减少一些奇奇怪怪的问题。一切还要从这张图讲起..这张图中说明了数组和切片的核心区别,同时也是本文讲解的重点:
在Golang中,数组与切片是两个概念,数组用的比较少,切片用的比较多,也更为常见,理解了它们之间的关系,能够帮助我们减少一些奇奇怪怪的问题。
一切还要从这张图讲起..
这张图中说明了数组和切片的核心区别,同时也是本文讲解的重点:
1.数组的声明需要指定长度,而切片可指定也可不指定。
2.切片本质是对数组一个引用,切片改变,数组也会改变。
3.切片包含三个部分:数组引用、长度属性、容量属性。长度属性指的是切片的元素数量,决定了可读取数据的上限;而容量属性指的切片的最大元素数量,决定切片了扩展的上限。
1.数组和切片的声明
// 数组的声明 var arr [3]int arr[0] = 1 // 切片的声明,通过var关键字 var slic []int // 切片的声明,通过make关键字 slic := make([]int, 0, 0) 复制代码
2.切片是对数组的引用
func main() {
// 底层数组声明
names := [4]string{
"John",
"Paul",
"George",
"Ringo",
}
fmt.Println(names)
// 获取切片,a,b共享一个数组
a := names[0:2]
b := names[1:3]
fmt.Println(a, b)
// 改变切片b,会改变底层数组,所以a也变了
b[0] = "XXX"
fmt.Println(a, b)
fmt.Println(names)
}
复制代码
3.切片的长度与容量
切片长度属性指的是切片的元素数量,决定了可读取数据的上限;而容量属性指的切片的最大元素数量,决定切片了扩展的上限
长度的计算: 即切片的元素数量,这个比较好理解。
容量的计算: 从切片的第一个元素起,到底层数组的最后一个元素,即为切片的容量。
栗子一:
func sliceLenAndCap() {
names := [6]string{
"John",
"Paul",
"George",
"Ringo",
"Richer",
"Amy",
}
a := names[2:4]
fmt.Println(a)
// 切片a包含George和Ringo元素,所以长度为2
fmt.Println(len(a))
// 从切片的第一个元素算起,即George,底层包含的元素是George到Amy,有4个,所以cap为4
fmt.Println(cap(a))
}
复制代码
长度和容量在声明切片时的作用,栗子二:
// 声明了一个长度和容量都为0的切片
// 与 num1 := []int{} 等价
var num1 []int
// 声明了一个长度为0,容量为3的切片,其底层数组包含三个元素,值都是0
num1 := make([]int, 0, 3)
// 声明一个长度为1,容量为3的切片,可以看到其元素值是0
num2 := make([]int, 1, 3)
fmt.Println(num2) // [0]
复制代码
切片的容量属性:单凭一个长度属性,Go编译器无法知道是否需要对底层数组进行扩容,所以还需要一个容量属性辅助判断。
栗子三:
func sliceAddElem() {
// 一开始容量为2,即底层数组只有两个元素
num1 := make([]int, 0, 2)
fmt.Printf("num1 %p", num1) // 0xc000020090
// 添加元素时,发现添加的元素数量超过底层数组的长度,所以创建了一个新的数组,并修改切片的引用,让它指向新数组
num1 = append(num1, 1, 2, 3, 4) // 0xc000018140
fmt.Printf("num1 %p", num1)
}
复制代码
4.切片和数组的注意事项
4.1 切片再强调
- 切片是对数组的引用,所以它本身并不会存储元素
- append等方法针对的对象是切片,不是数组,需小心
4.2 Golang数组和 C语言 数组的区别
1.C语言的数组名是一个元素的地址,代表指针型常量,所以不能将一个数组赋值给另一个数组;而 Go 的数组是一个值,所以可以赋给另外一个数组,且赋值时是拷贝所有元素到新数组中。
func arrayCopy() {
arr1 := [3]int{1, 2, 3}
arr2 := arr1
fmt.Println(arr2) // [1 2 3]
arr1[0] = -1
fmt.Println(arr1) // [-1 2 3]
fmt.Println(arr2) // [1 2 3]
}
复制代码
2.在Golang中,如果将一个数组传入某个函数,在函数内部接收到的是数组的副本而非指针。如果想传入指针,有两种做法,一是显式加上指针,第二就是传递切片(推荐做法)
func elemAdd(arr [3]int) {
for idx, _ := range arr{
arr[idx] += 1
}
fmt.Println("new", arr) // [2, 3, 4]
}
func elemAddTest() {
num1 := [3]int{1, 2, 3}
elemAdd(num1)
fmt.Println(num1) // [1, 2, 3]
}
// 1.显式加上指针
func elemAddByPoint(arr *[3]int) {
for idx, _ := range arr{
arr[idx] += 1
}
fmt.Println("new", arr) // &[2, 3, 4]
}
func elemAddByPointTest() {
num1 := [3]int{1, 2, 3}
elemAddByPoint(&num1)
fmt.Println(num1) // [2, 3, 4]
}
// 2.传递切片
func elemAddBySlice(arr []int) {
for idx, _ := range arr{
arr[idx] += 1
}
fmt.Println("new", arr) // [2, 3, 4]
}
func elemAddBySliceTest() {
num1 := []int{1, 2, 3}
elemAddBySlice(num1)
fmt.Println(num1) // [2, 3, 4]
}
复制代码
4.3 之前遇到的一个小坑
func sliceTest() {
// 声明两个切片,一开始两个切片的地址是不一样的
var t = make([]int, 0, 10)
var s = make([]int, 0, 10)
fmt.Printf("addr:%p \tlen:%v content:%v\n", t, len(t), t); // addr:0xc00009a000 len:0 content:[]
fmt.Printf("addr:%p \tlen:%v content:%v\n", s, len(s), s); // addr:0xc00009a050 len:0 content:[]
t = append(s, 1, 2, 3, 4)
// 后面t和s的地址是一样的,但是指向的值却不一样
fmt.Printf("addr:%p \tlen:%v content:%v\n", t, len(t), t); // addr:0xc00009a050 len:4 content:[1 2 3 4]
fmt.Printf("addr:%p \tlen:%v content:%v\n", s, len(s), s); // addr:0xc00009a050 len:0 content:[]
}
复制代码
这是因为 t = append(s, 1, 2, 3, 4) 这条语句向切片s的底层数组增加了四个元素(仅仅是底层数组变了),并让t指向这个底层数组,所以t和s的地址一样。而且由于新切片被赋值给t,而不是s,所以t的内容是 [1, 2, 3, 4] ,s的内容是 []
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
父与子的编程之旅
桑德 (Warren Sande)、桑德 (Carter Sande) / 苏金国、易郑超 / 人民邮电出版社 / 2014-10-1 / CNY 69.00
本书是一本家长与孩子共同学习编程的入门书。作者是一对父子,他们以Python语言为例,详尽细致地介绍了Python如何安装、字符串和操作符等程序设计的基本概念,介绍了条件语句、函数、模块等进阶内容,最后讲解了用Python实现游戏编程。书中的语言生动活泼,叙述简单明了。 为了让学习者觉得编程有趣,本书编排了很多卡通人物及场景对话,让学习者在轻松愉快之中跨入计算机编程的大门。 第 2 版新增内......一起来看看 《父与子的编程之旅》 这本书的介绍吧!