C ++和Java数组声明/定义:差异

时间:2015-09-24 17:03:22

标签: java c++ arrays

我的问题很简单(这并不意味着答案会很简单......: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 ++中创建(不是指针数组)。

有人能解释我是怎么回事吗?也许窗帘背后发生的事情应该说清楚。感谢。

7 个答案:

答案 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;