Передача 2D-массива функции С++
У меня есть функция, которую я хочу взять в качестве параметра 2D-массив переменной величины.
Пока у меня есть это:
void myFunction(double** myArray){
myArray[x][y] = 5;
etc...
}
И я объявил массив в другом месте моего кода:
double anArray[10][10];
Однако вызов myFunction(anArray)
дает мне ошибку.
Я не хочу копировать массив, когда передаю его. Любые изменения, сделанные в myFunction
, должны изменить состояние anArray
. Если я правильно понимаю, я хочу передать в качестве аргумента указатель на 2D-массив. Функция также должна принимать массивы разных размеров. Так, например, [10][10]
и [5][5]
. Как я могу это сделать?
Ответы
Ответ 1
Существует три способа передать 2D-массив функции:
-
Параметр представляет собой 2D-массив
int array[10][10];
void passFunc(int a[][10])
{
// ...
}
passFunc(array);
-
Параметр - это массив, содержащий указатели
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);
-
Параметр - это указатель на указатель
int **array;
array = new int *[10];
for(int i = 0; i <10; i++)
array[i] = new int[10];
void passFunc(int **a)
{
// ...
}
passFunc(array);
Ответ 2
Фиксированный размер
1. Перейдите по ссылке
template <size_t rows, size_t cols>
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;
}
}
В С++ передача массива по ссылке без потери информации о размерах, вероятно, является самой безопасной, так как не нужно беспокоиться о том, что вызывающий абонент передает неправильное измерение (флаги компилятора при несовпадении). Однако это невозможно с динамическими (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;
}
}
С-эквивалент предыдущего метода передает массив по указателю. Это не следует путать с передачей по типу разложенного типа массива (3), который является распространенным, популярным методом, хотя и менее безопасным, чем этот, но более гибким. Как (1), используйте этот метод, когда все размеры массива фиксированы и известны во время компиляции. Обратите внимание, что при вызове функции адрес массива должен быть передан process_2d_array_pointer(&a)
, а не адрес первого элемента путем распада process_2d_array_pointer(a)
.
Переменный размер
Они наследуются от 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);
, поскольку оно может потенциально привести к undefined поведение из-за сглаживания массива. Правильный способ передачи массива в этом методе приводит нас к неудобной части, то есть нам нужен дополнительный (суррогатный) массив указателей, каждый из которых указывает на соответствующую строку фактического, подлежащего передаче массива; этот суррогат затем передается функции (см. ниже); все это для выполнения той же работы, что и вышеупомянутые методы, которые более безопасны, чище и, возможно, быстрее.
Здесь программа драйверов для проверки вышеперечисленных функций:
#include <iostream>
// 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 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 first dimension decays into a pointer thereby becoming int**
}
Ответ 3
Модификация первого предложения shengy, вы можете использовать шаблоны, чтобы заставить функцию принять многомерную переменную массива (вместо хранения массива указателей, которые нужно управлять и удалять):
template <size_t size_x, size_t size_y>
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;
}
Операторы печати указывают, что массивы передаются по ссылке (путем отображения адресов переменных)
Ответ 4
Вы можете создать шаблон функции следующим образом:
template<int R, int C>
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<int C, int R>
void myFunction(double (&myArray)[R][C])
{
myFunction(*myArray, R, C);
}
Ответ 5
Удивлен, что никто не упомянул об этом, но вы можете просто создать шаблон на любом 2D, поддерживающем [] [] семантику.
template <typename TwoD>
void myFunction(TwoD& myArray){
myArray[x][y] = 5;
etc...
}
// call with
double anArray[10][10];
myFunction(anArray);
Он работает с любой двумерной "массивной" структурой данных, такой как std::vector<std::vector<T>>
, или пользовательским типом, чтобы максимизировать повторное использование кода.
Ответ 6
anArray[10][10]
не является указателем на указатель, это непрерывный блок памяти, подходящий для хранения 100 значений типа double, который компилятор знает, как обращаться, поскольку вы указали размеры. Вам нужно передать его функции в виде массива. Вы можете опустить размер начального измерения следующим образом:
void f(double p[][10]) {
}
Однако это не позволит вам передавать массивы с последним измерением, отличным от десяти.
Лучшим решением в С++ является использование std::vector<std::vector<double> >
: он почти такой же эффективный и значительно более удобный.
Ответ 7
Одномерный массив распадается на указатель указателя, указывающий на первый элемент в массиве. В то время как 2D-массив распадается на указатель, указывающий на первую строку. Итак, прототип функции должен быть -
void myFunction(double (*myArray) [10]);
Я бы предпочел std::vector
по необработанным массивам.
Ответ 8
Вы можете сделать что-то вроде этого...
#include<iostream>
using namespace std;
//for changing values in 2D array
void myFunc(double *a,int rows,int cols){
for(int i=0;i<rows;i++){
for(int j=0;j<cols;j++){
*(a+ i*rows + j)+=10.0;
}
}
}
//for printing 2D array,similar to myFunc
void printArray(double *a,int rows,int cols){
cout<<"Printing your array...\n";
for(int i=0;i<rows;i++){
for(int j=0;j<cols;j++){
cout<<*(a+ i*rows + j)<<" ";
}
cout<<"\n";
}
}
int main(){
//declare and initialize your array
double a[2][2]={{1.5 , 2.5},{3.5 , 4.5}};
//the 1st argument is the address of the first row i.e
//the first 1D array
//the 2nd argument is the no of rows of your array
//the 3rd argument is the no of columns of your array
myFunc(a[0],2,2);
//same way as myFunc
printArray(a[0],2,2);
return 0;
}
Ваш результат будет следующим:
11.5 12.5
13.5 14.5
Ответ 9
Вот вектор матричного примера векторов
#include <iostream>
#include <vector>
using namespace std;
typedef vector< vector<int> > Matrix;
void print(Matrix& m)
{
int M=m.size();
int N=m[0].size();
for(int i=0; i<M; i++) {
for(int j=0; j<N; j++)
cout << m[i][j] << " ";
cout << endl;
}
cout << endl;
}
int main()
{
Matrix m = { {1,2,3,4},
{5,6,7,8},
{9,1,2,3} };
print(m);
//To initialize a 3 x 4 matrix with 0:
Matrix n( 3,vector<int>(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
Ответ 10
Мы можем использовать несколько способов передать 2D-массив функции:
-
Используя одиночный указатель, мы должны придать тип 2D-массиву.
#include<bits/stdc++.h>
using namespace std;
void func(int *arr, int m, int n)
{
for (int i=0; i<m; i++)
{
for (int j=0; j<n; j++)
{
cout<<*((arr+i*n) + j)<<" ";
}
cout<<endl;
}
}
int main()
{
int m = 3, n = 3;
int arr[m][n] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
func((int *)arr, m, n);
return 0;
}
-
Использование двойного указателя Таким образом, мы также приводим тип 2d-массива
#include<bits/stdc++.h>
using namespace std;
void func(int **arr, int row, int col)
{
for (int i=0; i<row; i++)
{
for(int j=0 ; j<col; j++)
{
cout<<arr[i][j]<<" ";
}
printf("\n");
}
}
int main()
{
int row, colum;
cin>>row>>colum;
int** arr = new int*[row];
for(int i=0; i<row; i++)
{
arr[i] = new int[colum];
}
for(int i=0; i<row; i++)
{
for(int j=0; j<colum; j++)
{
cin>>arr[i][j];
}
}
func(arr, row, colum);
return 0;
}
Ответ 11
Одна важная вещь для передачи многомерных массивов:
-
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;
}`
Ответ 12
Вы можете использовать средство шаблона на С++ для этого. Я сделал что-то вроде этого:
template<typename T, size_t col>
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);
создает шаблон дважды, чтобы получить 2 определения функций (один из которых col = 3 и один, где col = 5).
Ответ 13
Если вы хотите передать int a[2][3]
в void func(int** pp)
вам понадобятся следующие вспомогательные шаги.
int a[2][3];
int* p[2] = {a[0],a[1]};
int** pp = p;
func(pp);
Поскольку первое [2]
может быть указано неявно, оно может быть дополнительно упрощено как.
int a[][3];
int* p[] = {a[0],a[1]};
int** pp = p;
func(pp);
Ответ 14
В случае, если вы хотите передать двумерный массив динамического размера в функцию, использование некоторых указателей может помочь вам.
void func1(int *arr, int n, int m){
...
int i_j_the_element = arr[i * m + j]; // use the idiom of i * m + j for arr[i][j]
...
}
void func2(){
...
int arr[n][m];
...
func1(&(arr[0][0]), n, m);
}
Ответ 15
Мы можем использовать несколько способов передать 2D-массив функции:
-
Используя одиночный указатель, мы должны отображать 2D-массив.
#include<bits/stdc++.h>
using namespace std;
void func(int *arr, int m, int n)
{
for (int i=0; i<m; i++)
{
for (int j=0; j<n; j++)
{
cout<<*((arr+i*n) + j)<<" ";
}
cout<<endl;
}
}
int main()
{
int m = 3, n = 3;
int arr[m][n] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
func((int *)arr, m, n);
return 0;
}
-
Использование двойного указателя Таким образом, мы также выводим массив 2d
#include<bits/stdc++.h>
using namespace std;
void func(int **arr, int row, int col)
{
for (int i=0; i<row; i++)
{
for(int j=0 ; j<col; j++)
{
cout<<arr[i][j]<<" ";
}
printf("\n");
}
}
int main()
{
int row, colum;
cin>>row>>colum;
int** arr = new int*[row];
for(int i=0; i<row; i++)
{
arr[i] = new int[colum];
}
for(int i=0; i<row; i++)
{
for(int j=0; j<colum; j++)
{
cin>>arr[i][j];
}
}
func(arr, row, colum);
return 0;
}