С++ и объявление/определение массива Java: отличия

мой вопрос действительно прост (что не означает, что ответ будет таким же простым..: D)

почему массивы в С++ включают размер как часть типа, а Java нет?

Я знаю, что ссылочные переменные массива Java являются просто указателями на массивы в куче, но также являются указателями С++ для массивов, но мне нужно предоставить размер даже тогда. Сначала проанализировать С++:

// 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, чтобы явно предоставить размер), и поэтому мне просто нужно предоставить размер для первого измерения при создании массив. Это позволяет мне создать jagged массив, который я не уверен, что могу создать на С++ (не массивы указателей).

Может кто-нибудь объяснить мне, как это? возможно, что происходит за шторами, должно быть ясно. Спасибо.

Ответ 1

Это потому, что в Java все массивы одномерны. Двумерный массив в Java - это всего лишь массив ссылок на одномерные массивы. Трехмерный массив в Java является всего лишь одномерным массивом ссылок на массивы ссылок на массивы любого базового типа, который вам нужен.

Или в С++ говорят, массив в 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]

По-прежнему нет ints здесь... Теперь мы можем добавить:

    arr3D[2][2] = new int[7];

И теперь результат будет:

[null, null, [null, null, [0, 0, 0, 0, 0, 0, 0]], null, null]

Итак, вы можете видеть, что это "массив указателей".

В С++, когда вы выделяете многомерный массив так, как вы описали, вы выделяете непрерывный массив, который фактически содержит все размеры массива и инициализируется вплоть до int. Чтобы узнать, есть ли массив 10x10x10 или массив 100x10, вы должны указать размеры.

Дальнейшее объяснение

В С++ объявление

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 ints. Но во время компиляции вы не узнаете этого, потому что вы не дали размеров. То же самое относится к ++. Если он не знает, что каждый элемент массива равен 15 ints, как он пропустит это количество байтов?

Кроме того, когда это массив 3x5 или 5x3? Если нужно перейти к элементу mm_array[0][2][1], нужно ли пропускать две строки из пяти элементов или две строки из трех элементов?

Вот почему он должен знать во время компиляции, размер его базового массива. Поскольку указатель не имеет информации о размерах в нем и просто указывает на непрерывный блок целого числа, эта информация должна быть известна заранее.

В Java ситуация другая. Сам массив и его подматрицы - все объекты Java. Каждый массив является одномерным. Когда у вас есть выражение типа

arr3D[0][1][2]

arr3D известен как ссылка на массив. Этот массив имеет информацию о длине и типе и один размер ссылок. Он может проверить, является ли 0 допустимым индексом и разыменовывает элемент 0 th, который сам является ссылкой на массив.

Это означает, что теперь он снова имеет информацию о типе и длине, а затем одно измерение ссылок. Он может проверить, является ли 1 допустимым индексом в этом массиве. Если это так, он может перейти к этому элементу и разыскать его и получить самый внутренний массив.

Поскольку массивы не являются смежным блоком, а скорее ссылками на объекты, вам не нужно знать размеры во время компиляции. Все распределяется динамически, и только третий уровень (в данном случае) имеет в нем фактические смежные целые числа - только одно измерение, которое не требует предварительного расчета.

Ответ 2

Я думаю, ваш реальный вопрос в том, почему массив стека должен иметь фиксированный размер во время компиляции.

Ну, во-первых, это упрощает вычисление адресов следующих локальных переменных.

Динамический размер для массива стека не является невозможным, он просто сложнее, как вы могли себе представить.

C99 поддерживает массивы переменной длины в стеке. Некоторые компиляторы С++ также поддерживают эту функцию. См. Также Разрешен ли размер массива во время выполнения без динамического распределения?

Ответ 3

Исправление:

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
}

Ответ 4

Я считаю, что это связано с тем, какой код компилятор выдает для обращения к массиву. Для динамических массивов у вас есть массив массивов, а ячейки адресованы перенаправлением перенаправления.

Но многомерные массивы хранятся в непрерывной памяти, а компилятор индексирует их, используя математическую формулу для вычисления позиции ячейки на основе каждого из размеров массива.

Поэтому размеры должны быть известны (объявлены) компилятору (все, кроме последнего).

Ответ 5

В Java, по-видимому, не заботятся о размере массива при определении ссылочной переменной массива (это ошибка в Java, чтобы явно предоставить размер),

Ядро не заботится о начальном размере массива при определении массива. Концепция массива в Java практически полностью отличается от C/С++.

Прежде всего, синтаксис для создания массива в Java уже отличается. Причина, по которой вы все еще видите квадратные скобки C/С++ в Java при объявлении массивов, заключается в том, что когда Java была реализована, они старались как можно больше следовать синтаксису C/С++.

Из документов Java:

Подобно объявлениям для переменных других типов, объявление массива имеет два компонента: тип массива и имя массива. Тип массива записывается как type [], где type - тип данных содержащихся элементов; скобки являются специальными символами, указывающими, что эта переменная содержит массив. размер массива не является частью его типа (поэтому скобки пусты)

Когда вы объявляете массив в Java, например:

int[] array;

Вы просто создаете объект, который Java назвал его массивом (который действует как массив).

Скобки [ ] являются просто символом, указывающим, что это объект Array. Как вы могли вставлять числа в определенный символ, который Java использует для создания объекта Array!

Скобки выглядят так, как мы использовали в объявлении массива C/С++. Но Java дает для него другое значение, поэтому синтаксис выглядит как C/С++.

Другое описание из документов Java:

Кванты разрешены в деклараторах как знак традиций C и С++.


Часть вашего вопроса:

Это позволяет мне создать jagged массив, который я не уверен, что могу создать на С++ (а не массивы указателей).

Из Java Docs:

В языке программирования Java многомерный массив представляет собой массив, чьи компоненты сами являются массивами. Это в отличие от массивов в C или Fortran. Следствием этого является то, что строкам разрешено изменять длину

Если вам интересно узнать больше о массивах Java, посетите:

Ответ 6

Разница между массивами в С++ и Java заключается в том, что массивы Java являются ссылками, как и все непримитивные объекты Java, в то время как массивы С++ не являются, как и все объекты С++ (да, вы слышите много, что массивы С++ похожи на указатели, но см. ниже).

Объявление массива в С++ выделяет память для массива.

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;

А как насчет массива С++? Ну, это указатели (потому что вы можете делать с ними арифметику указателей), но они являются постоянными указателями, значение которых фактически не хранится в программе, а известно во время компиляции. По этой причине следующий код С++ не будет компилироваться:

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

Ответ 7

Вы вводите в заблуждение значение некоторых из ваших С++-массивов: например, ваш "m_array" является указателем на массив значений - см. следующий компилируемый пример С++:

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;