内存分配方式不同
- 数组:
- 数组在Go语言中是固定长度的。其内存分配是在编译时就确定的,在栈上分配(如果数组大小比较小)或者在堆上分配(如果数组大小较大,具体由编译器优化决定)。一旦声明,数组的长度就不可改变,其占用的内存空间大小为数组元素类型大小乘以数组长度。例如声明
var arr [5]int
,那么arr
就会占用5 * 8
(假设int
类型在当前平台占8字节)字节的连续内存空间。
- 切片:
- 切片是动态长度的。切片本身是一个结构体,它由三个部分组成:指向底层数组的指针、切片的长度和切片的容量。切片的内存分配涉及到两个层面,一是切片结构体本身的内存分配,它通常在栈上(如果切片变量是函数内局部变量等情况),二是底层数组的内存分配,底层数组的内存是在堆上分配的。当使用
make
函数创建切片时,如slice := make([]int, 5, 10)
,会在堆上分配一个能容纳10个int
类型元素的数组,然后切片结构体指向这个数组,并且长度设为5,容量设为10。
对程序表现的不同影响举例
- 数组:
- 由于数组长度固定,在需要动态增加元素场景下会很不方便。例如:
package main
import "fmt"
func main() {
var arr [5]int
arr[0] = 1
// 以下代码尝试增加元素会导致越界错误
// arr[5] = 6
fmt.Println(arr)
}
- 这里如果尝试向超出数组长度的位置赋值,会导致编译错误,这就限制了数组在动态场景下的使用。
- 切片:
- 切片的动态特性使其在需要动态增加元素时很方便。例如:
package main
import "fmt"
func main() {
slice := make([]int, 0, 5)
slice = append(slice, 1)
slice = append(slice, 2)
fmt.Println(slice)
}
- 这里通过
append
函数可以方便地向切片中动态添加元素,即使最初切片的长度为0,随着append
操作,切片会根据需要动态增长,底层数组也可能会重新分配内存以适应新的元素数量。如果当前容量不足以容纳新元素,会重新分配一个更大的底层数组,将原数组内容复制过去,然后再添加新元素。