Ponteiros e matrizes

Embora pensemos em matrizes como uma estrutura multidimensional, contendo linhas e colunas, as matrizes são armazenadas na memória de forma linear, como um vetor de vetores.

Ilustração da representação de matrizes na memória.

Para facilitar, vamos relembrar a declaração de uma matriz em C:

int matriz[2][2]; // matriz[linhas][colunas]

Se atribuírmos valores à matriz, observaremos que cada linha da matriz é um vetor.

int matriz[2][2] = {
    {25,50}, // isso é um vetor
    {7, 14} // isso é outro vetor
};

Sim, eu sei que isso pode ser um pouco confuso, mas vou tentar explicar detalhadamente a seguir.

🤯 Expandindo a mente

Recapitulando:

  • 1️⃣ No conteúdo sobre Ponteiros e Vetores, vimos que a navegação em um vetor pode acontecer da forma simples: somando um valor inteiro ao endereço de memória da primeira posição, certo?
  • 2️⃣ No conteúdo sobre Aritmética de Ponteiros, vimos que as operações de adição e subtração a endereços de memória não levam em consideração o número absoluto, mas sim o espaço de memória utilizado por cada tipo de dado (4 bytes para um inteiro, por exemplo).

Como vetores e matrizes são estruturas de dados homogêneas (ou seja: armazenam dados do mesmo tipo), é fácil mudar de uma posição para outra pois o avanço na memória terá sempre a mesma quantidade: aquela necessária para armazenar o tipo de dado.

Se você entendeu até aqui, continue :)

🤔 Vetor de vetor

No exemplo de código anterior, como a matriz seria capaz de diferenciar um vetor do outro? A solução é bem interessante: quando declaramos uma matriz, já informamos a quantidade de linhas (número de itens do vetor de vetores), bem como o número de colunas (número de itens de cada vetor). Também temos o tipo de dado utilizado.

Ilustração da representação de matrizes na memória.

No código acima, matriz é um vetor de DUAS posições. Cada posição de matriz consiste em um vetor de números inteiros, com duas posições, totalizando 8 bytes de armazenamento.

Vejamos:

#include <stdio.h>
int main()
{
    int matriz[2][2] = {
        {25,50}, // isso é um vetor
        {7, 14} // isso é outro vetor
    };
    
    printf("Matriz: %p\n", matriz);
    printf("Matriz: %p\n", matriz+1);
    
    return 0;
}

Saída:

Matriz: 0x7ffe85a185f0
Matriz: 0x7ffe85a185f8

Resumidamente:

*(matriz+linha)

🚪 Acessando as colunas

Agora que já sabemos navegar entre as linhas da matriz, precisamos voltar nossas atenções para a navegação entre os elementos da lista. Felizmente, aqui não há muito segredo: estamos lidando com um vetor de inteiros.

Precisamos, a partir de uma posição inicial (a que representa a linha da matriz), selecionar incrementar um valor que representa o índice da coluna:

*(*(matriz+linha)+coluna)

A sintaxe é um pouco estranha, então vamos entender por partes:

  • Com *(matriz+linha), acessamos o vetor que contém a linha desejada;
  • Com *(...+coluna), especificamos qual a coluna desejada.

👨‍💻 Vamos ao código

Como já estamos acostumados, podemos acessar as posições da matriz usando a tradicional notação matriz[linha][coluna]:

#include <stdio.h>


int main()
{
    int matriz[2][2] = {{1,2}, {3,4}};
    
    for(int i = 0; i < 2; i++){
        for(int j = 0; j < 2; j++){
            printf("matriz[%d][%d] => %d\n", i,j,matriz[i][j]);
        }
    }
    
    return 0;
}

Alternativamente, podemos fazer uso de ponteiros: *(*(matriz+linha)+coluna):

#include <stdio.h>


int main()
{
    int matriz[2][2] = {{25,50}, {7,14}};
    
    for(int i = 0; i < 2; i++){
        for(int j = 0; j < 2; j++){
            printf("matriz[%d][%d] => %d\n", i,j, *(*(matriz+i)+j));
        }
    }
    
    return 0;
}

📖 Bibliografia

Livros

  • Backes, A. (2018). Linguagem C - Completa e Descomplicada.