我的问题很简单(这并不意味着答案会很简单..:D)
为什么C++中的数组包含大小作为类型的一部分而Java不包含?
我知道Java数组引用变量只是指向堆上数组的指针,但C++指向数组的指针也是如此,但我需要提供一个大小.让我们先分析一下C++:
// in C++ :
// an array on the stack:
int array[*constexpr*];
// a bidimensional array on the stack:
int m_array[*constexpr1*][*constexpr2*];
// a multidimensional array on the stack:
int mm_array[*constexpr1*][*constexpr2*][*constexpr3*];
// a dynamic "array" on the heap:
int *array = new int[n];
// a dynamic bidimensional "array" on the heap:
int (*m_array)[*constexpr*] = new int[n][*constexpr*];
// a dynamic multidimensional "array" on the heap:
int (*mm_array)[*constexpr*][*constexpr*] = new int [n][*constexpr1*][*constexpr2*];
Run Code Online (Sandbox Code Playgroud)
n不必是编译时常量表达式,所有元素都是默认初始化的.动态分配的"数组"不是类型数组,但新表达式产生指向第一个元素的指针.
因此,当我创建一个动态数组时,除了第一个维度之外的所有维度都必须是常量表达式(否则我无法声明指针来保存它们的元素).这样对吗??
现在到Java.I只能在堆上分配数组,因为这是Java的工作方式:
// a dynamic array on the heap:
int[] array = new int[n];
// a dynamic bidimensional array on the heap:
int[][] m_array = new int[n][];
// a dynamic multidimensional array on the heap:
int[][][] mm_array = new int [n][][];
Run Code Online (Sandbox Code Playgroud)
在Java中,它在定义数组引用变量时似乎并不关心数组大小(在Java中显式提供大小是一个错误),因此我只需要在创建数组时为第一个维度提供大小.这允许我创建锯齿状数组,我不确定我可以在C++中创建(不是指针数组).
谁能解释我是怎么回事?也许窗帘背后发生的事情应该说清楚.谢谢.
那是因为在Java中,所有数组都是单维的.Java中的二维数组仅仅是对一维数组的引用数组.Java中的三维数组只是对所需基类型数组的引用数组的一维引用数组.
或者在C++中,用Java编写一个数组,如果它不是一个原始数组,它就是一个"指针数组".
所以,例如,这段代码:
int[][][] arr3D = new int [5][][];
System.out.println(Arrays.deepToString(arr3D));
Run Code Online (Sandbox Code Playgroud)
会产生输出:
[null, null, null, null, null]
您可以决定初始化其中一个元素:
arr3D[2] = new int[3][];
Run Code Online (Sandbox Code Playgroud)
而println现在的输出将是:
[null, null, [null, null, null], null, null]
这里仍然没有注意......现在我们可以添加:
arr3D[2][2] = new int[7];
Run Code Online (Sandbox Code Playgroud)
现在结果将是:
[null, null, [null, null, [0, 0, 0, 0, 0, 0, 0]], null, null]
所以,你可以看到这是一个"指针数组".
在C++中,当您按照描述的方式分配多维数组时,您将分配一个连续的数组,该数组实际上包含数组的所有维度,并一直初始化为整数.为了能够知道它是10x10x10阵列还是100x10阵列,你必须提到尺寸.
进一步说明
在C++中,声明
int (*mm_array)[5][3];
Run Code Online (Sandbox Code Playgroud)
表示"mm_array是指向5x3整数数组的指针".当你为它分配东西时,你希望那个东西是一个指向连续内存块的指针,它至少足以包含15个整数,或者可能是一个包含几个这样的5x3数组的数组.
假设你没有提到"5"和"3".
int (*mm_array)[][]; // This is not a legal declaration in C++
Run Code Online (Sandbox Code Playgroud)
现在,假设你有一个指向新分配数组的指针,我们有如下语句:
mm_array[1][1][1] = 2;
Run Code Online (Sandbox Code Playgroud)
要么
mm_array++;
Run Code Online (Sandbox Code Playgroud)
为了知道在哪里放数,它需要知道数组索引1的位置.元素0很简单 - 它就在指针处.但元素1在哪里?之后它应该是15英寸.但是在编译时,你不会知道,因为你没有给出尺寸.同样适用于++.如果它不知道数组的每个元素是15个整数,它将如何跳过那么多字节?
此外,它何时是3x5或5x3阵列?如果需要转到元素mm_array[0][2][1],是否需要跳过两行五个元素,或两行三个元素?
这就是为什么它需要在编译时知道其基本数组的大小.由于指针没有关于其中的大小的信息,并且仅指向连续的整数块,因此需要事先知道该信息.
在Java中,情况有所不同.数组本身及其子数组都是Java对象.每个阵列都是一维的.当你有一个表达式
arr3D[0][1][2]
Run Code Online (Sandbox Code Playgroud)
arr3D已知是对数组的引用.该数组具有长度和类型信息,以及引用的一个维度.它可以检查是否0是有效索引,并取消引用0th元素,该元素本身是对数组的引用.
这意味着现在它再次具有类型和长度信息,然后是单个引用维度.它可以检查1该数组中是否为有效索引.如果是,它可以转到该元素,并取消引用它,并获取最内层的数组.
由于数组不是连续的块,而是对象的引用,因此您无需在编译时知道大小.一切都是动态分配的,只有第三级(在这种情况下)中有实际的连续整数 - 只有一个维度,不需要提前计算.