我的问题很简单(这并不意味着答案会很简单......: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.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][][];
在Java中,在定义数组引用变量时它似乎并不关心数组大小(它是Java中的一个错误,用于显式提供大小),因此我只需要提供大小对于创建数组时的第一个维度。这允许我创建锯齿状数组,我不确定我可以在C ++中创建(不是指针数组)。
有人能解释我是怎么回事吗?也许窗帘背后发生的事情应该说清楚。感谢。
答案 0 :(得分:8)
那是因为在Java中,所有数组都是单维的。 Java中的二维数组仅仅是对一维数组的引用数组。 Java中的三维数组只是对所需基类型数组的引用数组的一维引用数组。
或者在C ++中,用Java编写一个数组,如果它不是一个原始数组,它就是一个"指针数组"。
所以,例如,这段代码:
int[][][] arr3D = new int [5][][];
System.out.println(Arrays.deepToString(arr3D));
会产生输出:
[null, null, null, null, null]
您可以决定初始化其中一个元素:
arr3D[2] = new int[3][];
同一println
的输出现在是:
[null, null, [null, null, null], null, null]
这里仍然没有注意......现在我们可以添加:
arr3D[2][2] = new int[7];
现在结果将是:
[null, null, [null, null, [0, 0, 0, 0, 0, 0, 0]], null, null]
所以,你可以看到这是一个"指针数组"。
在C ++中,当您按照描述的方式分配多维数组时,您将分配一个连续的数组,该数组实际上包含数组的所有维度,并一直初始化为整数。为了能够知道它是10x10x10阵列还是100x10阵列,你必须提到尺寸。
进一步说明
在C ++中,声明
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
元素,它本身就是对数组的引用。
这意味着现在它再次具有类型和长度信息,然后是单个引用维度。它可以检查1
是否是该数组中的有效索引。如果是,它可以转到该元素,并取消引用它,并获得最内层的数组。
由于数组不是连续的块,而是对象的引用,因此您不需要在编译时知道大小。一切都是动态分配的,只有第三级(在这种情况下)中有实际的连续整数 - 只有一个维度,不需要提前计算。
答案 1 :(得分:1)
我猜你真正的问题是,为什么堆栈数组在编译时必须有一个固定的大小。
嗯,首先,这样可以更容易地计算以下局部变量的地址。
堆栈数组的动态大小并非不可能,它就像你想象的那样复杂得多。
C99支持堆栈上的可变长度数组。一些C ++编译器也支持此功能。另请参阅Array size at run time without dynamic allocation is allowed?
答案 2 :(得分:0)
校正:
C 有时 具有维度
Java
Sometype some[];
声明本身是对Object的引用(声明),可以更改(到新实例或数组)。这可能是一个原因所以在java维度中不能给出“在左侧”。它接近
Sometype * some
C中的(原谅我,Java中的数组更加智能和安全) 如果我们考虑将数组传递给C函数,正式情况就像在Java中一样。我们不仅没有维度,也没有维度。
void func(Sometype arg[])
{
// in C totally unknown (without library / framework / convention etc)
// in Java formally not declared, can be get at runtime
}
答案 3 :(得分:0)
我认为这与编译器解决数组的代码有关。对于动态数组,您有一个数组数组,并通过重定向重定向来处理单元格。
但多维数组存储在连续内存中,编译器使用数学公式对它们进行索引,以根据每个数据计算单元格位置数组的维度。
因此,需要知道(声明)维度到编译器(除最后一个之外的所有维度)。
答案 4 :(得分:0)
在Java中,在定义数组引用变量时似乎并不关心数组大小(在Java中显式提供大小是一个错误),
定义数组时,Java不关心初始数组大小。 Java中数组的概念几乎与C / C ++完全不同。
首先,在Java中创建数组的语法已经不同了。 在声明数组时,你仍然在Java中看到C / C ++看似相似的方括号的原因是因为在实现Java时,他们试图尽可能地遵循C / C ++的语法。
来自Java文档:
与其他类型的变量的声明一样,数组声明有两个组件:数组的类型和数组的名称。数组的类型写为type [],其中type是包含元素的数据类型; 括号是特殊符号 ,表示此变量包含数组。数组 的大小不属于其类型 (这是括号为空的原因)
在Java中声明数组时,例如:
int[] array;
你只是在创建一个Java,它称之为数组(它就像一个数组)。
括号[ ]
仅仅是符号,表示这是一个Array对象。如何将数字插入到Java使用它创建数组对象的特定符号中!!
括号看起来像我们在C / C ++数组声明中使用的那样。但是Java给语法提供了不同的含义看起来像 C / C ++。
来自Java文档的另一个描述:
允许在声明符中使用括号作为对C和C ++传统的点头。
您的部分问题:
这允许我创建锯齿状数组,我不确定我可以在C ++中创建(不是指针数组)。
来自Java Docs:
在Java编程语言中,多维数组是组件本身就是数组的数组。与
或Fortran中的数组不同,这是。其结果是允许行的长度变化
如果您有兴趣了解有关Java阵列的更多信息,请访问:
答案 5 :(得分:0)
C ++和Java中数组之间的区别在于Java数组是引用,就像所有非原始Java对象一样,而C ++数组不像所有C ++对象一样(是的,你听到很多C ++数组就像指针一样) ,但见下文)。
在C ++中声明一个数组为数组分配内存。
int a[2];
a[0] = 42;
a[1] = 64;
完全合法。但是,要为阵列分配内存,您必须知道它的大小。
在Java中声明一个数组不会为数组分配内存,仅用于引用,所以如果你这样做:
int[] a;
a[0] = 42;
您将获得NullPointerException
。首先必须构造数组(以及在Java中,构造需要知道其大小的数组):
int[] a = new int[2];
a[0] = 42;
a[1] = 64;
那么C ++数组是指针呢?好吧,它们是指针(因为你可以用它们做指针运算)但它们是常量指针,它们的值实际上并不存储在程序中但在编译时是已知的。因此,以下C ++代码将无法编译:
int a[2];
int b[2];
a = b;
答案 6 :(得分:0)
您混淆了某些C ++数组的含义: 例如,您的&#m; mray'是一个指向值数组的指针 - 请参阅以下可编译的C ++示例:
int array_of_values[3] = { 1, 2, 3 };
int (*m_array)[3] = &array_of_values;
等效的Java是:
int[] array_of_values = {1, 2, 3};
int[] m_array = array_of_values;
同样,你的' mm_array'是指向数组数组的指针:
int array_of_array_of_values[3][2] = { 1, 2, 3, 4, 5, 6 };
int (*mm_array)[3][2] = &array_of_array_of_values;
等效的Java是:
int[][] array_of_array_of_values = { {1, 2}, {3, 4}, {5, 6} };
int[][] mm_array = array_of_array_of_values;