Go by Example-图解数组
基本概念
1、数组是具有相同唯一类型的一组已编号且长度固定的数据项序列,这种类型可以是任意的原始类型例如整形、字符串或者自定义类型。
2、在 Go 中因为数组的内存布局是连续的,所以可以通过索引(位置)来读取(或者修改),索引是从 0 开始,第一个元素索引为 0,第二个索引为 1,这样以此类推。
3、声明数组时需要指定数据的类型,以及数组的长度。
var array [6] int
// 声明的规则:var + 变量名 + 类型
Go 和 Python 不一样,在声明变量的时候依次是 “var”、变量名、变量类型。这里我们声明一个包含 6 个元素的 int 类型的数组,这个数组在初始化的时候会被设置为该类型的零值。
数组创建
- 声明一个长度为 5 的 int 类型的空数组。
var arr=[5] int {}
fmt.Println (arr)
因为没有赋值,所以会打印其零值也就是 5 个 0
上面的过程我们可以通过一个图来表示:
图中 5 个连续的蓝色格子表示数组占用内存是连续分配的。
每一个格子表示数组的一个元素,每个元素的类型相同,这里的例子全是整数的,我们可以通过数组的下标访问数组中对应位置的元素。
当数组初始化的时候,数组每个元素都会初始化为其零值,其他语言习惯叫默认值。
每个数据类型的零值也是不一样的,这里就拿我们这个例子,因为每个元素都是 int 类型所以其零值为 0。
- 声明一个长度为 6 的 int 类型数组并赋值
array:= [6] int {5,10, 20, 30, 40, 50}
- 如果忽略 [] 中的数字不设置数组大小,Go 语言会根据元素的个数来设置数组的大小
arr1:=[...] int {1, 2, 3, 4, 5}
fmt.Println (arr1)
需要注意不指定数组大小的时候使用 ..., 如果不写东西就是切片了。
4. 还可以在定义的时候根据下标进行赋值
array:=[5] int {0:2,3:2}
定义了一个长度为 5 的 int 类型数组,: 前面的 0,3 表示当前下标,: 后面是要赋的值
通过演示图可以得知,下标 0 和 3 的元素赋值了,其他没有赋值的元素仍然是 0。
- 如果数组下标太大,编译会提示下标越界
array:=[4] int {0:2,3:2}
array [6] =4
fmt.Println (array1)
这里我们定义了数组长度为 4,其下标最大是 3, 这是要给下标第 6 个元素赋值,编译不通过。
数组复制
在 Go 中具有相同类型的,相同个数的数组,可以相互直接进行赋值。
// 复制
array1:=[4] int {0:2,3:2}
array2:=[4] int {}
array3:=[4] int {3,4,5,7}
array2 =array1 // 具有相同类型的,相同个数的数组,可以相互直接进行赋值
array3 =array2
fmt.Println (array2)
fmt.Println (array3) // 可以看到 array3 被 array2 完全覆盖
数组遍历
Go 语言一种独有的结构 for...range,可以用来遍历访问数组的元素,也可以遍历 map。这里先说数组
for ix, value := range array01 {
...
}
第一个返回值 ix 是数组的索引,第二个是在该索引位置的值;他们都是仅在 for 循环内部可见的局部变量。value 只是 array01 某个索引位置的值的一个拷贝,不能用来修改 array01 该索引位置的值。如下面的例子
package main
import "fmt"
var array [6] int
func main () {
array [1] =7 #根据下标给数组赋值,下标 0 开始。
for k,v:=range array {
fmt.Printf ("% d:% d \n",k,v)
}
}
输出结果
0:0
1:7
2:0
3:0
4:0
5:0
上面的代码我们只对列表第二个元素进行了赋值,可以看出其他位置默认零值为 0。
有时候,在一开始就想好了给数组赋几个值,赋什么值,这个时候我们可以使用下面的方式定义。
不过上面的代码有个限制就是,一旦长度设定了,后面就能在改变了。
所以一般情况下更建议使用后面文章提到的切片。
数组指针和指针数组
在说这个问题前,首先需要了解什么是 指针。
指针就是一个变量,它存储的数据不仅仅是一个普通的值,如简单的整数或字符串,而是另一个变量的内存地址。
一个指针变量可以指向任何一个值的内存地址它指向那个值的内存地址,在 32 位机器上占用 4 个字节,在 64 位机器上占用 8 个字节,并且与它所指向的值的大小无关。当然,可以声明指针指向任何类型的值来表明它的原始性或结构性;你可以在指针类型前面加上 * 号(前缀)来获取指针所指向的内容,这里的 * 号是一个类型更改器。使用一个指针引用一个值被称为间接引用。
当一个指针被定义后没有分配到任何变量时,它的零值为 nil。
每个变量在运行时都拥有一个地址,这个地址代表变量在内存中的位置。Go 语言中使用 & 作符放在变量前面对变量进行 “取地址” 操作。
了解了什么是指针了,下面让我们先看什么是数组指针。
简单说,数组指针其实就是 “数组的指针” 的简写,指针指向的数组的内存地址。
其形式为
var arr = [...] int {5:2}
// 数组指针
var ptf *[6] int = &arr // 和数组数量必须相等
ptf [2]=3
ptf [1]=7
fmt.Println (ptf)
然后看指针数组,同样的可以理解为指针数组的全称是 “指针类型的数组”。
func main () {
array1 := [5]*int {0: new (int), 1: new (int)}
*array1 [0] = 10
*array1 [1] = 20
array1 [2] = new (int) // 因为定义的时候没有给下标 2 的元素分配空间,所以这里是分配一块内存
*array1 [2] = 30 // 如果没有上一步这步会报错
fmt.Println (array1)
}
打印可以发现
[0xc000016038 0xc000016040]
所谓的指针数组就是,数组的元素全是指针组成的。
上面 array1 数组操作用图表示的话
此时的数组内每个元素存的实际上是指向 new 创建对应类型的指针,这里是 int 类型的指针。
官方对 new 的描述是:内建函数 new 用来分配内存,它的第一个参数是一个类型,不是一个值,它的返回值是一个指向新分配类型零值的指针。
需要注意的一点是只有给元素分配内存空间了才能对其进行操作,如果元素是 nil,直接对元素赋值的话就会出现错误,就像上面的 array1[2]。
这个数组的元素是指针类型的,存放的是指向整数的地址的指针。
指针数组的复制
下面我们看看如果对指针数组进行复制操作,内存中变化什么样呢。
func main () {
array1 := [5]*int {0: new (int), 1: new (int)}
// 为索引为 0 和 1 的元素赋值
*array1 [0] = 10
*array1 [1] = 20
array2 := array1
*array1 [0] =80
fmt.Println (array2==array1)
}
通过图片可以得知,复制数组指针,只会复制指针的值,而不会复制指针所指向的值。
所以修改 array1 的指针指向的地址时,array2 也会跟着变,所以 array1 和 array2 相等。
后记
在函数间传递数组,如果数组过大将造成巨大的开销,所以一般会将其转换为指针,这里为什么没有单独的拿出讲解,就是因为不推荐给函数传入数组。而是使用抽象度更高的切片。
更多内容关注公众号:python学习开发,添加微信:italocxa,回复加群:关键词Python、Go,需要哪个回复哪个即可。