在任何一门语言里,数组应该都是非常基础的类型了,使用率当然也应该是很高的。go不但提供了数组,而且还在数组这个类型之上加了一层包装,这个包装也就是slice。
go的数组变量(也就是数组名)是真真实实的数组,而不是一个指向数组内存起始位置的指针,也不能和同类型的指针进行转化,这一点严重不同于C语言; C语言主要还是类型系统太弱了,因此很多的数据最终都退化为指针的方式来访问。
go的数组定义:
var a [10]int var a = [10]int{0,1,2,3,4,5,6,7,8,9} var a = [...]int{0,1,2,3,4,5,6,7,8,9} var a = [2][2]int{[2]int{1,1}, [2]int{2,2}} var a = [2][2]int{{1,1}, {2,2}}
由上面的数组定义可以看出,go的数组类型由两部分组成——类型和长度,二者缺一不可。数组本来就是一块存储相同类型元素的连续内存空间,因此决定一个数组的类型,必然需要决定其存储元素的类型以及存储多少个元素。这一点在所有语言中貌似都是相同的,没有一个异类出现。
前文提到slice是在数组之上进行的一层封装,因此,每个slice的背后都有一个苦逼的数组; 就好比一个嚣张跋扈的女人背后总有一个苦逼的好男人。我觉得Slice也挺嚣张的,总是可以随心所欲的操纵背后的数组。
下面看看Slice究竟长什么样子:
var a = [10]int{0,1,,2,3,4,5,6,7,8,9}
var s1 = a[0:5]
var s2 = a[0:]
var s3 = a[:]
可以看出slice是可以随心所欲的表示一个数组上的一个或者一段元素,更关键的是slice不是一个值类型,而是一个引用类型,就好比指针一样,对slice取得的元素进行写操作实际是影响到了底层的数组层,slice真的只是一层薄薄的操作封装罢了。因此,slice有一个重要的用途就是作为函数的参数,就这可以避免直接传递数组而导致整个数组发生一次内存拷贝。
go编程中,倡导用slice对数组进行操作,而不是直接操作裸的数组,因为slice看上去更加的优雅,特性也更贴近C语言的指针。
slice也不是go的专利,支持这个特性的语言多了,至少python的list是支持这样的分片操作的。仔细观察能够发现近几年出现的这些动态语言都有着一些传统语言所不具备的优秀特性,go不但有着这样一些特性,并且还是编译型的静态语言。这也说明软件生产力也在进步了,但很大一部分程序员貌似却不想挪动自己的脚步,浅尝一下新鲜的味道,特别是一帮有情结的C程序员,这里也包括我自己,呵呵。
再补充一段展示数组和Slice关系的代码:
package main import "fmt" func main() { a := []int{0,1,2,3,4,5,6,7,8,9} s := a[0:5] Print(a, "a") Print(s, "s") s1 := append(s, 0) fmt.Printf("=================\n") Print(a, "a") Print(s, "s") Print(s1, "s1") } func Print(o []int, name string) { fmt.Printf("%s: ", name) for _, v := range o { fmt.Printf("%d ", v) } fmt.Printf("\n") }
运行结果:
a: 0 1 2 3 4 5 6 7 8 9
s: 0 1 2 3 4
=================
a: 0 1 2 3 4 0 6 7 8 9
s: 0 1 2 3 4
s1: 0 1 2 3 4 0
这段代码是为了加深自己的理解而做的实验,我想还是可以看出一点端倪来的。
备注:go语言学习笔记系列只是自己随心所记的一点笔记和感悟,不是教材之类。
有疑问加站长微信联系(非本文作者)