• 数组是一种固定长度且有序的元素集合。

数组 - 图1

访问数组元素

  • 数组中的每个元素都可以通过[ ] 和一个从 0 开始的索引进行访问。
  • 数组的长度可由内置函数 len 来确定。
  • 在声明数组时,未被赋值元素的值是对应类型的零值。

数组越界

  • Go 编译器在检测到对越界元素的访问时会报错
  • 如果 Go 编译器在编译时未能发现越界错误,那么程序在运行时会出现 panic
  • Panic 会导致程序崩溃

使用复合字面值初始化数组

  • 复合字面值(composite literal)是一种给复合类型初始化的紧凑语法。
  • Go 的复合字面值语法允许我们只用一步就完成数组声明和数组初始化两步操作:

数组 - 图2

  • 可以在复合字面值里使用 … 作为数组的长度,这样 Go 编译器会为你算出数组的元素数量
  1. package main
  2. import "fmt"
  3. func main() {
  4. planets := [...]string{
  5. "Mercury",
  6. "Venus",
  7. "Earth",
  8. "Mars",
  9. "Jupiter",
  10. "Saturn",
  11. "Uranus",
  12. "Neptune",
  13. }
  14. fmt.Println(planets)
  15. fmt.Println(len(planets))
  16. }
  • 无论哪种方式,数组的长度都是固定的。

遍历数组

  • for 循环
  1. package main
  2. import "fmt"
  3. func main() {
  4. dwarfs := [5]string{"Ceres", "Pluto", "Haumea", "Makemake", "Eris"}
  5. for i := 0; i < len(dwarfs); i++ {
  6. dwarf := dwarfs[i]
  7. fmt.Println(i, dwarf)
  8. }
  9. }
  • 使用 range
  1. package main
  2. import "fmt"
  3. func main() {
  4. dwarfs := [5]string{"Ceres", "Pluto", "Haumea", "Makemake", "Eris"}
  5. for i, dwarf := range dwarfs {
  6. fmt.Println(i, dwarf)
  7. }
  8. }

数组的复制

  • 无论数组赋值给新的变量还是将它传递给函数,都会产生一个完整的数组副本。
  1. package main
  2. import "fmt"
  3. func main() {
  4. planets := [...]string{
  5. "Mercury",
  6. "Venus",
  7. "Earth",
  8. "Mars",
  9. "Jupiter",
  10. "Saturn",
  11. "Uranus",
  12. "Neptune",
  13. }
  14. planetsMarkII := planets
  15. planets[2] = "whoops"
  16. fmt.Println(planets)
  17. fmt.Println(planetsMarkII)
  18. }
  • 数组也是一种值,函数通过值传递来接受参数。所以数组作为函数的参数就非常低效。
  1. package main
  2. import "fmt"
  3. // terraform accomplishes nothing
  4. func terraform(planets [8]string) {
  5. for i := range planets {
  6. planets[i] = "New " + planets[i]
  7. }
  8. }
  9. func main() {
  10. planets := [...]string{
  11. "Mercury",
  12. "Venus",
  13. "Earth",
  14. "Mars",
  15. "Jupiter",
  16. "Saturn",
  17. "Uranus",
  18. "Neptune",
  19. }
  20. terraform(planets)
  21. fmt.Println(planets)
  22. }
  • 数组的长度也是数组类型的一部分。
  • 尝试将长度不符的数组作为参数传递,将会报错。
  • 函数一般使用 slice 而不是数组作为参数。

数组的数组

  1. package main
  2. import "fmt"
  3. func main() {
  4. var board [8][8]string
  5. board[0][0] = "r"
  6. board[0][7] = "r"
  7. for column := range board[1] {
  8. board[1][column] = "p"
  9. }
  10. fmt.Print(board)
  11. }

作业题

  • 扩展例子 16.8 的程序,使用字符 kqrbnp 表示上方的黑棋,字符 KQRBNP 表示下方的白棋,然后在棋子的起始位置是打印出所有的棋子。
  • 编写一个能够美观的打印出整个棋盘的函数
  • 使用 [8][8]rune 数组而不是字符串来表示棋盘。

数组 - 图3

  1. package main
  2. import "fmt"
  3. func display(board [8][8]rune) {
  4. for _, row := range board {
  5. for _, column := range row {
  6. if column == 0 {
  7. fmt.Print(" ")
  8. } else {
  9. fmt.Printf("%c ", column)
  10. }
  11. }
  12. fmt.Println()
  13. }
  14. }
  15. func main() {
  16. var board [8][8]rune
  17. // black pieces
  18. board[0][0] = 'r'
  19. board[0][1] = 'n'
  20. board[0][2] = 'b'
  21. board[0][3] = 'q'
  22. board[0][4] = 'k'
  23. board[0][5] = 'b'
  24. board[0][6] = 'n'
  25. board[0][7] = 'r'
  26. // pawns
  27. for column := range board[1] {
  28. board[1][column] = 'p'
  29. board[6][column] = 'P'
  30. }
  31. // white pieces
  32. board[7][0] = 'R'
  33. board[7][1] = 'N'
  34. board[7][2] = 'B'
  35. board[7][3] = 'Q'
  36. board[7][4] = 'K'
  37. board[7][5] = 'B'
  38. board[7][6] = 'N'
  39. board[7][7] = 'R'
  40. display(board)
  41. }