Передача 2D-массива функции C ++

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

Пока у меня это:

void myFunction(double** myArray){ myArray[x][y] = 5; etc... } 

И я объявил массив в другом месте моего кода:

 double anArray[10][10]; 

Однако вызов myFunction(anArray) дает мне ошибку.

Я не хочу копировать массив, когда я его anArray . Любые изменения, внесенные в myFunction должны изменять состояние anArray . Если я правильно понимаю, я хочу передать в качестве аргумента указатель на 2D-массив. Функция также должна принимать массивы разных размеров. Так, например, [10][10] и [5][5] . Как я могу это сделать?

Существует три способа передать 2D-массив функции:

  1. Параметр представляет собой 2D-массив

     int array[10][10]; void passFunc(int a[][10]) { // ... } passFunc(array); 
  2. Параметр представляет собой массив, содержащий указатели

     int *array[10]; for(int i = 0; i < 10; i++) array[i] = new int[10]; void passFunc(int *a[10]) //Array containing pointers { // ... } passFunc(array); 
  3. Параметр - указатель на указатель

     int **array; array = new int *[10]; for(int i = 0; i <10; i++) array[i] = new int[10]; void passFunc(int **a) { // ... } passFunc(array); 

Исправленный размер

1. Передать по ссылке

 template  void process_2d_array_template(int (&array)[rows][cols]) { std::cout << __func__ << std::endl; for (size_t i = 0; i < rows; ++i) { std::cout << i << ": "; for (size_t j = 0; j < cols; ++j) std::cout << array[i][j] << '\t'; std::cout << std::endl; } } 

В C ++ передача массива по ссылке без потери информации о размерах, вероятно, является самой безопасной, так как не нужно беспокоиться о том, что вызывающая сторона передает неправильное измерение (флаги компилятора при несоответствии). Однако это невозможно с динамическими (freestore) массивами; он работает только для автоматических ( обычно стек-живых ) массивов, т. е. размерность должна быть известна во время компиляции.

2. Пропустить указатель

 void process_2d_array_pointer(int (*array)[5][10]) { std::cout << __func__ << std::endl; for (size_t i = 0; i < 5; ++i) { std::cout << i << ": "; for (size_t j = 0; j < 10; ++j) std::cout << (*array)[i][j] << '\t'; std::cout << std::endl; } } 

C-эквивалент предыдущего метода передает массив по указателю. Это не следует путать с передачей по разлагаемому типу указателя массива (3) , который является распространенным, популярным методом, хотя и менее безопасным, чем этот, но более гибким. Как и (1) , используйте этот метод, когда все размеры массива фиксированы и известны во время компиляции. Обратите внимание, что при вызове функции адрес массива должен быть передан process_2d_array_pointer(&a) а не адрес первого элемента методом decay process_2d_array_pointer(a) .

Размер переменной

Они наследуются от C, но менее безопасны, компилятор не имеет возможности проверить, гарантируя, что вызывающий передаёт необходимые измерения. Функция только банков на том, что вызывающий абонент входит в качестве измерения (измерений). Они более гибкие, чем предыдущие, поскольку массивы разной длины могут быть переданы им неизменно.

Следует помнить, что нет такой вещи, как передача массива непосредственно в функцию в C [а в C ++ они могут передаваться как ссылка (1) ]; (2) передает указатель на массив, а не сам массив. Всегда передавать массив as-is становится операцией копирования-указателя, которая облегчается природой массива разложения в указатель .

3. Пропустите (значение) указатель на разложившийся тип

 // int array[][10] is just fancy notation for the same thing void process_2d_array(int (*array)[10], size_t rows) { std::cout << __func__ << std::endl; for (size_t i = 0; i < rows; ++i) { std::cout << i << ": "; for (size_t j = 0; j < 10; ++j) std::cout << array[i][j] << '\t'; std::cout << std::endl; } } 

Хотя допускается использование int array[][10] , я бы не рекомендовал его в соответствии с вышеприведенным синтаксисом, поскольку приведенный выше синтаксис дает понять, что array идентификаторов является единственным указателем на массив из 10 целых чисел, в то время как этот синтаксис выглядит как 2D, но является тем же самым указателем на массив из 10 целых чисел. Здесь мы знаем количество элементов в одной строке (т. Е. Размер столбца, здесь 10), но количество строк неизвестно и, следовательно, должно быть передано в качестве аргумента. В этом случае есть определенная безопасность, поскольку компилятор может указывать, когда передается указатель на массив со вторым измерением, не равным 10. Первое измерение является изменяющейся частью и может быть опущено. См. Здесь аргументы в пользу того, почему разрешено исключать только первое измерение.

4. Пропустить указатель на указатель

 // int *array[10] is just fancy notation for the same thing void process_pointer_2_pointer(int **array, size_t rows, size_t cols) { std::cout << __func__ << std::endl; for (size_t i = 0; i < rows; ++i) { std::cout << i << ": "; for (size_t j = 0; j < cols; ++j) std::cout << array[i][j] << '\t'; std::cout << std::endl; } } 

Опять же, есть альтернативный синтаксис int *array[10] который совпадает с int **array . В этом синтаксисе [10] игнорируется, поскольку он распадается на указатель, тем самым становясь int **array . Возможно, это всего лишь сигнал вызывающему, что переданный массив должен иметь не менее 10 столбцов, даже если требуется подсчет строк. В любом случае компилятор не указывает на какие-либо нарушения длины и размера (он проверяет только, является ли переданный тип указателем на указатель), поэтому для определения параметра и значения столбца, как параметра, имеет смысл.

Примечание: (4) является наименее безопасным вариантом, поскольку он вряд ли имеет проверку типа и наиболее неудобен. Нельзя законно передать 2D-массив этой функции; C-FAQ осуждает обычное обходное решение int x[5][10]; process_pointer_2_pointer((int**)&x[0][0], 5, 10); int x[5][10]; process_pointer_2_pointer((int**)&x[0][0], 5, 10); поскольку это может привести к неопределенному поведению из-за сглаживания массива. Правильный способ передачи массива в этом методе приводит нас к неудобной части, т. Е. Нам нужен дополнительный (суррогатный) массив указателей, каждый из которых указывает на соответствующую строку фактического, подлежащего передаче массива; этот суррогат затем передается функции (см. ниже); все это для того, чтобы выполнить ту же работу, что и вышеупомянутые методы, которые более безопасны, чище и, возможно, быстрее.

Вот программа драйверов для проверки вышеперечисленных функций:

 #include  // copy above functions here int main() { int a[5][10] = { { } }; process_2d_array_template(a); process_2d_array_pointer(&a); // <-- notice the unusual usage of addressof (&) operator on an array process_2d_array(a, 5); // works since a's first dimension decays into a pointer thereby becoming int (*)[10] int *b[5]; // surrogate for (size_t i = 0; i < 5; ++i) { b[i] = a[i]; } // another popular way to define b: here the 2D arrays dims may be non-const, runtime var // int **b = new int*[5]; // for (size_t i = 0; i < 5; ++i) b[i] = new int[10]; process_pointer_2_pointer(b, 5, 10); // process_2d_array(b, 5); // doesn't work since b's first dimension decays into a pointer thereby becoming int** } 

Модификация первого предложения Шенги позволяет использовать шаблоны, чтобы заставить функцию принимать многомерную переменную массива (вместо хранения массива указателей, которые нужно управлять и удалять):

 template  void func(double (&arr)[size_x][size_y]) { printf("%p\n", &arr); } int main() { double a1[10][10]; double a2[5][5]; printf("%p\n%p\n\n", &a1, &a2); func(a1); func(a2); return 0; } 

Операторы печати указывают, что массивы передаются по ссылке (путем отображения адресов переменных)

Вы можете создать шаблон функции следующим образом:

 template void myFunction(double (&myArray)[R][C]) { myArray[x][y] = 5; etc... } 

Тогда у вас есть размеры размеров через R и C. Для каждого размера массива будет создана другая функция, поэтому, если ваша функция велика и вы называете ее множеством различных размеров массивов, это может быть дорогостоящим. Вы можете использовать его как оболочку над такой функцией, хотя:

 void myFunction(double * arr, int R, int C) { arr[x * C + y] = 5; etc... } 

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

 template void myFunction(double (&myArray)[R][C]) { myFunction(*myArray, R, C); } 

anArray[10][10] не является указателем на указатель, это непрерывный кусок памяти, подходящий для хранения 100 значений типа double, который компилятор знает, как обращаться, поскольку вы указали размеры. Вам нужно передать его функции в виде массива. Вы можете опустить размер начального измерения следующим образом:

 void f(double p[][10]) { } 

Однако это не позволит вам передавать массивы с последним измерением, отличным от десяти.

Лучшим решением на C ++ является использование std::vector > : он почти такой же эффективный и значительно более удобный.

Удивлен, что никто не упомянул об этом, но вы можете просто создать шаблон на любом 2D, поддерживающем [] [] семантику.

 template  void myFunction(TwoD& myArray){ myArray[x][y] = 5; etc... } // call with double anArray[10][10]; myFunction(anArray); 

Он работает с любой двумерной «массивной» структурой данных, такой как std::vector> , или пользовательский тип, чтобы максимизировать повторное использование кода.

Одномерный массив распадается на указатель указателя, указывающий на первый элемент в массиве. В то время как 2D-массив распадается на указатель, указывающий на первую строку. Итак, прототип функции должен быть –

 void myFunction(double (*myArray) [10]); 

Я бы предпочел std::vector над необработанными массивами.

Вы можете сделать что-то вроде этого …

 #include using namespace std; //for changing values in 2D array void myFunc(double *a,int rows,int cols){ for(int i=0;i 

Ваш результат будет следующим:

 11.5 12.5 13.5 14.5 

Одна важная вещь для передачи многомерных массивов:

  • First array dimension необязательно.
  • Необходимо указать Second(any any further)dimension .

1. Когда только второе измерение доступно глобально (либо как макрос, либо как глобальная константа)

 `const int N = 3; `void print(int arr[][N], int m) { int i, j; for (i = 0; i < m; i++) for (j = 0; j < N; j++) printf("%d ", arr[i][j]); }` int main() { int arr[][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; print(arr, 3); return 0; }` 

2. Использование одного указателя : в этом методе мы должны прибегать к двумерному массиву при переходе к функции.

 `void print(int *arr, int m, int n) { int i, j; for (i = 0; i < m; i++) for (j = 0; j < n; j++) printf("%d ", *((arr+i*n) + j)); } `int main() { int arr[][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; int m = 3, n = 3; // We can also use "print(&arr[0][0], m, n);" print((int *)arr, m, n); return 0; }` 

Вот вектор матричного примера векторов

 #include  #include  using namespace std; typedef vector< vector > Matrix; void print(Matrix& m) { int M=m.size(); int N=m[0].size(); for(int i=0; i(4,0)); print(n); return 0; } 

вывод:

 1 2 3 4 5 6 7 8 9 1 2 3 0 0 0 0 0 0 0 0 0 0 0 0 

Вы можете использовать средство шаблона на C ++ для этого. Я сделал что-то вроде этого:

 template T process(T a[][col], size_t row) { ... } 

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

 int some_mat[3][3], another_mat[4,5]; process(some_mat, 3); process(another_mat, 4); 

дважды создает шаблон для создания двух определений функций (один из которых col = 3 и один, где col = 5).

Мы можем использовать несколько способов передать 2D-массив функции:

  • Используя одиночный указатель, мы должны придать тип 2D-массиву.

     #include using namespace std; void func(int *arr, int m, int n) { for (int i=0; i 
  • Использование двойного указателя Таким образом, мы также приводим тип 2d-массива

  #include using namespace std; void func(int **arr, int row, int col) { for (int i=0; i>row>>colum; int** arr = new int*[row]; for(int i=0; i>arr[i][j]; } } func(arr, row, colum); return 0; } 
Interesting Posts
Давайте будем гением компьютера.