二维数组的内存布局(Go、C++、Java)

964 阅读3分钟

引言

首先,毫无疑问的说数组下标都是从0开始的。

其次,对于一维数组,数组是线性数据结构,内存空间的地址是连续的

然而,对于二维数组,不同语言可能有所区别。

不同编程语言的内存管理是不一样的,本文分析了Go、C++、java三种语言的二维数组的内存地址布局。

Go 二维数组

Go中二维数组是连续分布的。

我们来做一个实验,Go测试代码如下:

package main
import "fmt"

func main() {
    var intArr [2][3]int ;
    // 默认为零 
    // [[0 0 0] [0 0 0]]
    
    // 赋值
    intArr[1][0] = 1
    intArr[1][1] = 2
    intArr[1][2] = 3

    // 遍历二维数组 for-range
    // 0 0 0
    // 1 2 3
    for _,values := range intArr {
        for _ ,val := range values {
            fmt.Print(val," ")
        }
        fmt.Println()
    }
    // 二维数组在内存的布局
    // 
    // intArr的地址0xc000086030

    // intArr[0]的地址0xc000086030
    // intArr[0][0]的地址0xc000086030
    // intArr[0][1]的地址0xc000086038    0xc000086038 = 0xc000086030 + 8
    // intArr[0][2]的地址0xc000086040    0xc000086040 = 0xc000086038 + 8

    // intArr[1]的地址0xc000086048       0xc000086048 = 0xc000086030 + 8*3
    // intArr[1][0]的地址0xc000086048    
    // intArr[1][1]的地址0xc000086050    0xc000086050 = 0xc000086048 + 8
    // intArr[1][2]的地址0xc000086058    0xc000086058 = 0xc000086050 + 8

    // 遍历二维数组 for 
    fmt.Printf("intArr的地址%p \n\n",&intArr)
    for i := 0; i <len(intArr); i++ {
        fmt.Printf("intArr[%v]的地址%p \n",i,&intArr[i])
        for j:=0; j < len(intArr[i]);j++{
            fmt.Printf("intArr[%v][%v]的地址%p \n",i,j,&intArr[i][j])
        }
        fmt.Println()
    }
}

地址为16进制,可以看出二维数组地址是连续一条线性的。

友友可能看不懂内存地址,我就简单介绍一下, 0xc000086040 与 0xc000086048 差了一个8,就是8个字节,因为这是一个int型的数组,所以两个相邻数组元素地址差8个字节。

C++ 二维数组

C++中二维数组也是连续分布的。

我们来做一个实验,C++测试代码如下:

#include <iostream>

using namespace std;

int main()
{
    int array[2][3] = {
		{0, 1, 2},
		{3, 4, 5}
    };
    cout << &array[0][0] << " " << &array[0][1] << " " << &array[0][2] << endl;
    cout << &array[1][0] << " " << &array[1][1] << " " << &array[1][2] << endl;
    
    //0x7ffc3ba35620 0x7ffc3ba35624 0x7ffc3ba35628
    //0x7ffc3ba3562c 0x7ffc3ba35630 0x7ffc3ba35634

    return 0;
}

可以看出在C++中二维数组在地址空间上是连续的。0x7ffc3ba35628 与 0x7ffc3ba3562c 差了一个4,C++中int型占4字节,所以两个相邻数组元素地址差4个字节。

Java 二维数组

Java是没有指针的,同时也不对程序员暴露其元素的地址,寻址操作完全交给虚拟机。

所以看不到每个元素的地址情况,这里我以Java为例,也做一个实验。

public class Main
{
	public static void main(String[] args) {
		int[][] arr = {{1, 2, 3}, {3, 4, 5}, {6, 7, 8}, {9,9,9}};
        System.out.println(arr[0]);
        System.out.println(arr[1]);
        System.out.println(arr[2]);
        System.out.println(arr[3]);
        
        // [I@5fdef03a
        // [I@3b22cdd0
        // [I@1e81f4dc
        // [I@4d591d15
	}
}

这里的数值也是16进制,这不是真正的地址,而是经过处理过后的数值了,我们也可以看出,二维数组的每一行头结点的地址是没有规则的,更谈不上连续。

所以Java的二维数组可能是如下排列的方式:

java二维数组

总结

不同编程语言的内存管理是不一样的,对于二维数组,Go和C++语言的地址空间是连续的,而Java语言中不同行之间的地址是无序的。