一.数组
1.初始化
arr1 := [3]int{1, 2, 3}
arr2 := [...]int{1, 2, 3}
上述两种声明方式在运行期间得到的结果是完全相同的,后一种声明方式在编译期间就会被『转换』成为前一种。
2.数据存储
- 当元素数量小于或者等于 4 个时,会直接将数组中的元素放置在栈上;
- 当元素数量大于 4 个时,会将数组中的元素放置到静态区再搬到堆上并在运行时取出;
总结起来,如果数组中元素的个数小于或者等于 4 个,那么所有的变量会直接在栈上初始化,如果数组元素大于 4 个,变量就会在静态存储区初始化然后拷贝到栈上,这些转换后的代码才会继续进入中间代码生成和机器码生成两个阶段,最后生成可以执行的二进制文件。
3.数组访问越界
数组和字符串的一些简单越界错误都会在编译期间发现,比如我们直接使用整数或者常量访问数组,但是如果使用变量去访问数组或者字符串时,编译器就无法发现对应的错误了,这时就需要 Go 语言运行时发挥作用了。
二:切片
1.声明
[]int
[]interface{}
切片在编译期间的生成的类型只会包含切片中的元素类型。
编译期间的切片是 Slice
类型的,但是在运行时切片由如下的 SliceHeader
结构体表示,其中 Data
字段是指向数组的指针,Len
表示当前切片的长度,而 Cap
表示当前切片的容量,也就是 Data
数组的大小:
type SliceHeader struct {
Data uintptr
Len int
Cap int
}
切片与数组的关系非常密切,切片引入了一个抽象层,提供了对数组中部分片段的引用,作为数组的引用,我们可以在运行区间可以修改它的长度,如果底层的数组长度不足就会触发扩容机制,切片中的数组就会发生变化,不过在上层看来切片时没有变化的,上层只需要与切片打交道不需要关心底层的数组变化
切片是运行时才会确定内容的结构,所有的操作还需要依赖 Go 语言的运行时来完成,我们接下来就会介绍切片一些常见操作的实现原理。
1.初始化
- 通过下标的方式获得数组或者切片的一部分;
- 使用字面量初始化新的切片;
- 使用关键字
make
创建切片:
arr[0:3] or slice[0:3]
slice := []int{1, 2, 3}
slice := make([]int, 10)
通过上面3种方式都能初始化一个切片。第一种使用下标获取数组或者切片的一部分,则会创建一个切片。后面俩种方式就是常用的直接的初始化切片的方式。slice := []int{1, 2, 3}叫字面量初始化新切片方式。