Passando uma matriz de comprimento variável multidimensional para uma function

Há toneladas de perguntas semelhantes, mas ainda não consegui encontrar nenhuma resposta relevante para o recurso de matrizes de comprimento variável em C99 / C11.

Como passar array de comprimento variável multidimensional para uma function em C99 / C11?

Por exemplo:

void foo(int n, int arr[][]) // <-- error here, how to fix? { } void bar(int n) { int arr[n][n]; foo(n, arr); } 

Compilador ( g++-4.7 -std=gnu++11 ) diz:
error: declaration of 'arr' as multidimensional array must have bounds for all dimensions except the first

Se eu alterá-lo para int *arr[] , o compilador ainda reclama:
error: cannot convert 'int (*)[(((sizetype)(((ssizetype)n) + -1)) + 1)]' to 'int**' for argument '2' to 'void foo(int, int**)'

Próxima pergunta, como passá-lo por valor e como passá-lo por referência? Aparentemente, geralmente você não quer que toda a matriz seja copiada quando você a passa para uma function.

Com arrays de comprimento constante é simples, uma vez que, como a “constante” implica, você deve saber o comprimento quando você declara a function:

 void foo2(int n, int arr[][10]) // <-- ok { } void bar2() { int arr[10][10]; foo2(10, arr); } 

Eu sei, passando matrizes para funções como esta não é uma prática recomendada, e eu não gosto nada disso. Provavelmente é melhor fazer com pointers planos ou objects (como std: vector) ou de alguma forma. Mas ainda assim, sou um pouco curioso, qual é a resposta aqui do ponto de vista teórico.

Passar matrizes para funções é um pouco engraçado em C e C ++. Não há rvalues ​​de tipos de array, então você está realmente passando um ponteiro.

Para endereçar uma matriz 2D (uma matriz real, não uma matriz de matrizes), você precisará passar 2 blocos de dados:

  • o ponteiro para onde começa
  • quão larga é uma fileira

E estes são dois valores separados, seja C ou C ++ ou com VLA ou sem ou outros.

Algumas maneiras de escrever isso:

Mais simples, funciona em todos os lugares, mas precisa de mais trabalho manual

 void foo(int width, int* arr) { arr[x + y*width] = 5; } 

VLA, padrão C99

 void foo(int width, int arr[][width]) { arr[x][y] = 5; } 

VLA com argumentos invertidos, declaração de parâmetro de encaminhamento (extensão GNU C)

 void foo(int width; int arr[][width], int width) { arr[x][y]=5; } 

C ++ com VLA (extensão GNU C ++, terrivelmente feia)

 void foo(int width, int* ptr) { typedef int arrtype[][width]; arrtype& arr = *reinterpret_cast(ptr); arr[x][y]=5; } 

Grande observação:

A notação [x] [y] com uma matriz 2D funciona porque o tipo da matriz contém a largura. Não VLA = tipos de matriz devem ser corrigidos em tempo de compilation.

Portanto: se você não pode usar o VLA, então …

  • não há como lidar com isso em C,
  • não há como manipulá-lo sem uma class de proxy com sobrecarga de operadores sobrecarregados em C ++.

Se você pode usar extensões VLA (C99 ou GNU C ++), então …

  • você está no verde em C
  • você ainda precisa de uma bagunça em C ++, use classs em vez disso.

Para C ++, boost::multi_array é uma escolha sólida.

Uma solução alternativa

Para arrays 2D, você pode fazer duas alocações separadas:

  • uma matriz 1D de pointers para T (A)
  • um array 2D de T (B)

Em seguida, defina os pointers em (A) para apontar para as respectivas linhas de (B).

Com esta configuração, você pode simplesmente passar (A) por volta como um simples T** e se comportará bem com [x][y] indexação.

Essa solução é boa para o 2D, mas precisa de mais e mais clichê para dimensões mais altas. Também é mais lento que a solução VLA por causa da camada extra de indireção.

Você também pode encontrar uma solução similar com uma alocação separada para cada linha de B Em C isso se parece com um malloc-in-a-loop e é análogo ao vetor-de-vetores de C ++. No entanto, isso tira o benefício de ter toda a matriz em um bloco.

Não existe uma maneira clara de se fazer isso, mas você pode usar uma solução alternativa para tratar um array bidimensional como um array unidimensional e reconvertê-lo em um array bidimensional dentro da function.

 void foo2(int n, int *arr) { int *ptr; // use this as a marker to go to next block int i; int j; for(i = 0; i < n; i++) { ptr = arr + i*n; // this is the starting for arr[i] ... for (j = 0; j < n ;j++) { printf(" %d ", ptr[j]); // This is same as arr[i][j] } } } void bar2() { int arr[10][10]; foo2(10, (int *)arr); }