我的问题确实很简单(这并不意味着答案就这么简单..: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*];
n不必是编译时常量表达式,所有元素都是默认初始化的。动态分配的“数组”不是数组类型,但是新表达式产生指向第一个元素的指针。
因此,当我创建一个动态数组时,除第一个维之外的所有维都必须是常量表达式(否则我无法声明指针来保存其元素)。这样对吗??
现在到Java。由于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][][];
在Java中,定义数组引用变量时似乎并不关心数组大小(在Java中显式提供大小是错误的),因此在创建数组时,我只需要为第一个维度提供大小。这使我可以创建锯齿状的数组,但不确定是否可以在C++中创建(不是指针数组)。
有人可以解释我的意思吗?也许幕后发生的事情应该弄清楚。谢谢。
最佳答案
这是因为在Java中,所有数组都是一维的。 Java中的二维数组仅仅是对一维数组的引用的数组。 Java中的三维数组仅仅是对引用数组的引用的一维数组,而对您想要的任何基本类型的数组的引用都是如此。
或用C++讲,Java中的数组,如果不是原始数组,则是“指针数组”。
因此,例如,此代码:
int[][][] arr3D = new int [5][][];
System.out.println(Arrays.deepToString(arr3D));
将产生输出:
[null, null, null, null, null]
You can decide to initialize one of its elements:
arr3D[2] = new int[3][];
现在,相同
println
的输出为:[null, null, [null, null, null], null, null]
Still no ints here... Now we can add:
arr3D[2][2] = new int[7];
现在的结果是:
[null, null, [null, null, [0, 0, 0, 0, 0, 0, 0]], null, null]
So, you can see that this is an "array of pointers".
In C++, when you allocate a multi-dimensional array the way you described, you are allocating a contiguous array which actually holds all the dimensions of the array and is initialized all the way through to the ints. To be able to know whether it's a 10x10x10 array or a 100x10 array, you have to mention the sizes.
Further explanation
In C++, the declaration
int (*mm_array)[5][3];
表示“mm_array是指向5x3整数数组的指针”。当给它分配东西时,您希望它是指向连续内存块的指针,该内存块至少足以容纳15个整数,或者可能是几个这样的5x3数组的数组。
假设您没有提到“5”和“3”。
int (*mm_array)[][]; // This is not a legal declaration in C++
现在,假设您已获得一个指向新分配的数组的指针,并且我们有如下语句:
mm_array[1][1][1] = 2;
要么
mm_array++;
为了知道将数字放在哪里,它需要知道数组的索引1在哪里。元素0很简单-就在指针处。但是元素1在哪里?在那之后应该是15个整数。但是在编译时,您不会知道,因为您没有提供大小。
++
也是如此。如果不知道数组的每个元素都是15个整数,它将如何跳过那么多字节?此外,何时是3x5或5x3阵列?如果需要转到
mm_array[0][2][1]
元素,是否需要跳过两行,每行包含五个元素,或者跳过两行,每行包含三个元素?这就是为什么它需要在编译时知道,它的基本数组的大小的原因。由于指针中没有关于大小的信息,而仅指向连续的整数块,因此该信息将需要事先知道。
在Java中,情况有所不同。数组本身及其子数组都是Java对象。每个阵列都是一维的。当你有一个像
arr3D[0][1][2]
已知
arr3D
是对数组的引用。该数组具有长度和类型信息,以及一维引用。它可以检查0
是否为有效索引,并取消对0
th元素的引用,该元素本身是对数组的引用。这意味着现在它再次具有类型和长度信息,然后具有单个引用维度。它可以检查
1
在该数组中是否为有效索引。如果是这样,它可以转到该元素,然后取消引用它,并获取最里面的数组。由于数组不是连续的块,而是对对象的引用,因此您不需要在编译时就知道大小。一切都是动态分配的,只有第三个级别(在这种情况下)中具有实际连续的整数-只有一个维,不需要预先计算。