概述

Go 语言中数组在初始化之后大小就无法改变,存储元素类型相同、但是大小不同的数组类型在 Go 语言看来也是完全不同的,只有两个条件都相同才是同一个类型。

初始化

  1. arr1 := [3]int{1, 2, 3}
  2. arr2 := [...]int{1, 2, 3}

一维数组

  1. // 全局:
  2. var arr0 [5]int = [5]int{1, 2, 3}
  3. var arr1 = [5]int{1, 2, 3, 4, 5}
  4. var arr2 = [...]int{1, 2, 3, 4, 5, 6}
  5. var str = [5]string{3: "hello world", 4: "tom"}
  6. // 局部:
  7. a := [3]int{1, 2} // 未初始化元素值为 0。
  8. b := [...]int{1, 2, 3, 4} // 通过初始化值确定数组长度。
  9. c := [5]int{2: 100, 4: 200} // 使用索引号初始化元素。
  10. d := [...]struct {
  11. name string
  12. age uint8
  13. }{
  14. {"user1", 10}, // 可省略元素类型。
  15. {"user2", 20},
  16. }

多维数组

  1. // 全局
  2. var arr0 [5][3]int
  3. var arr1 [2][3]int = [...][3]int{{1, 2, 3}, {7, 8, 9}}
  4. // 局部:
  5. a := [2][3]int{{1, 2, 3}, {4, 5, 6}}
  6. b := [...][2]int{{1, 1}, {2, 2}, {3, 3}} // 第 2 纬度不能用 "..."。

语句转换

对于一个由字面量组成的数组,根据数组元素数量的不同,编译器会在负责初始化字面量的 [cmd/compile/internal/gc.anylit](https://github.com/golang/go/blob/f07059d949057f414dd0f8303f93ca727d716c62/src/cmd/compile/internal/gc/sinit.go#L875-L967) 函数中做两种不同的优化:

  1. 当元素数量小于或者等于 4 个时,会直接将数组中的元素放置在栈上;
  2. 当元素数量大于 4 个时,会将数组中的元素放置到静态区并在运行时取出;
  1. func anylit(n *Node, var_ *Node, init *Nodes) {
  2. t := n.Type
  3. switch n.Op {
  4. case OSTRUCTLIT, OARRAYLIT:
  5. if n.List.Len() > 4 {
  6. ...
  7. }
  8. fixedlit(inInitFunction, initKindLocalCode, n, var_, init)
  9. ...
  10. }
  11. }

当数组的元素小于或者等于四个时,[cmd/compile/internal/gc.fixedlit](https://github.com/golang/go/blob/f07059d949057f414dd0f8303f93ca727d716c62/src/cmd/compile/internal/gc/sinit.go#L515-L583) 会负责在函数编译之前将 [3]{1, 2, 3} 转换成更加原始的语句:

  1. func fixedlit(ctxt initContext, kind initKind, n *Node, var_ *Node, init *Nodes) {
  2. var splitnode func(*Node) (a *Node, value *Node)
  3. ...
  4. for _, r := range n.List.Slice() {
  5. a, value := splitnode(r)
  6. a = nod(OAS, a, value)
  7. a = typecheck(a, ctxStmt)
  8. switch kind {
  9. case initKindStatic:
  10. genAsStatic(a)
  11. case initKindLocalCode:
  12. a = orderStmtInPlace(a, map[string][]*Node{})
  13. a = walkstmt(a)
  14. init.Append(a)
  15. }
  16. }
  17. }
  1. 访问数组的索引是非整数时会直接报错 —— non-integer array index %v;
  2. 访问数组的索引是负数时会直接报错 —— “invalid array index %v (index must be non-negative)”;
  3. 访问数组的索引越界时会直接报错 —— “invalid array index %v (out of bounds for %d-element array)”;

对数组的访问和赋值需要同时依赖编译器和运行时,它的大多数操作在编译期间都会转换成对内存的直接读写,在中间代码生成期间,编译器还会插入运行时方法 panicIndex 调用防止发生越界错误。

参考