什么是数组
首先下一个定义,数组是对线性的内存区域的抽象。高维数组和一维数组有着同样的内存布局。(大学生考试的时候别借鉴哈,这是自己下的定义,相当于是一篇议论文的论点。)
线性的内存区域说白了就是连续的内存区域。无论一维数组、二维数组、N维数组都处在连续的内存区域中,数据排列是连续的。CPU缓存对连续的内存区域具有较高的亲和性,这也是数组的访问速度要快于链表的一个重要原因。
一维数组
以C语言中的一维数组为例:int array[3] = {1,2,3};,此时array保存的即数组的首地址,以该地址为首的连续的内存区域中,保存了1,2,3的值。
二维数组
二维数组可以理解成:是保存了一维数组首地址(指针)的一维数组。
 这是什么意思呢?以下面三个一维数组:array0、array1、array2为例,(前提,他们仨处于一块连续的内存区域上,且首尾无间断),假设它们的首地址分别是:0x0000,0x000C,0x0018。看一下这三个数字,它们每两个之间的差都是0xc。因为C语言中int类型占4个字节宽度。所以说每个一维数组占用的内存长度都是12个字节,并且array1数组首地址刚好是array0数组末地址+1的位置,array2和array1的关系亦是如此。
int array0[3] = {1,2,3};
int array1[3] = {4,5,6};
int array2[3] = {7,8,9};
那么这片连续的内存区域可以表示为这个样子:
   +----++----++----++----++----++----++----++----++----+
...|  1 ||  2 ||  3 ||  4 ||  5 ||  6 ||  7 ||  8 ||  9 |...+----++----++----++----++----++----++----++----++----+⬆                 ⬆                 ⬆0x0000            0x000c            0x0018array0            array1            array2
使用C语言定义一个一维指针数组:
 int *array[3] = {array0,array1,array2};,数组中保存三个一维数组的首地址。
用该数组指针模拟一下二维数组。
#include "stdio.h"
int main() {int array0[3] = {1,2,3};int array1[3] = {4,5,6};int array2[3] = {7,8,9};int *array[3] = {array0,array1,array2};for(int i = 0; i < 3; i++) {for(int j = 0; j < 3; j++){printf("%d\t",*(*array+j)+i*3);}printf("\n");}
}
输出的内容是:
1       2       3
4       5       6
7       8       9
他的内存布局是这样的:
   +----++----++----++----++----++----++----++----++----+
...|  1 ||  2 ||  3 ||  4 ||  5 ||  6 ||  7 ||  8 ||  9 |...+----++----++----++----++----++----++----++----++----+⬆                 ⬆                 ⬆0x0000            0x000c            0x0018array0            array1            array2+----------++----------++----------+
...|  0x0000  ||  0x000c  ||  0x0018  |...+----------++----------++----------+⬆array
我们不搞的那么麻烦,直接用C语言定义一个普通的二维数组:
#include "stdio.h"
int main() {int array[3][3] =  {{1,2,3},{4,5,6},{7,8,9}};for(int i = 0; i < 3; i++) {for( int j = 0; j < 3; j++) {printf("%d\t",array[i][j]);}printf("\n");}
}
其打印内容也是:
1       2       3
4       5       6
7       8       9
使用访问连续内存区域的方式*((*array)+i),打印该数组:
#include "stdio.h"
int main() {int array[3][3] =  {{1,2,3},{4,5,6},{7,8,9}};for( int i = 0; i < 3 * 3; i++) {printf("%d\t",*((*array)+i));}
}
其输出内容为:
1       2       3       4       5       6       7       8       9
其内存布局是这样的:
   +----++----++----++----++----++----++----++----++----+
...|  1 ||  2 ||  3 ||  4 ||  5 ||  6 ||  7 ||  8 ||  9 |...+----++----++----++----++----++----++----++----++----+⬆array
- 可以看出,二维数组是将元素连续排列的一维数组。
- 同理高维数组,也是将元素线性排列的一维数组。
- 这也佐证了我们 数组是对线性的内存区域的抽象。高维数组和一维数组有着同样的内存布局 的观点。
如何理解Go语言的数组
Go语言中的数组,和C语言的数组大同小异,数组的首地址也指向了一片连续的内存区域。这里的数组指的是array,而不是slice。
如何将一个一维数组映射成一个二维数组
仁者见仁,智者见智
第一种方式:
package mainimport "fmt"func main() {array0 := [12]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}var array [3][4]intfor i := 0; i < 12; i++ {array[i/4][i%4] = array0[i]}fmt.Println(array)
}
上述方式展示了利用计算下标的方式进行转化,其结果为:
[[1 2 3 4] [5 6 7 8] [9 10 11 12]]
第二种方式,终极大杀器

package mainimport ("fmt""unsafe"
)func main() {array0 := [12]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}var array = *(*[3][4]int)(unsafe.Pointer(&array0))fmt.Println(array)
}
结果为:
[[1 2 3 4] [5 6 7 8] [9 10 11 12]]
这种强制转化的方式,是利用了高维数组和一维数组有着同样的内存布局的这个原理,直接在类型层面做了一层转化,此时array 是 zero copy的,也就是说跟原数组共用同一片内存。二者只要更改任意一方元素,都会影响到对方。