《快学 Go 语言》第 4 课 —— 低调的数组

数组就是一篇连续的内存,几乎全部的计算机语言都有数组,只不过 Go 语言里面的数组其实并不经常使用,这是由于数组是定长的静态的,一旦定义好长度就没法更改,并且不一样长度的数组属于不一样的类型,之间不能相互转换相互赋值,用起来多有不方便之处。git

切片是动态的数组,是能够扩充内容增长长度的数组。当长度不变时,它用起来就和普通数组同样。当长度不一样时,它们也属于相同的类型,之间能够相互赋值。切片的便捷性让数组的绝大多数应用领域都普遍地被取代了。github

不过也不能够小瞧数组,在切片的底层实现中,数组是切片的基石,是切片的特殊语法隐藏了内部的细节,让用户不能直接看到内部隐藏的数组。切片不过是数组的一个包装,给顽固的数组装上了灵活的翅膀,让石头也能够展翅飞翔。数组

仅仅是上面纯文字的说明,读者确定会感受很懵。下面让咱们来看具体的实例。函数

数组变量的定义

咱们先试一下只申明类型,不赋初值。这时编译器会给数组默认赋上「零值」。数组的零值就是全部内部元素的零值。性能

package main

import "fmt"

func main() {
	var a [9]int
	fmt.Println(a)
}

------------
[0 0 0 0 0 0 0 0 0]
复制代码

下面咱们看看另外三种变量定义的形式, 效果都是同样的ui

package main

import "fmt"

func main() {
	var a = [9]int{1, 2, 3, 4, 5, 6, 7, 8, 9}
	var b [10]int = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	c := [8]int{1, 2, 3, 4, 5, 6, 7, 8}
	fmt.Println(a)
	fmt.Println(b)
	fmt.Println(c)
}

---------------------
[1 2 3 4 5 6 7 8 9]
[1 2 3 4 5 6 7 8 9 10]
[1 2 3 4 5 6 7 8]
复制代码

数组的访问

接下来咱们使用下标来简单操做一下数组,这个数组里存的是数字的平方值spa

package main

import "fmt"

func main() {
	var squares [9]int
	for i := 0; i < len(squares); i++ {
		squares[i] = (i + 1) * (i + 1)
	}
	fmt.Println(squares)
}

--------------------
[1 4 9 16 25 36 49 64 81]
复制代码

数组的下标越界检查(高阶知识)

上面的代码中咱们注意到可使用内置函数 len() 来直接获取数组的长度。数组的长度是编译期肯定的,当咱们使用 len() 函数访问数组的长度属性时,编译器在背后偷偷把它替换成了整数值。3d

package main

import "fmt"

func main() {
	var a = [5]int{1,2,3,4,5}
	a[101] = 255
	fmt.Println(a)
}

-----
./main.go:7:3: invalid array index 101 (out of bounds for 5-element array)
复制代码

上面的代码运行结果说明了 Go 语言会对数组访问下标越界进行编译器检查。有一个重要的问题是,若是下标是一个变量,Go 是如何检查下标越界呢?变量须要在运行时才能够决定是否越界,Go 是如何办到的呢?code

package main

import "fmt"

func main() {
	var a = [5]int{1,2,3,4,5}
	var b = 101
	a[b] = 255
	fmt.Println(a)
}

------------
panic: runtime error: index out of range

goroutine 1 [running]:
main.main()
	/Users/qianwp/go/src/github.com/pyloque/practice/main.go:8 +0x3d
exit status 2
复制代码

答案是 Go 会在编译后的代码中插入下标越界检查的逻辑,因此数组的下标访问效率是要打折扣的,比不得 C 语言的数组访问性能。cdn

数组赋值

一样的子元素类型而且是一样长度的数组才能够相互赋值,不然就是不一样的数组类型,不能赋值。数组的赋值本质上是一种浅拷贝操做,赋值的两个数组变量的值不会共享。

package main

import "fmt"

func main() {
	var a = [9]int{1, 2, 3, 4, 5, 6, 7, 8, 9}
	var b [9]int
	b = a
	a[0] = 12345
	fmt.Println(a)
	fmt.Println(b)
}

--------------------------
[12345 2 3 4 5 6 7 8 9]
[1 2 3 4 5 6 7 8 9]
复制代码

从上面代码的运行结果中能够看出赋值后两个数组并无共享内部元素。若是数组的长度很大,那么拷贝操做是有必定的开销的,使用的时候必定须要注意。下面咱们尝试使用不一样长度的数组赋值会有什么结果

package main

import "fmt"

func main() {
	var a = [9]int{1, 2, 3, 4, 5, 6, 7, 8, 9}
	var b [10]int
	b = a
	fmt.Println(b)
}

--------------------------
./main.go:8:4: cannot use a (type [9]int) as type [10]int in assignment
复制代码

能够看出不一样长度的数组之间赋值是禁止的,由于它们属于不一样的类型。

数组的遍历

数组除了可使用下标进行遍历以外,还可使用 range 关键字来遍历,range 遍历提供了下面两种形式。

package main

import "fmt"

func main() {
	var a = [5]int{1,2,3,4,5}
	for index := range a {
        fmt.Println(index, a[index])
    }
    for index, value := range a {
		fmt.Println(index, value)
	}
}

------------
0 1
1 2
2 3
3 4
4 5
0 1
1 2
2 3
3 4
4 5
复制代码

考虑到切片的内容太多,咱们将独立一节专门讲解切片,下一节将是 Go 语言的极有价值的一节,读者必定要努力搞清楚每个细节。

扫一扫上面的二维码,阅读《快学 Go 语言》更多章节

相关文章
相关标签/搜索