1.概述
数组是Go语言中的基本类型之一,通常可以用以下方式来表示一个数组。
Go语言的数组与Java一样,在初始化的时候就需要指定数组的大小,并且确认之后就无法修改。
编译期间通过NewArray函数初始化,两个参数elem和bound分别表示元素类型以及数组的大小。
2.初始化
Go语言有两种不同的初始化方式,分别是显式的指定数组大小,另外一种是[…]声明数组,
数组大小
显式指定在类型检查阶段数组大小就会被提取出来,然后通过newArray方法创建数组。而[…]方式编译器会在cmd/compile/internal/gc.typecheckcomplit函数中,通过遍历的方式对数组的大小进行推导。因此,这种方式会比显式指定要多一点工作量。
语句转换
对字面量的初始化编译器做了两种不同的优化
当元素个数小于等于4个,会直接将数组中的元素放在栈上。
当元素个数大于4个,会将元素放在静态区并在运行时取出。
假设代码需要初始化 [5]int{1, 2, 3, 4, 5},那么我们可以将上述过程理解成以下的伪代码:
至于编译器为什么要这么优化,等后面看了内存管理之后来填坑。
3.访问和赋值
无论在栈上还是在静态存储区,数组在内存中都是连续的内存空间。我们可以通过数组开头的指针,元素的数量,以及元素所占的空间大小来表示数组。
访问
数组越界是比较严重的错误,Go会在编译期间检查静态类型来判断数组越界,主要有3类错误
访问数组的索引是非整数时,报错 “non-integer array index %v”;
访问数组的索引是负数时,报错 “invalid array index %v (index must be non-negative)";
访问数组的索引越界时,报错 “invalid array index %v (out of bounds for %d-element array)";
对于简单的错误,比如直接使用整数类型或者常量访问数组,是能够直接检查出来的
整数类型访问,直接报错 常量访问越界,也直接报错
而对于变量的访问数组或者字符串时,编译器就不能提前发现错误了。当在运行时发现错误程序就会崩溃退出。
对于无法判断数组下标是否越界的时候,编译器会在中间代码中插入IsInBounds,来检查是否越界,如果越界,则触发程序崩溃的PanicBounds指令。例如elem = arr[i],中间码则如下
将arr[i]改成arr[2]之后,则不需要IsInBounds来判断了,因为在编译器就能直接发现错误了。此时生成的中间码如下
赋值
赋值的过程:首先确认目标数组的地址,在通过PtrIndex获取目标元素的地址,最后使用Store指令将数据存入地址中。