二维数组的内存表示是什么?
What is two dimensional array's memory representation?
在Java中,二维数组是多个一维数组。这意味着那些在内存上不连续的一维数组。
相比之下,在C中,二维数组实际上就是一维数组,大小为total_row * total_column。因为Go语言使用了很多C语言的概念。
所以我的问题是:二维数组在 Go 中的内存表示看起来像 C 还是 Java?
在 Go 中,slices are often mistaken for arrays,所以我对两者都作了回答。
数组
Array types are always one-dimensional but may be composed to form multi-dimensional types.
你的答案很清楚。但答案不仅仅是因为数组在 Go 中是值,它们不像切片那样是描述符(headers)。
看这个简单的例子:
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
不管你换行换列,它的大小都是一样的。
切片
这同样适用于切片的情况:多维切片是切片的切片。 Spec: Slice types:
A slice is a descriptor for a contiguous segment of an underlying array and provides access to a numbered sequence of elements from that array.
...
Like arrays, slices are always one-dimensional but may be composed to construct higher-dimensional objects.
切片是描述符,切片 header 包含指向底层(后备)数组元素的指针、长度和容量。因此,总切片数在内存使用方面很重要。
看这个例子:
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]))
输出(在Go Playground上试试):
2 2000
1000 2000
x
和 y
多维切片总共有 2000 个元素(2000 字节),但是 x
只存储 2
个切片,每个切片有 1000
元素,而 y
存储 1000
个切片,每个切片有 2
个元素。
这意味着 x
需要 2
片 headers 而 y
需要 1000
片 headers (对于元素,+ 1 代表 x
和 y
他们自己)!
切片header表示为reflect.SliceHeader
:
type SliceHeader struct {
Data uintptr
Len int
Cap int
}
切片的大小 header 在 32 位架构上是 12 字节,在 64 位架构上是 24 字节。因此,对于 32 位 arch 元素,x
需要 2,000 字节加上内存中的 2x12 字节,即 2,024 字节,而 y
的元素需要 2,000 字节加上1,000*12 即 14,000 字节.
另请注意,多维切片的元素可能包含不同长度的切片:
With arrays of arrays, the inner arrays are, by construction, always the same length; however with slices of slices (or arrays of slices), the inner lengths may vary dynamically. Moreover, the inner slices must be initialized individually.
就像这个例子:
x := make([][]byte, 2)
x[0] = []byte{1, 2}
x[1] = []byte{1, 2, 3, 4}
fmt.Println(x[0])
fmt.Println(x[1])
输出(在Go Playground上试试):
[1 2]
[1 2 3 4]
如果没看过,推荐:The Go Blog: Arrays, slices (and strings): The mechanics of 'append'
对我来说,Go 中的 slice 就像 C++ 中的 vector
。 Vector
还有 3 个关键数据成员:数据指针、容量和长度。如果 vector
是 2d,则子向量的内部长度也可能动态变化。
相比之下,Go 中的数组看起来像 C 中的数组。
在Java中,二维数组是多个一维数组。这意味着那些在内存上不连续的一维数组。
相比之下,在C中,二维数组实际上就是一维数组,大小为total_row * total_column。因为Go语言使用了很多C语言的概念。
所以我的问题是:二维数组在 Go 中的内存表示看起来像 C 还是 Java?
在 Go 中,slices are often mistaken for arrays,所以我对两者都作了回答。
数组
Array types are always one-dimensional but may be composed to form multi-dimensional types.
你的答案很清楚。但答案不仅仅是因为数组在 Go 中是值,它们不像切片那样是描述符(headers)。
看这个简单的例子:
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
不管你换行换列,它的大小都是一样的。
切片
这同样适用于切片的情况:多维切片是切片的切片。 Spec: Slice types:
A slice is a descriptor for a contiguous segment of an underlying array and provides access to a numbered sequence of elements from that array.
...
Like arrays, slices are always one-dimensional but may be composed to construct higher-dimensional objects.
切片是描述符,切片 header 包含指向底层(后备)数组元素的指针、长度和容量。因此,总切片数在内存使用方面很重要。
看这个例子:
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]))
输出(在Go Playground上试试):
2 2000
1000 2000
x
和 y
多维切片总共有 2000 个元素(2000 字节),但是 x
只存储 2
个切片,每个切片有 1000
元素,而 y
存储 1000
个切片,每个切片有 2
个元素。
这意味着 x
需要 2
片 headers 而 y
需要 1000
片 headers (对于元素,+ 1 代表 x
和 y
他们自己)!
切片header表示为reflect.SliceHeader
:
type SliceHeader struct {
Data uintptr
Len int
Cap int
}
切片的大小 header 在 32 位架构上是 12 字节,在 64 位架构上是 24 字节。因此,对于 32 位 arch 元素,x
需要 2,000 字节加上内存中的 2x12 字节,即 2,024 字节,而 y
的元素需要 2,000 字节加上1,000*12 即 14,000 字节.
另请注意,多维切片的元素可能包含不同长度的切片:
With arrays of arrays, the inner arrays are, by construction, always the same length; however with slices of slices (or arrays of slices), the inner lengths may vary dynamically. Moreover, the inner slices must be initialized individually.
就像这个例子:
x := make([][]byte, 2)
x[0] = []byte{1, 2}
x[1] = []byte{1, 2, 3, 4}
fmt.Println(x[0])
fmt.Println(x[1])
输出(在Go Playground上试试):
[1 2]
[1 2 3 4]
如果没看过,推荐:The Go Blog: Arrays, slices (and strings): The mechanics of 'append'
对我来说,Go 中的 slice 就像 C++ 中的 vector
。 Vector
还有 3 个关键数据成员:数据指针、容量和长度。如果 vector
是 2d,则子向量的内部长度也可能动态变化。
相比之下,Go 中的数组看起来像 C 中的数组。