тип переменного размера, объявленный вне какой-либо функции
-
07-07-2019 - |
Вопрос
при объявлении двумерного массива
int random[height][width];
а затем использовать его в функции
void populate(int random[height][width], int x, int y)
выдает ошибку типа размера переменной, объявленного вне какой-либо функции.Я знаю, что делаю что-то не так, и это что-то маленькое.У меня просто плохая память...
Решение
Я собираюсь подойти прямо сейчас и сказать вам, что многомерные массивы не стоят мозговых усилий в C или C ++. Вам гораздо лучше использовать одномерные массивы (или, что еще лучше, стандартные контейнеры) и писать функцию индексации:
inline int index (int x, int y)
{
return x + y * width;
}
Теперь по вашей проблеме. C ++ не поддерживает массивы переменной длины C99. Компилятор должен знать, во время компиляции, размер массива. Например, следующее не будет работать.
int dim = 4;
int ar[dim];
Если бы dim
было const
, это работало бы, потому что компилятор мог бы точно сказать, насколько широким должен быть ar
(поскольку значение dim
не изменится). Вероятно, это проблема, с которой вы столкнулись.
Если вы хотите изменить размер во время компиляции, вам нужно сделать что-то более сложное, например написать шаблонную ссылку. Вы не можете использовать указатель для многомерных массивов из-за способа их размещения в C / C ++. Шаблонный пример может выглядеть следующим образом:
template <int Width, int Height>
void populate(int (&(&random)[Width])[Height], int x, int y);
Это безобразно.
Во время выполнения вам нужно будет использовать new
для размещения данных или использовать тип контейнера.
Другие советы
Вы не можете определить массив с непостоянными измерениями (шириной, высотой) вне функции, то есть - не в кадре стека, потому что измерения не известны во время компиляции. Вам придется использовать константы или распределять их динамически (либо в куче, либо в кадре стека).
Когда массив передается в качестве параметра функции (передача по значению), он превращается в указатель на первый элемент массива. Даже если вы можете четко прочитать в сигнатуре размеры массива, эти измерения игнорируются компилятором. Такое поведение совместимо с C.
Используя C ++, вы можете передавать массив по ссылке, и это больше не будет проблемой.
int extract_value( int (&a)[10][10], int row, int col ) {
return a[row][col];
}
int main() {
int a[10][10] = {};
a[5][5] = 1;
std::cout << extract_value( a, 5, 5 ) << std::endl;
int b[5][5];
// extract_value( b, 2, 2 ); // error: the function takes an array of 10x10
}
Параметр функции должен точно соответствовать, то есть он принимает только массив 10х10 элементов. Вы можете избавиться от этого ограничения, шаблонизируя функцию по размерам массива. Как только вы это сделаете, введите:
template <typename T, int Rows, int Cols>
T extract_value( T (&a)[Rows][Cols], int row, int col ) {
return a[row][col];
}
int main() {
int a[5][7] = {};
extract_value( a, 3, 4 );
int b[8][2] = {};
extract_value( b, 7, 1 ); // correct, the compiler matches sizes
double c[4][4] = {};
extract_value( c, 2, 2 ); // different types are allowed
}
Это решение все еще громоздко в том смысле, что размеры должны быть постоянными времени компиляции, а массив должен быть выделен в стеке. Решением этой проблемы является определение некоторого класса, который занимает динамическую память в буфере (линейном) и имеет преобразование из N-координатной системы в одномерный массив для получения значений, как это было предложено ранее. Вы можете получить некоторые советы о том, как это сделать, в этом FAQ о перегрузке операторов, обеспечивающей реализацию 2D-матрицы. Как только вы это реализовали, вы можете просто использовать это как параметр для функций / методов.
Я бы рекомендовал следовать этому последнему пути: инкапсулировать N-мерный массив в класс, который обеспечивает преобразования в одномерный вектор (в C ++ FAQ Lite используется необработанный указатель, я предпочитаю контейнеры STL).
Я проиллюстрирую это примером:
//глобальные
const int ARRAY_SIZE = 16
struct ArrayType_t arrayType[ARRAY_SIZE];
Несмотря на то, что ARRAY_SIZE объявлен как константа int, его значение не инициализируется во время компиляции, и, следовательно, компилятор не знает размер массива и выдает такую ошибку.Однако, если вы создадите это как хэш-определение
#define ARRAY_SIZE 16
struct ArrayType_t arrayType[ARRAY_SIZE]
===> это работает, потому что ARRAY_SIZE определяется во время
компиляции, и компилятор может знать размер массива во время компиляции.
Вы можете использовать что-то вроде этого:
void populate(int height, int width, int **random)
{
//here you can work from random[0][0] to random[height][width]
}
тогда вы можете использовать его следующим образом:
int main()
{
int height=10;
int width=20;
int **myarray = new int*[height];
for( int i=0; i< height; i++ ) myarray[i] = new int[width];
populate( height, width, myarray);
}
но, конечно, вам придется остерегаться переполнения буфера