众所周知,在Java中,二维数组是多维一维数组。这意味着那些一维数组在内存中不连续。
相反,在C中,二维数组实际上是大小为 total_row * total_column的 一维数组。因为Go语言使用了C语言中的许多概念。所以我的问题是:Go语言中的二维数组的内存表示是否像C或Java中那样?
在Go中,通常将slice误认为是array,所以我对两者都作了回答。
引用规范:数组类型:
数组类型始终是一维的, 但可以组成多维类型。
在那里,您的答案清晰明了。但是答案不只是因为数组是Go中的值,它们也不像切片一样是描述符(标头)。
看这个简单的例子:
x := [5][5]byte{} fmt.Println(&x[0][3]) fmt.Println(&x[0][4]) fmt.Println(&x[1][0])
输出(在Go Playground上尝试):
0x10432203 0x10432204 0x10432205
如您所见,为数组分配和使用的内存是连续的:第二行从内存地址开始,该地址是第一行最后一个元素的地址的后继地址。
检查数组大小:
fmt.Println(unsafe.Sizeof([4][6]int{})) // 96 fmt.Println(unsafe.Sizeof([6][4]int{})) // 96
切换行和列无关紧要,其大小相同。
在切片的情况下也是如此:多维切片是切片的切片。规格:切片类型:
切片是 基础数组 连续段的描述符,并提供对该数组中元素编号序列的访问。 … 像数组一样,切片始终是一维的,但可以组成更高维度的对象。
切片是描述符,切片头包含指向基础(支持)数组的元素,长度和容量的指针。因此,总片数取决于内存使用情况。
请参阅以下示例:
x := make([][]byte, 2) for i := range x { x[i] = make([]byte, 1000) } fmt.Println(len(x), len(x)*len(x[0])) y := make([][]byte, 1000) for i := range y { y[i] = make([]byte, 2) } fmt.Println(len(y), len(y)*len(y[0]))
2 2000 1000 2000
既x和y多维片具有2000个元素总(2000个字节),但x存储2仅切片,每个具有1000元件,而y存储1000切片,每个具有2元件。
x
y
2
1000
这意味着x需要2片头,同时y需要1000切片头(用于元素+1 x和y自己)!
切片标头表示为reflect.SliceHeader:
reflect.SliceHeader
type SliceHeader struct { Data uintptr Len int Cap int }
在32位架构上,切片标头的大小为12个字节,在64位架构上,其为24个字节。因此,对于32位arch元素,x要求2,000字节加上2x12字节的内存(即 2,024字节) ,而元素的y要求2,000字节加上1,000 * 12的 14,000字节 。
另请注意,多维切片的元素可能包含具有不同长度的切片:
对于数组数组,内部数组通过构造始终具有相同的长度;但是,对于切片的切片(或切片的阵列),内部长度可能会动态变化。此外,内部切片必须单独初始化。
像这个例子一样:
x := make([][]byte, 2) x[0] = []byte{1, 2} x[1] = []byte{1, 2, 3, 4} fmt.Println(x[0]) fmt.Println(x[1])
[1 2] [1 2 3 4]
如果您还没有读过,推荐您:The Go Blog:数组,切片(和字符串):“ append”的机制