内容简介:Go 语言给用户提供了三种数据结构用于管理集合数据:数组、切片(Go语言中,数组是一个长度固定的数据类型,用于存储一段相同数据类型的元素,这些元素在内存中是连续存储的。数组存储的类型可以是内置类型,如整型、字符串等,也可以是自定义的数据结构。强调数组数组声明有两个要点:
Go 语言给用户提供了三种数据结构用于管理集合数据:数组、切片( Slice )和映射( Map )。这三种数据结构是语言核心的一部分,在标准库里被广泛使用。学会这些数据结构,编写 go 程序会变得快速、有趣且十分灵活。掌握数组是理解切片和映射的基础,我们就从数组开始学习。
什么是数组
Go语言中,数组是一个长度固定的数据类型,用于存储一段相同数据类型的元素,这些元素在内存中是连续存储的。数组存储的类型可以是内置类型,如整型、字符串等,也可以是自定义的数据结构。强调数组 固定 ,有别于切片,它是可以增长和收缩的动态序列。数组的每个元素可以通过索引下标来访问,索引下标的范围是从 [0 , len(array)-1] 。
声明与初始化
数组声明有两个要点:
- 指定数组存储的数据的类型;
- 元素个数,即数组的长度;
var array0 [5]int // 声明一个包含5个元素的整型数组,但我们并未初始化 fmt.Println(array0) //输出:[0 0 0 0 0] 复制代码
前面我们已经讲过,Go 语言中声明变量时,总会使用对应类型的零值来对变量进行初始化。数组也不例外。 当数组初始化时,数组内每个元素都初始化为对应类型的零值。从输出结果可以看到,整型数组里的每个元素都初始化为 0,也就是整型的零值。
var array0 [5]int
array0 = [5]int{1,2,3,4,5} //手动初始化
fmt.Println(array0) //输出:[1 2 3 4 5]
复制代码
最基本的声明并初始化:
// 声明并初始化
var array0 = [5]int{1,2,3,4,5}
fmt.Println(array0)
复制代码
使用Go提供的 := 操作符:
array0 := [5]int{1,2,3,4,5}
复制代码
Go提供了一种机制,免去了我们指定数组长度的烦恼,使用 ... ,根据初始化时数组元素的数量来确定该数组的长度。
array := [...]int{1,2,3,4,5}
fmt.Println(len(array)) // 内置的len()函数返回数组中元素的个数。
复制代码
假如我只想给索引为1、3的元素指定初始化的值怎么办?还是有办法的:
array := [...]int{0,2,0,4,0}
复制代码
更简便的方法:
array := [5]int{1:2,3:4}
复制代码
学会使用数组
上面提到过得,因为数组的内存分布是连续的,所以在数组访问任一的效率是很高,这也是数组的优势。可以使用 [] 运算符访问数组的当个元素。
array := [5]int{1,2,3,4,5}
fmt.Println(array[3]) //访问单个元素
array[3] = 30 //修改当个元素的值
fmt.Println(array[3])
复制代码
使用 for 、 for range 循环遍历数组:
array := [5]int{1,2,3,4,5}
// for
for i:=0;i<len(array);i++ {
fmt.Printf("索引%d的值: %d\n",i,array[i])
}
// for range
for i,v := range array{
fmt.Printf("索引%d的值: %d\n",i,v)
}
复制代码
输出的结果是一样的:
索引0的值: 1 索引1的值: 2 索引2的值: 3 索引3的值: 4 索引4的值: 5 复制代码
数组变量的类型包括 数组长度 和 每个元素的类型 。Go语言规定只有这两部分都相同的数组,才是类型相同的数组,才能互相赋值,不然会编译出错。
var array1 [5]int
array2 := [5]int{1,2,3,4,5}
array1 = array2
fmt.Println(array1) // 输出:[1 2 3 4 5]
var array3 [4]int = array2
// 编译出错:cannot use array2 (type [5]int) as type [4]int in assignment
复制代码
数组指针和指针数组
我们可以声明一个指针变量,指向一个数组:
arr := [6]int{5:9}
// 数组指针
var ptr *[6]int = &arr
// 简写
ptr := &arr
复制代码
需要 注意 的是,指针变量 ptr 的类型是 *[6]int ,也就是说它只能指向包含6个元素的整型数组,否则编译报错。 指针数组和数组差不多,只不过元素类型是指针:
// 指针数组
x,y := 1,2
var arrPtr = [5]*int{1:&x,3:&y} // 没有手动初始化的元素,已经自动初始化指针类型对应的零值 nil
fmt.Println(*arrPtr[1]) // 输出:1
*arrPtr[1] = 10
fmt.Println(x,*arrPtr[1]) // 输出:10 10
复制代码
*arrPtr[1] = 10 ,同时也修改了变量 x 的值,因为 x 和 arrPtr[1] 指向同一内存地址。 提一点,相同类型的指针数组也可以相互赋值。 总结一句话:注意 *与 谁结合,如 p *[5]int , * 与数组结合说明是数组指针;如 p [5]*int , * 与 int 结合,说明这个数组都是 int 类型的指针,是指针数组。
函数间传递数组
函数之间传递变量时, 总是以值的方式传递的。如果变量是一个数组,意味着整个数组,不管有多大,都会完整赋值一份,并传递给函数。复制出来的数组只是原数组的一份副本,在函数中修改传递进来数组是不会改变原数组的值得。
// 传递数组的副本
func modify(a [5]int) {
a[1] = 1
fmt.Println(a)
}
func main(){
arr := [5]int{4:9}
fmt.Println(arr)
modify(arr)
fmt.Println(arr)
}
复制代码
输出:
[0 0 0 0 9] [0 1 0 0 9] [0 0 0 0 9] 复制代码
原数组元素的值没有被修改。
大家可以想一个问题,如果一个数组的数据量很大,如果还采用值传递的话,这无疑是一个开销很大的操作,对内存和性能都是不友好的。还好,我们还有一个更好的办法:传递指向数组的指针,这样只需要复制一个数组类型的指针大小就可以。
// 传递数组的指针
func modifyPtr(a *[5]int){
a[1] = 2
fmt.Println(*a)
}
func main(){
arr := [5]int{4:9}
fmt.Println(arr)
modifyPtr(&arr)
fmt.Println(arr)
}
复制代码
输出:
[0 0 0 0 9] [0 2 0 0 9] [0 2 0 0 9] 复制代码
有没有发现!原数组已经改变了,因为现在传递的是数组指针, 所以如果改变指针指向的值,原数组在内存的值也会被修改。这种操作虽然更有效地利用内存(免去了大量的内存复制)、性能也更好,但如果没有处理好指针,也会带来不必要的问题,所以,使用的时候需要谨慎小心。
下一节,我们来讲讲切片!
关注公众号「Golang来了」,获取最新文章!
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
你必须知道的495个C语言问题
Steve Summit / 孙云、朱群英 / 人民邮电出版社 / 2009-2 / 45.00元
“本书是Summit以及C FAQ在线列表的许多参与者多年心血的结晶,是C语言界最为珍贵的财富之一。我向所有C语言程序员推荐本书。” ——Francis Glassborow,著名C/C++专家,ACCU(C/C++用户协会)前主席 “本书清晰阐明了Kernighan与Ritchie《The C programming Language》一书中许多简略的地方,而且精彩地总结了C语言编程......一起来看看 《你必须知道的495个C语言问题》 这本书的介绍吧!