Tamanho fixo
1. Passe por referência
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;
}
}
No C ++, passar a matriz por referência sem perder as informações da dimensão é provavelmente o mais seguro, pois não é necessário se preocupar com o chamador passando uma dimensão incorreta (sinalizadores do compilador quando não correspondem). No entanto, isso não é possível com matrizes dinâmicas (armazenamento livre); ele funciona apenas para matrizes automáticas ( geralmente com vida útil da pilha ), ou seja, a dimensionalidade deve ser conhecida no momento da compilação.
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 em C do método anterior está passando a matriz por ponteiro. Isso não deve ser confundido com a passagem pelo tipo de ponteiro deteriorado da matriz (3) , que é o método comum e popular, embora menos seguro que esse, mas mais flexível. Como (1) , use esse método quando todas as dimensões da matriz forem fixas e conhecidas em tempo de compilação. Observe que, ao chamar a função, o endereço da matriz deve ser passado process_2d_array_pointer(&a)
e não o endereço do primeiro elemento por deterioração process_2d_array_pointer(a)
.
Tamanho variável
Eles são herdados de C, mas são menos seguros, o compilador não tem como verificar, garantindo que o chamador esteja passando as dimensões necessárias. A função baseia-se apenas no que o chamador passa como as dimensões. Elas são mais flexíveis do que as anteriores, já que matrizes de diferentes comprimentos podem ser passadas para elas invariavelmente.
Deve-se lembrar que não existe uma passagem direta de uma matriz para uma função em C [enquanto em C ++ elas podem ser passadas como referência (1) ]; (2) está passando um ponteiro para a matriz e não a própria matriz. Sempre passar uma matriz como está se torna uma operação de cópia de ponteiro, facilitada pela natureza da matriz de decair em um ponteiro .
3. Passe por (valor) um ponteiro para o tipo deteriorado
// 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 recomendaria a sintaxe acima, pois a sintaxe acima deixa claro que o identificador array
é um ponteiro único para uma matriz de 10 números inteiros, enquanto essa sintaxe parece uma matriz 2D, mas é o mesmo ponteiro para uma matriz de 10 números inteiros. Aqui sabemos o número de elementos em uma única linha (ou seja, o tamanho da coluna, 10 aqui), mas o número de linhas é desconhecido e, portanto, deve ser passado como argumento. Nesse caso, há alguma segurança, pois o compilador pode sinalizar quando um ponteiro para uma matriz com segunda dimensão diferente de 10 é passado. A primeira dimensão é a parte variável e pode ser omitida. Veja aqui a justificativa de por que somente 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 sintaxe alternativa int *array[10]
da mesma int **array
. Nesta sintaxe, o [10]
é ignorado quando se decompõe em um ponteiro, tornando-se assim int **array
. Talvez seja apenas uma sugestão para o chamador que a matriz passada tenha pelo menos 10 colunas; mesmo assim, a contagem de linhas é necessária. De qualquer forma, o compilador não sinaliza violações de tamanho / tamanho (apenas verifica se o tipo passado é um ponteiro para o ponteiro), exigindo, portanto, a contagem de linhas e colunas, pois o parâmetro faz sentido aqui.
Nota: (4) é a opção menos segura, pois quase não possui verificação de tipo e é a mais inconveniente. Não se pode legitimamente passar uma matriz 2D para essa função; O C-FAQ condena a solução usual de fazer int x[5][10]; process_pointer_2_pointer((int**)&x[0][0], 5, 10);
, pois pode levar a um comportamento indefinido devido ao achatamento da matriz. A maneira correta de passar uma matriz nesse método nos leva à parte inconveniente, ou seja, precisamos de uma matriz adicional (substituta) de ponteiros, com cada um de seus elementos apontando para a respectiva linha da matriz real a ser passada; esse substituto é então passado para a função (veja abaixo); tudo isso para realizar o mesmo trabalho que os métodos acima, mais seguros, limpos e talvez mais rápidos.
Aqui está um programa de driver para testar as funções acima:
#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'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**
}