Как указала проблема, это выполнимо:
#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
или же vector
s.
Есть ли плюсы и минусы этих двух решений, которые я не получаю, или это только то, что есть?
Кстати, я использую компилятор LLVM от Apple.
Я уверен, что вы можете легко найти реализацию для функциональности 2D-массива, но вы также можете создать свой собственный класс. Самый простой способ — это использовать std::vector
хранить данные и иметь функцию отображения индекса, которая берет ваши две координаты и возвращает один индекс в вектор.
Код клиента будет выглядеть немного иначе, вместо arr[x][y]
у вас есть arr.at (x, y), но в остальном он делает то же самое. Вам не нужно возиться с управлением памятью, как это делается std::vector
Просто используйте v.resize(N*N)
в конструкторе или функции установки размеров.
Ни одна из форм не совместима со стандартом C ++, поскольку стандарт не поддерживает массивы переменной длины (VLA) (интересно, что C99 поддерживает, но C не является C ++). Однако некоторые компиляторы имеют расширение для поддержки этого, включая ваш компилятор:
Clang поддерживает такие массивы переменной длины в очень ограниченных случаях для совместимости с программами GNU C и C99:
- Тип элемента массива переменной длины должен быть типом POD («обычные старые данные»), что означает, что он не может иметь никаких объявленных пользователем конструкторов или деструкторов, каких-либо базовых классов или любых членов не-POD типа. Все типы C являются типами POD.
- Массивы переменной длины нельзя использовать в качестве типа нетипичного параметра шаблона.
Но, учитывая, что расширение на месте, почему не работает ваш второй фрагмент? Это потому что VLA применяется только к автоматическим переменным — то есть аргументы или локальные переменные. k
является автоматическим, но это просто указатель — сам массив определяется new int[i][i]
, который выделяет в куче и решительно не автоматическая переменная.
Вы можете прочитать больше об этом на соответствующий раздел руководства GCC.
По сути, то, что компиляторы обычно делают с двумерными массивами (фиксированными или переменными), таково:
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
выше, в этом нет необходимости, потому что, по крайней мере, как только компилятор сделал это, он остается такого размера.