Passando um array 2D para uma function C ++

Eu tenho uma function que eu quero tomar, como parâmetro, um array 2D de tamanho variável.

Até agora eu tenho isso:

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

E eu declarei um array em outro lugar no meu código:

 double anArray[10][10]; 

No entanto, chamar myFunction(anArray) me dá um erro.

Eu não quero copiar o array quando eu passá-lo. Quaisquer alterações feitas no myFunction devem alterar o estado de anArray . Se bem entendi, eu só quero passar como um argumento um ponteiro para um array 2D. A function também precisa aceitar matrizes de tamanhos diferentes. Então, por exemplo, [10][10] e [5][5] . Como posso fazer isso?

Existem três maneiras de passar um array 2D para uma function:

  1. O parâmetro é um array 2D

     int array[10][10]; void passFunc(int a[][10]) { // ... } passFunc(array); 
  2. O parâmetro é um array contendo pointers

     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. O parâmetro é um ponteiro para um ponteiro

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

Tamanho fixo

1. Passe por referência

 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; } } 

Em C ++, passar a matriz por referência sem perder as informações de dimensão é provavelmente o mais seguro, já que não é necessário se preocupar com o chamador passando uma dimensão incorreta (sinalizadores de compilador quando não combinados). No entanto, isso não é possível com matrizes dinâmicas (freestore); ele funciona apenas para matrizes automáticas ( geralmente em pilha ), isto é, a dimensionalidade deve ser conhecida em tempo de compilation.

2. Passe pelo ponteiro

 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; } } 

O equivalente C do método anterior está passando a matriz pelo ponteiro. Isso não deve ser confundido com a passagem pelo tipo de ponteiro decadente da matriz (3) , que é o método comum e popular, embora menos seguro que este, mas mais flexível. Como (1) , use este método quando todas as dimensões da matriz forem fixas e conhecidas em tempo de compilation. Note que ao chamar a function o endereço da matriz deve ser passado process_2d_array_pointer(&a) e não o endereço do primeiro elemento pela decadência process_2d_array_pointer(a) .

Tamanho Variável

Estes são herdados de C, mas são menos seguros, o compilador não tem como verificar, garantindo que o chamador está passando as dimensões necessárias. A function só faz bancos no que o chamador passa como dimensão (s). Estes são mais flexíveis do que os acima, pois matrizes de diferentes comprimentos podem ser passados ​​para eles invariavelmente.

Deve ser lembrado que não há como passar um array diretamente para uma function em C [enquanto em C ++ eles podem ser passados ​​como uma referência (1) ]; (2) está passando um ponteiro para o array e não o próprio array. Sempre passar um array como-se torna uma operação de cópia de ponteiro que é facilitada pela natureza do array de decair em um ponteiro .

3. Passe (valor) um ponteiro para o tipo decadente

 // 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; } } 

Embora int array[][10] seja permitido, eu não o recomendaria sobre a syntax acima, pois a syntax acima deixa claro que a array identificadores é um único ponteiro para uma matriz de 10 inteiros, enquanto essa syntax parece ser uma Matriz 2D, mas é o mesmo ponteiro para uma matriz de 10 inteiros. Aqui nós sabemos o número de elementos em uma única linha (isto é, o tamanho da coluna, 10 aqui), mas o número de linhas é desconhecido e, portanto, deve ser passado como um argumento. Nesse caso, há alguma segurança, pois o compilador pode sinalizar quando um ponteiro para uma matriz com a segunda dimensão diferente de 10 é passado. A primeira dimensão é a parte variável e pode ser omitida. Veja aqui a justificativa de porque apenas a primeira dimensão pode ser omitida.

4. Passe o ponteiro para um ponteiro

 // 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; } } 

Novamente, há uma syntax alternativa de int *array[10] que é o mesmo que int **array . Nessa syntax, o [10] é ignorado conforme decai em um ponteiro, tornando-se int **array . Talvez seja apenas uma sugestão para o chamador que o array passado deve ter pelo menos 10 colunas, mesmo assim a contagem de linhas é necessária. Em qualquer caso, o compilador não sinaliza por quaisquer violações de tamanho / tamanho (ele apenas verifica se o tipo passado é um ponteiro para o ponteiro), portanto, requer que as contagens de linha e coluna, como o parâmetro faz sentido aqui.

Nota: (4) é a opção menos segura, uma vez que dificilmente tem qualquer tipo de verificação e o mais inconveniente. Não se pode legitimamente passar um array 2D para essa function; C-FAQ condena a solução usual de fazer 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); como isso pode potencialmente levar a um comportamento indefinido devido ao achatamento do array. O modo certo de passar um array nesse método nos leva à parte inconveniente, isto é, precisamos de uma matriz adicional (substituta) de pointers com cada um de seus elementos apontando para a respectiva linha da matriz real, a ser passada; este substituto é então passado para a function (veja abaixo); Tudo isso para obter o mesmo trabalho que os methods acima, que são mais seguros, mais limpos e talvez mais rápidos.

Aqui está um programa de driver para testar as funções acima:

 #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** } 

Uma modificação da primeira sugestão de shengy, você pode usar modelos para fazer a function aceitar uma variável de multidimensional array (em vez de armazenar uma matriz de pointers que precisam ser gerenciados e excluídos):

 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; } 

As instruções print estão lá para mostrar que as matrizes estão sendo passadas por referência (exibindo os endereços das variables)

Você pode criar um modelo de function como este:

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

Então, você tem os dois tamanhos de dimensão via R e C. Uma function diferente será criada para cada tamanho de matriz, portanto, se sua function for grande e você a chamar com uma variedade de tamanhos de matriz diferentes, isso pode ser caro. Você poderia usá-lo como um wrapper sobre uma function como esta:

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

Ele trata a matriz como unidimensional e usa a aritmética para descobrir as compensações dos índices. Nesse caso, você definiria o modelo da seguinte maneira:

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

anArray[10][10] não é um ponteiro para um ponteiro, é um pedaço contíguo de memory adequado para armazenar 100 valores do tipo double, que o compilador sabe como endereçar porque você especificou as dimensões. Você precisa passá-lo para uma function como um array. Você pode omitir o tamanho da dimensão inicial da seguinte maneira:

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

No entanto, isso não permitirá que você passe matrizes com a última dimensão diferente de dez.

A melhor solução em C ++ é usar std::vector > : é quase tão eficiente e significativamente mais conveniente.

Surpreso que ninguém mencionou isso ainda, mas você pode simplesmente modelar qualquer coisa semântica de suporte 2D [] [].

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

Ele funciona com qualquer estrutura de dados “array-like” 2D, como std::vector> , ou um tipo definido pelo usuário para maximizar a reutilização de código.

Uma matriz dimensional única decai para um ponteiro apontando para o primeiro elemento na matriz. Enquanto um array 2D decai para um ponteiro apontando para a primeira linha. Então, o protótipo da function deve ser –

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

Eu preferiria std::vector em matrizes cruas.

Você pode fazer algo assim …

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

Sua saída será a seguinte ...

 11.5 12.5 13.5 14.5 

Uma coisa importante para transmitir matrizes multidimensionais é:

  • First array dimension não precisa ser especificada.
  • Second(any any further)dimension deve ser especificada.

1.Quando apenas segunda dimensão está disponível globalmente (como uma macro ou como uma constante global)

 `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.Usando um único ponteiro : Neste método, devemos typecast o array 2D ao passar para a function.

 `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; }` 

Aqui está um vetor de exemplo matriz de vetores

 #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; } 

saída:

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

Você pode usar o recurso de modelo em C ++ para fazer isso. Eu fiz algo assim:

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

O problema com esta abordagem é que para cada valor de col que você fornece, uma nova definição de function é instanciada usando o modelo. assim,

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

instancia o modelo duas vezes para produzir 2 definições de function (uma em que col = 3 e uma em que col = 5).

Podemos usar várias maneiras de passar um array 2D para uma function:

  • Usando o ponteiro único , temos que tipografar o array 2D.

     #include using namespace std; void func(int *arr, int m, int n) { for (int i=0; i 
  • Usando o ponteiro duplo Desta forma, também typecast a matriz 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; }