4.2.1 数组
数组是具有相同唯一类型的一组已编号且长度固定的数据序列(所有的数据项目都是相同的数据类型);元素的数据类型可以是任意的原始类型例如整型、字符串或者自定义类型。数组长度必须是一个常量表达式,且必须是一个非负整数。 相对于去声明 number0, number1, ..., number99 的变量,使用数组形式 numbers[0], numbers[1] ..., numbers[99] 更加方便且易于扩展。
数组长度也是数组类型的一部分,不同的长度,不同的数据类型都属于不同的数组类型; 数组元素可以通过索引(位置)来读取(或者修改),索引从 0 开始,第一个元素索引为 0,第二个索引为 1,以此类推。数组元素可以通过 索引(位置)来读取(或者修改),数组以 0 开始在所有类 C 语言中是相似的)。元素的数目,也称为长度 或者数组大小必须是固定的并且在声明该数组时就给出(编译时需要知道数组长度以便分配内存);数组长度最大为 2Gb。
4.2.1.1 声明
Go 语言数组声明必须指定元素类型及元素个数,语法格式如下:
var variable_name [SIZE] variable_type或者多维数组
var variable_name [SIZE] []variable_type
下面是示例代码
var names [10]string // 声明长度为10的字符串数组var ages [10]int32 // 声明长度为10的整数32数组
var conns [20]*net.Conn // 声明长度为20的Conn指针数组
var objects [5]sturct{Name:string} // 声明长度为5的自定义结构体数组
4.2.1.2 初始化
可以直接通过和花括号{}赋值的方式进行初始化;格式如下:
variable_name = [SIZE] variable_type{n1[,n2,n3]}下面是示例代码
var arr [3]intarr = [3]int{1, 2, 3}
声明数组的同时快速初始化数组
var arr = [3]int{1, 2, 3}arr2 = [2]int{1, 2}
通过指定下标来初始化元素
costs := [5]float32{1:2.0,3:7.0}初始化数组中 {} 中的元素个数不能大于 [] 中的数字。如果忽略 [] 中的数字不设置数组大小,Go 语言会根据元素的个数来设置数组的大小;
如果数组长度不确定,可以使用 ... 代替数组的长度,编译器会根据元素个数自行推断数组的长度:
var costs = [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}或
costs := [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
以上实例读取了第五个元素。数组元素可以通过索引(位置)来读取(或者修改),索引从 0 开始,第一个元素索引为 0,第二个索引为 1,以此类推;
数组的元素如果没有进行初始化,Golang会按照变量的初始化的规则对数组类型里的元素进行初始化;例如每个元素是一个整型值,当声明数组时所有的元素都会被自动初始化为默认值 0, 例如每个元素如果是字符串类型,当声明数组时所有的元素会被自动初始为默认值“”(空字符串);如果每个元素是一个某种类型的指针,当声明数组时所有的元素会被自动初始为默认值nil(空指针);
示例
var arr = [...]int{1, 2, 3}fmt.Printf("%v %T %d\n", arr, arr, unsafe.Sizeof(arr))
var arr2 [2]string
arr2 = [2]string{}
fmt.Printf("%v %T %d\n", arr2, arr2, unsafe.Sizeof(arr2))
var aa []int16
fmt.Printf("%v %T %d\n", aa, aa, unsafe.Sizeof(aa))
var twoDim [3][3]int16
twoDim = [3][3]int16{[3]int16{1, 2, 3}, [3]int16{4, 5, 6}, [3]int16{7, 8, 9}}
fmt.Printf("%v %T %d\n", twoDim, twoDim, unsafe.Sizeof(twoDim))
var twoSlice [3][]int16
twoSlice = [3][]int16{[]int16{1, 2, 3}, []int16{4, 5, 6}, []int16{7, 8, 9}}
fmt.Printf("%v %T %d\n", twoSlice, twoSlice, unsafe.Sizeof(twoSlice))
代码结果
=== RUN TestLesson04_1[1 2 3] [3]int 24
[ ] [2]string 32
[] []int16 24
[[1 2 3] [4 5 6] [7 8 9]] [3][3]int16 18
[[1 2 3] [4 5 6] [7 8 9]] [3][]int16 72
--- PASS: TestLesson04_1 (0.00s)
思考
var twoDim [3][3]int16 和 var twoSlice [3][]int16 有什么区别4.2.1.3 访问
数组元素可以通过索引(位置)来读取。格式为数组名后加中括号,中括号中为索引的值。例如:
var cost float32 = costs[2]以上实例读取了数组 costs第3个元素的值。
遍历数组的方法既可以for 条件循环,也可以使用 for-range。这两种 for 结构对于切片(slices)来说也同样适用。
以下演示了数组完整操作(声明、赋值、访问)的实例:
实例
package charpter04import "fmt"
import "testing"
func TestLesson04_3(t *testing.T) {
var n [10]int /* n 是一个长度为 10 的数组 */
var i, j int
/* 为数组 n 初始化元素 */
for i = 0; i < len(n); i++ {
n[i] = i + 100 /* 设置元素为 i + 100 */
}
/* 输出每个数组元素的值 */
for j = 0; j < len(n); j++ {
fmt.Printf("Element[%d] = %d\n", j, n[j])
}
}=== RUN TestLesson04_3
Element[0] = 100
Element[1] = 101
Element[2] = 102
Element[3] = 103
Element[4] = 104
Element[5] = 105
Element[6] = 106
Element[7] = 107
Element[8] = 108
Element[9] = 109
--- PASS: TestLesson04_3 (0.00s)
使用 for-range
// 声明数组的同时快速初始化数组
costs := [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
/* 输出数组元素 */
for i, one := range costs {
fmt.Printf("costs[%d] = %f\n", i, one)
}
costs2 := [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
/* 输出每个数组元素的值 */
for j, one := range costs2 {
fmt.Printf("costs2[%d] = %f\n", j, one)
}
// 将索引为 1 和 3 的元素初始化
costs3 := [5]float32{1: 2.0, 3: 7.0}
for k, one := range costs3 {
fmt.Printf("costs3[%d] = %f\n", k, one)
}
}=== RUN TestLesson04_4
costs[0] = 1000.000000
costs[1] = 2.000000
costs[2] = 3.400000
costs[3] = 7.000000
costs[4] = 50.000000
costs2[0] = 1000.000000
costs2[1] = 2.000000
costs2[2] = 3.400000
costs2[3] = 7.000000
costs2[4] = 50.000000
costs3[0] = 0.000000
costs3[1] = 2.000000
costs3[2] = 0.000000
costs3[3] = 7.000000
costs3[4] = 0.000000
--- PASS: TestLesson04_4 (0.00s)
注意事项
Go中的数组是一个值(valuetype)类型(不像 C/C++ 中是指向首元素的指针),所有的值类型变量,在复制和作为参数传递时,都将产生一次复制动作,如果将数组作为函数的参数类型,则在函数调用时参数数据将发生数据复制,因此,在函数体中无法修改传入的数组的内容,因为函数内操作的只是传入数组的一个副本。可以直接使用=来复制两个数组类型相同的数组对象(包括数组的长度,数组中元素的类型)
func modify(array [5]int) {array[0] = -1
fmt.Printf("%v %T %d\n", array, array, unsafe.Sizeof(array))
}
func TestLesson04_5(t *testing.T) {
array := [5]int{1, 2, 3, 4, 5}
modify(array)
fmt.Printf("%v %T %d\n", array, array, unsafe.Sizeof(array))
}
输出结果
=== RUN TestLesson04_5[-1 2 3 4 5] [5]int 40
[1 2 3 4 5] [5]int 40
--- PASS: TestLesson04_5 (0.00s)
比较两个数组是否相等; 如果两个数组类型相同(包括数组的长度,数组中元素的类型)的情况下,我们可以直接通过较运算符(==和 !=)来判断两个数组是否相等,只有当两个数组的所有元素都是相等的时候数组才是相等的,不能比较两个类型不同的数组,否则程序将无法完成编译
一个数组可以由零个或多个元素组成,一旦声明了,数组的长度就固定了,不能动态变化。len() 和 cap() 返回结果始终一样。如果超过了数组固定的长度; 编译时会报错:invalid array index x (out of bounds for x-element array)
func TestLesson04_6(t *testing.T) {array := [5]int{1, 2, 3, 4, 5}
array[5] = 5
fmt.Printf("%v %T %d\n", array, array, unsafe.Sizeof(array))
}# go-in-practice/code/charpter-01 [go-in-practice/code/charpter-01.test]
.\lesson03_test.go:176:8: invalid argument: array index 5 out of bounds [0:5]
特别注意
由于数组是值类型(value-type);作为参数或者返回值时;在传递值过程中;会进行内存中的值复制;如果数组的长度过大;会有较大的内存复制上的开销;所以建议此情况下,使用切片或者使用数组的指针对象进行传递