Este artigo explora a distinção e o uso de ponteiros para array e arrays de ponteiros em C, conceitos que frequentemente causam confusão.
Entendendo Ponteiros para Array
Assim como um ponteiro para inteiro (int \*p) armazena o endereço de um inteiro, um ponteiro para array armazena o endereço de um array inteiro. Para ilustrar:
int *p;é um ponteiro para um inteiro.char *pc;é um ponteiro para um caractere.
Seguindo essa analogia, um ponteiro para array seria um ponteiro que aponta para um array. A declaração de um ponteiro para array reflete sua natureza.
Considere os seguintes exemplos:
int* p1 = &arr;: O tipo deste ponteiro éint *.char* p2 = &arr;: O tipo deste ponteiro échar *.int (*p3)[5] = &arr;: O tipo deste ponteiro éint (*)[5].
A regra geral para determinar o tipo de um ponteiro é remover o nome do array da sua declaração. Isso se assemelha à forma como determinamos o tipo de uma variável de array.
Um ponto adicional a ser notado para int arr[10];:
arr: Representa o endereço do primeiro elemento do array.&arr[0]: Também representa o endereço do primeiro elemento.&arr: Representa o endereço do array em si.
Diferenciando Pontieros para Array e Arrays de Ponteiros na Sintaxe
A distinção mais clara entre ponteiros para array e arrays de ponteiros pode ser vista em suas declarações:
// Exemplo de Array de Ponteiros
int *ptrArray[10]; // ptrArray é um array de 10 ponteiros para int
// Exemplo de Ponteiro para Array
int (*arrayPtr)[10]; // arrayPtr é um ponteiro para um array de 10 ints
A chave para diferenciar essas duas estruturas em código C está em observar a precedência dos operadores. Em int *ptrArray[10];, os colchetes [] têm maior precedência que o asterisco *. Isso significa que ptrArray é primeiramente um array (de 10 elementos), e cada elemento desse array é um ponteiro para int. Portanto, é um array de ponteiros.
Inversamente, em int (*arrayPtr)[10];, os parênteses () forçam o asterisco * a ser avaliado antes dos colchetes []. Isso indica que arrayPtr é primeiramente um ponteiro, e esse ponteiro aponta para um array de 10 ints. Portanto, é um ponteiro para um array.
Em resumo: A precedência dos operadores na declaração é o fator determinante.
No contexto de int (*arrayPtr)[10];, o asterisco dentro dos parênteses (*arrayPtr) não é um operador de desreferência, mas sim uma notação que indica que arrayPtr é um ponteiro.
Imprimindo um Array Bidimensional Usando um Ponteiro para Array
A função a seguir demonstra como um ponteiro para array pode ser usado para percorrer e imprimir os elementos de um array bidimensional:
#include <stdio.h>
void print2DArray(int (*p)[4], int rows, int cols) {
int i, j;
for (i = 0; i < rows; i++) {
for (j = 0; j < cols; j++) {
// Acessa o elemento usando aritmética de ponteiros
printf("%d ", *(*(p + i) + j));
}
printf("\n");
}
}
int main() {
int matrix[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
printf("Printing using pointer to array:\n");
print2DArray(matrix, 3, 4);
return 0;
}
A linha crucial aqui é printf("%d ", *(*(p + i) + j));. Para entender isso, vamos compará-la com o acesso a um array unidimensional:
int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int *p = arr; // p aponta para o início de arr
printf("%d", p[i]); // Equivalente a arr[i]
printf("%d", *(p + i)); // Equivalente a arr[i]
printf("%d", *(arr + i)); // Equivalente a arr[i]
printf("%d", arr[i]); // Acesso direto
No contexto do ponteiro para array bidimensional int (*p)[4], p + i avança o ponteiro para a i-ésima linha (cada linha tendo 4 elementos). Então, *(p + i) desreferencia para obter o início dessa linha (que é um array de 4 inteiros). Finalmente, *(*(p + i) + j) acessa o j-ésimo elemento dentro dessa linha.
Ao passar um array bidimensional para uma função em C, estamos na verdade passando o endereço do seu primeiro elemento. No caso de um array bidimensional, o primeiro elemento é a primeira linha, que é tratada como um array unidimensional.
Resumo e Exemplos Adicionais
Para consolidar o entendimento:
int arr[5];: Declara um array de 5 elementos, onde cada elemento é umint.int *ptrArr[10];: Declara um array de 10 elementos, onde cada elemento é um ponteiro paraint.ptrArré um array de ponteiros.int (*arrPtr)[10];: DeclaraarrPtrcomo um ponteiro que aponta para um array de 10ints.arrPtré um ponteiro para um array.int (*(*arrPtrArr)[5])[10];: Esta declaração é mais complexa. Ela define um array (com 5 elementos) onde cada elemento é um ponteiro para um array de 10ints.