这里将自己学习Go及其区块链的一些笔记、积累分享一下,如果涉及到了文章、文字侵权,请联系我删除或调整。
- 数组是数据的集合,以满足按照逻辑对数据进行分组的需要。
- 数组也是基本的数据结构(由多个数据元素组成),以存储一系列可被基零的连续下标索引的数据。
- 为了创建数组,需要声明一个数组类型的变量,在声明中指定其类型和长度。
1)var arr [5]int 仅声明未初始化
方括号中的数字表示数组长度,即所含元素的个数,后面紧跟元素的数据类型,即数组的类型。
2)var arr = [5]int{10, 20, 30, 40, 50} 声明并初始化
数组也可以在声明的同时初始化,花括号中是用逗号分隔的初值序列,其长度可以小于但不能大于数组的长度,未显式指定初值的元素被默认初始化为适当类型的零值。
3)arr := [...]int{10, 20, 30, 40, 50} 注意省略号不可省略
带有显式初始化的数组声明,其长度也可以用省略号表示,编译器会根据初值的个数自动计算其长度,但如果连省略号也不写,那就不再是数组而变成切片了,二者在性质上并不完全等价。
- 数组一经声明,其长度即不可改变,既不能添加元素,也不能删除元素,但我们可以修改数组元素的值,可以把数组看作一种“值可写,但结构不可写”的数据类型。
- 数组是一种独立的数据类型,可以内存复制的形式被整体地赋值、传参和从函数中返回,前提是目标数组必须与源数组具有完全相同的类型和长度。
- 数组中的元素可通过从0开始的,连续正整数形式的下标访问,试图访问超出合法下标范围的元素,即所谓下标越界,将导致编译错误。
- 数组是一种空间递归的数据结构,所谓n维数组其实就是n-1维数组的一维数组,即我们可以将数组的元素也声明为数组。
- 如果在声明的同时初始化高维数组,只有最高维的长度可以用省略号"..."表示。
// var 数组 [长度]类型
// var 数组 = [长度(...)]类型{初值表}
// 数组 := [长度(...)]类型{初值表}
package main
import "fmt"
// 传数组,复制数据
func foo(x [3]int) {
for i := 0; i < len(x); i++ {
x[i]++
}
fmt.Println("foo:", x)
}
在go语言中的函数声明时,参数声明为数组类型,那么函数调用过程的形参会对实参进行复制,在函数体内的操作不会修改原实参的数据。
// 传切片,共享数据
func bar(x []int) {
for i := 0; i < len(x); i++ {
x[i]++
}
fmt.Println("bar:", x)
}
在go语言中的函数声明时,参数声明为切片类型,那么函数调用过程的形参会共享实参的数据,在函数体内的操作会修改原实参的数据。
在go语言中,可以对切片进行元素追加的操作(相当于重新建立了1个数据,并进行了数据的拷贝)。对于切片而言,当执行“追加元素”的操作时,切片会发生结构性的改变——会产生一次元素的复制。
对于hum(x []int)函数而言,其先执行了自增,而后执行了元素复制;而fun(x []int)则是先执行了元素复制,而后执行自增。具体如下述demo所示。
// 共享->修改->复制
func hum(x []int) { // 形参x与实参指向同一个内存区域
for i := 0; i < len(x); i++ {
x[i]++
}
// 此步执行完毕后,x指向了一个新的内存区域(形参x的副本 + 追加的元素)
// 并且,新的数据没有返回给实参,则实参本身没有变化。
x = append(x, x[len(x)-1]+1)
fmt.Println("hum:", x)
}
// 共享->复制->修改
func fun(x []int) { // 形参x与实参指向同一个内存区域
// 此步执行完毕后,x指向了一个新的内存区域(形参x的副本 + 追加的元素)
x = append(x, x[len(x)-1]+1)
// 自增操作的对象是x指向的副本,而不再是原实参指向的数据,因此,fun函数中
// 的任何操作,都不会影响到原实参指向的数据。
for i := 0; i < len(x); i++ {
x[i]++
}
fmt.Println("fun:", x)
}
func main() {
//
// 数组的声明与赋值
//
var a [3]string // 以前文学习的数组初始化方法,声明并初始化了字符串数组a
a[0] = "Hello World!"
a[1] = "Hello George!"
a[2] = "Hello Go!"
fmt.Println(a)
//
// 数组与切片型参数
//
var b = [...]int{1, 2, 3}
fmt.Println(b) // [1 2 3]
// 传数组,复制数据,不影响原实参数据
foo(b) // [2 3 4]
fmt.Println(b) // [1 2 3]
// 传切片,共享数据,影响原实参数据
bar(b[:]) // [2 3 4]
fmt.Println(b) // [2 3 4]
// 共享->修改->复制
// hum首先执行了自增,因为是"传切片"所以实参原数据也发生了改变[1,2,3]->
// [3,4,5];其次,hum执行了“追加元素”操作,此时发生了元素复制,新的元素
// 添加在了b副本的末尾,而不是直接添加在了b的末尾。因此,hum函数内外打印
// 的结果不同。
hum(b[:]) // [3 4 5 6]
fmt.Println(b) // [3 4 5]
// 共享->复制->修改
// fun虽然也是“传切片”,但fun函数第一行就执行了“追加元素”的操作,因此
// fun后续所有的操作都是针对b的副本在执行,不影响实参指向的数据。因此,在
// 执行fun函数后,b的打印结果依旧是[3,4,5],并没有发生自增。
fun(b[:]) // [4 5 6 7]
fmt.Println(b) // [3 4 5]
多维数据的实例:
// 二维数组下标循环
c := [2][3]int{{1, 2, 3}, {4, 5, 6}}
fmt.Println(c)
for i := 0; i < len(c); i++ {
for j := 0; j < len(c[i]); j++ {
fmt.Printf("c[%v][%v]: %v\n", i, j, c[i][j])
}
}
// 二维数组范围循环
d := [...][3]int{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}
fmt.Println(d)
for i, row := range d {
for j, col := range row {
fmt.Printf("d[%v][%v]: %v\n", i, j, col)
}
}
}
// 输出结果:
[[1 2 3] [4 5 6]]
c[0][0]: 1
c[0][1]: 2
c[0][2]: 3
c[1][0]: 4
c[1][1]: 5
c[1][2]: 6
[[1 2 3] [4 5 6] [7 8 9]]
d[0][0]: 1
d[0][1]: 2
d[0][2]: 3
d[1][0]: 4
d[1][1]: 5
d[1][2]: 6
d[2][0]: 7
d[2][1]: 8
d[2][2]: 9