Почему я могу объявить 2D-массив с переменной размера, но не новой?

Как указала проблема, это выполнимо:

#include <iostream>

int main(int argc, char *argv[])
{
unsigned short int i;
std::cin >> i;
unsigned long long int k[i][i];
}

Здесь я объявил массив размером i с i, оба измерения являются переменными.

Но не это:

#include <iostream>

int main(int argc, char *argv[])
{
unsigned short int i;
std::cin >> i;
unsigned long long int** k = new int[i][i];
delete[] k;
}

Я получил сообщение компилятора о том, что

ошибка: только первое измерение выделенного массива может иметь динамический
размер

Я вынужден сделать это:

#include <iostream>

int main(int argc, char *argv[])
{
unsigned short int i;
std::cin >> i;
unsigned long long int** k = new unsigned long long int*[i];
for ( unsigned short int idx = 0 ; idx < i ; ++ i )
k[idx] = new unsigned long long int[i];
for ( unsigned short int idx = 0 ; idx < i ; ++ i )
delete[] k[idx];
delete[] k;
}

Насколько я понимаю, new и delete используются для размещения чего-либо в куче, а не в стеке, что не будет удаляться, когда оно выходит из области видимости, и полезно для передачи данных между функциями и объектами и т. Д.

Я не понимаю, что происходит, когда я заявляю, что k в первом примере мне говорят, что объявленный массив должен (и мог) иметь только постоянные измерения, а когда требуется массив неизвестного размера, всегда следует учитывать new & delete или же vectors.

Есть ли плюсы и минусы этих двух решений, которые я не получаю, или это только то, что есть?

Кстати, я использую компилятор LLVM от Apple.

0

Решение

Я уверен, что вы можете легко найти реализацию для функциональности 2D-массива, но вы также можете создать свой собственный класс. Самый простой способ — это использовать std::vector хранить данные и иметь функцию отображения индекса, которая берет ваши две координаты и возвращает один индекс в вектор.

Код клиента будет выглядеть немного иначе, вместо arr[x][y] у вас есть arr.at (x, y), но в остальном он делает то же самое. Вам не нужно возиться с управлением памятью, как это делается std::vectorПросто используйте v.resize(N*N) в конструкторе или функции установки размеров.

0

Другие решения

Ни одна из форм не совместима со стандартом C ++, поскольку стандарт не поддерживает массивы переменной длины (VLA) (интересно, что C99 поддерживает, но C не является C ++). Однако некоторые компиляторы имеют расширение для поддержки этого, включая ваш компилятор:

Из Руководства Clang:

Clang поддерживает такие массивы переменной длины в очень ограниченных случаях для совместимости с программами GNU C и C99:

  • Тип элемента массива переменной длины должен быть типом POD («обычные старые данные»), что означает, что он не может иметь никаких объявленных пользователем конструкторов или деструкторов, каких-либо базовых классов или любых членов не-POD типа. Все типы C являются типами POD.
  • Массивы переменной длины нельзя использовать в качестве типа нетипичного параметра шаблона.

Но, учитывая, что расширение на месте, почему не работает ваш второй фрагмент? Это потому что VLA применяется только к автоматическим переменным — то есть аргументы или локальные переменные. k является автоматическим, но это просто указатель — сам массив определяется new int[i][i], который выделяет в куче и решительно не автоматическая переменная.

Вы можете прочитать больше об этом на соответствующий раздел руководства GCC.

1

По сути, то, что компиляторы обычно делают с двумерными массивами (фиксированными или переменными), таково:

int arr[x][y] ---> int arr[x*y];

arr[2][4]= something ---> arr[2+4*x]= something;

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

Конечно, вы можете имитировать это и с помощью new [], но это не поддерживается самим компилятором.

Вероятно, по той же причине, т. Е. Потому что было бы еще сложнее отслеживать размеры, особенно при перемещении указателей.

Например. с новым указателем вы могли бы позже написать:

newarr= someotherarray;

а также someotherarray может быть что-то даже с разными размерами. Если бы компилятор выполнил перевод 2-dim-> one dim, ему пришлось бы отслеживать все возможные изменения размера.

С выделенным стеком arr выше, в этом нет необходимости, потому что, по крайней мере, как только компилятор сделал это, он остается такого размера.

0
По вопросам рекламы [email protected]