【问题标题】:C++ and Java array declaration/definition : differencesC++ 和 Java 数组声明/定义:差异
【发布时间】:2015-09-26 00:28:55
【问题描述】:

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

谁能解释一下这是怎么回事?也许窗帘后面发生的事情应该清楚。谢谢。

【问题讨论】:

  • In Java, it doesn't seem to care about array size when defining an array reference variable (it's an error in Java to explicitly provide a size 这不是真的。你能给出一个关于这个的编码示例吗?
  • 首先要记住的是 C/C++ 没有多维数组。
  • @user3437460 int [10] 数组;数组引用变量。在 Java 中是非法的
  • @Luca 那是因为int[10] array 在 Java 中是错误的语法。您可以在 Java int [] array = new int[10] 中提供数组大小。我在回答你的问题吗?
  • @user3437460 我认为你应该更仔细地阅读这个问题。并区分引用变量和对象实例。

标签: java c++ arrays


【解决方案1】:

这是因为在 Java 中,所有数组都是一维的。 Java 中的二维数组仅仅是对一维数组的引用数组。 Java 中的三维数组只是一个一维数组,其中包含对数组的引用,这些数组对您想要的任何基本类型的数组的引用。

或者用 C++ 来说,Java 中的数组,如果它不是原始数组,它就是“指针数组”。

所以,例如,这段代码:

    int[][][] arr3D = new int [5][][];

    System.out.println(Arrays.deepToString(arr3D));

会产生输出:

[空,空,空,空,空]

您可以决定初始化其中一个元素:

    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 个 int,它怎么会跳过那么多字节呢?

此外,什么时候是 3x5 或 5x3 数组?如果需要去元素mm_array[0][2][1],是需要跳过两行五个元素,还是跳过两行三个元素?

这就是为什么它需要知道,在编译时,它的基本数组的大小。由于指针中没有关于大小的信息,并且仅指向一个连续的整数块,因此需要提前知道该信息。

在 Java 中,情况有所不同。数组本身及其子数组都是 Java 对象。每个数组都是一维的。当你有这样的表达时

arr3D[0][1][2]

arr3D 已知是对数组的引用。该数组具有长度和类型信息,以及一维引用。它可以检查0 是否为有效索引,并取消引用0th 元素,该元素本身就是对数组的引用。

这意味着现在它再次具有类型和长度信息,然后是单一维度的引用。它可以检查1 是否是该数组中的有效索引。如果是,它可以转到该元素,并取消引用它,并获取最里面的数组。

由于数组不是一个连续的块,而是对对象的引用,所以您不需要在编译时知道大小。一切都是动态分配的,只有第三层(在这种情况下)有实际连续的整数——只有一个维度,不需要预先计算。

【讨论】:

  • 这解释了为什么您必须提供一个常量表达式作为所有维度的大小(为动态“数组”保存第一个),以及为什么在 Java 中您只需要提供第一个。但这并没有解释为什么 Java 中的引用变量(指向 C++ 的数组类型的指针)声明时没有大小
  • @Luca 它不需要大小,因为它不需要知道如何将分配给它的长连续块划分为行、列等。因为没有这样的块会分配给它。它只有一个维度。
  • mmmm...我想我开始明白了,但我还没有。您能否提供更明确的解释,或指出一些我可以阅读以理解的资源?
  • @Luca 我添加了进一步的解释,我希望它已经足够了。
  • 当 Java 从堆栈中的前身对象中丢失时,区域中的对象“就像 Fortran 中的块数据“true”c 静态常量。丢失的常驻“胖”对象仅通过引用许多人认为更简单。语言是更简单,编译器更简单,可以更好地学习。从设计开始:对于受过大众教育的程序员来说应该是语言。
【解决方案2】:

我想您真正的问题是,为什么堆栈数组在编译时必须具有固定大小。

首先,这样可以更容易地计算以下局部变量的地址。

堆栈数组的动态大小并非不可能,它只是更复杂,正如您想象的那样。

C99 确实支持堆栈上的可变长度数组。一些 C++ 编译器也支持此功能。另见Array size at run time without dynamic allocation is allowed?

【讨论】:

    【解决方案3】:

    我相信这与编译器发出什么代码来处理数组有关。对于 动态 数组,您有一个 数组数组,并且通过重定向重定向来寻址单元格。

    多维数组存储在连续内存中,编译器使用数学公式对它们进行索引,以根据每个数组计算单元格位置数组的维度。

    因此,编译器需要知道(声明)维度(最后一个除外)。

    【讨论】:

    • 它没有解释为什么引用声明时没有大小
    • @Luca 数组引用,就像数组需要声明除一个维度之外的所有维度的大小,因为最后一个维度只是标量,但其他维度需要相乘才能计算单元格索引。
    • @Luca 抱歉弄错了,数组引用需要声明它们看起来的所有维度。 (自从我在 C++ 中使用数组以来)。
    • 不,Galik,在Java中,当你声明一个数组引用变量时,你不能声明大小。如果你这样做是错误的。
    【解决方案4】:

    更正:

    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
    }
    

    【讨论】:

      【解决方案5】:

      在Java中,定义数组引用变量时似乎并不关心数组大小(在Java中显式提供大小是错误的),

      当你定义一个数组时,并不是 Java 不关心初始数组的大小。 Java中数组的概念与C/C++几乎完全不同。

      首先,在 Java 中创建数组的语法已经不同。 声明数组时在 Java 中仍然看到 C/C++ 类似方括号的原因是因为在实现 Java 时,它们试图尽可能地遵循 C/C++ 的语法。

      来自 Java 文档

      与其他类型变量的声明一样,数组声明有两个组成部分:数组类型和数组名称。数组的类型写为 type[],其中 type 是所包含元素的数据类型; 方括号是特殊符号,表示此变量包含一个数组。数组的大小不是其类型的一部分这就是括号为空的原因

      当你在 Java 中声明一个数组时,例如:

      int[] array;
      

      您只是在创建一个对象,Java 将其称为数组(其行为类似于数组)。

      括号[ ] 只是表示这是一个数组对象的符号。你怎么能将数字插入到 Java 使用它来创建数组对象的特定符号中!!

      括号看起来像我们在 C/C++ 数组声明中使用的。但是Java赋予它不同的含义,语法看起来像 C/C++。

      Java 文档中的另一个描述

      在声明符中允许使用括号作为对 C 和 C++ 传统的致敬。


      部分问题:

      这允许我创建锯齿状数组,我不确定我是否可以在 C++ 中创建(不是指针数组)。

      来自 Java 文档:

      在 Java 编程语言中,多维数组是一个数组,其组件本身就是数组。这与 C 或 Fortran 中的数组不同。这样做的结果是允许行的长度不同

      如果您有兴趣了解有关 Java 数组的更多信息,请访问:

      【讨论】:

      • 备注:此答案并未完全解决 OP 提出的所有问题,因为回答他的所有问题需要更长的时间。然而,这个答案确实回答了 OP 问题的某些方面。特别是在 Java 空括号声明中。
      【解决方案6】:

      C++ 和 Java 中的 in 数组之间的区别在于,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++ 数组是指针呢?好吧,它们是指针(因为您可以用它们进行指针运算)但它们是 constant 指针,其值实际上并未存储在程序中,而是在编译时已知。因此,以下 C++ 代码将无法编译:

      int a[2];
      int b[2];
      a = b;
      

      【讨论】:

        【解决方案7】:

        您混淆了某些 C++ 数组的含义: 例如,您的 'm_array' 是指向值数组的指针 - 请参阅以下可编译的 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;
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2017-11-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-03-29
          • 1970-01-01
          相关资源
          最近更新 更多