Até agora, trabalhamos com variáveis cujo tamanho é conhecido em tempo de compilação (alocação estática). No entanto, muitas vezes precisamos alocar memória durante a execução do programa, por exemplo, para criar arrays cujo tamanho só é conhecido em tempo de execução ou para estruturas de dados dinâmicas como listas encadeadas. A alocação dinâmica de memória em C é feita principalmente com as funções malloc, calloc, realloc e free, todas definidas no cabeçalho <stdlib.h>.

Nesta aula, focaremos nas duas funções mais fundamentais: malloc para alocar memória e free para liberá-la. Entender corretamente seu uso é crucial para escrever programas eficientes e evitar problemas como vazamentos de memória e acesso inválido.

malloc e sizeof

A função malloc (memory allocation) aloca um bloco contíguo de memória de um determinado tamanho (em bytes) e retorna um ponteiro para o início desse bloco. Se a alocação falhar (por falta de memória), retorna NULL. A sintaxe é: void *malloc(size_t size);.

Para alocar espaço para um tipo específico, usamos o operador sizeof para obter o tamanho em bytes daquele tipo. Isso torna o código portável e evita erros de tamanho. Por exemplo, para alocar um array de 10 inteiros: int *p = (int *) malloc(10 * sizeof(int));. O cast (int *) não é obrigatório em C, mas é comum para clareza.

Exemplo prático:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int n;
    printf("Quantos inteiros deseja alocar? ");
    scanf("%d", &n);

    int *arr = (int *) malloc(n * sizeof(int));
    if (arr == NULL) {
        printf("Erro de alocação!\n");
        return 1;
    }

    for (int i = 0; i < n; i++) {
        arr[i] = i * 10;
    }

    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");

    free(arr);
    return 0;
}

Note que malloc não inicializa a memória alocada; o conteúdo é indeterminado. Se precisar de memória zerada, use calloc.

free

A função free libera a memória previamente alocada por malloc, calloc ou realloc. Sua sintaxe é: void free(void *ptr);. Após chamar free, o ponteiro se torna inválido (dangling pointer) e não deve ser usado. É uma boa prática atribuir NULL ao ponteiro após liberá-lo.

Chamar free duas vezes no mesmo ponteiro (double free) ou em um ponteiro não alocado causa comportamento indefinido. Sempre libere a memória alocada quando não for mais necessária para evitar vazamentos.

Exemplo:

int *p = (int *) malloc(sizeof(int));
if (p != NULL) {
    *p = 42;
    printf("%d\n", *p);
    free(p);
    p = NULL; // evita dangling pointer
}

Verificando NULL

Sempre verifique se malloc retornou NULL antes de usar o ponteiro. Isso indica que a alocação falhou. Ignorar essa verificação pode levar a falhas de segmentação. A verificação é simples: if (ptr == NULL) { /* trata erro */ }.

Exemplo de verificação:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int *arr = (int *) malloc(1000000000 * sizeof(int)); // alocação grande
    if (arr == NULL) {
        printf("Memória insuficiente!\n");
        return 1;
    }
    // ... uso ...
    free(arr);
    return 0;
}

Em programas robustos, é essencial tratar o erro de alocação adequadamente, seja exibindo uma mensagem, liberando recursos e encerrando, ou tentando uma alocação menor.

Vazamentos de memória

Vazamento de memória (memory leak) ocorre quando a memória alocada dinamicamente não é liberada após o uso, fazendo com que o programa consuma cada vez mais memória. Em programas de longa execução (servidores, sistemas embarcados), vazamentos podem levar à exaustão de memória e falhas.

Para evitar vazamentos, cada malloc deve ter um free correspondente. Use ferramentas como Valgrind (Linux) ou Dr. Memory (Windows) para detectar vazamentos. Além disso, adote boas práticas:

  • Libere memória assim que não for mais necessária.
  • Atribua NULL ao ponteiro após free.
  • Evite perder o endereço do bloco alocado antes de liberá-lo.
  • Em funções que retornam ponteiros alocados, documente que o chamador é responsável por liberar.

Exemplo de vazamento:

void func() {
    int *p = (int *) malloc(sizeof(int));
    *p = 10;
    // esqueceu de free(p);
}

int main() {
    func();
    // p nunca foi liberado -> vazamento
    return 0;
}

Para corrigir, adicione free(p); antes do fim da função.

Boas práticas finais

  • Sempre inclua <stdlib.h> para usar malloc e free.
  • Use sizeof para calcular o tamanho, não valores fixos.
  • Verifique o retorno de malloc para NULL.
  • Libere a memória alocada e evite double free.
  • Para arrays, aloque o tamanho total (número de elementos * sizeof(tipo)).

Referências

Exercícios

  1. Escreva um programa que aloca dinamicamente um array de 5 floats, preenche com valores lidos do usuário e imprime a soma.

    ✓ Resposta:
    #include <stdio.h>
    #include <stdlib.h>
    
    int main() {
        int n = 5;
        float *arr = (float *) malloc(n * sizeof(float));
        if (arr == NULL) {
            printf("Erro de alocação\n");
            return 1;
        }
        printf("Digite 5 números: ");
        for (int i = 0; i < n; i++) {
            scanf("%f", &arr[i]);
        }
        float soma = 0;
        for (int i = 0; i < n; i++) {
            soma += arr[i];
        }
        printf("Soma: %.2f\n", soma);
        free(arr);
        return 0;
    }
  2. O que acontece se você chamar free duas vezes no mesmo ponteiro? Faça um programa que demonstre (mas não execute em produção).

    ✓ Resposta: Chamar free duas vezes no mesmo ponteiro causa comportamento indefinido (double free). Pode corromper o heap, causar crash ou ser explorado por atacantes. Exemplo:
    int *p = malloc(sizeof(int));
    free(p);
    free(p); // undefined behavior
  3. Escreva uma função que recebe um inteiro n, aloca um array de n inteiros, preenche com zeros e retorna o ponteiro. O chamador deve liberar a memória.

    ✓ Resposta:
    #include <stdlib.h>
    
    int *cria_array_zeros(int n) {
        int *arr = (int *) malloc(n * sizeof(int));
        if (arr != NULL) {
            for (int i = 0; i < n; i++) {
                arr[i] = 0;
            }
        }
        return arr;
    }
    
    // Exemplo de uso:
    int main() {
        int *meu_arr = cria_array_zeros(10);
        if (meu_arr) {
            // usar...
            free(meu_arr);
        }
        return 0;
    }
  4. Identifique o vazamento de memória no código abaixo e corrija:

    void exemplo() {
        int *p = malloc(sizeof(int));
        *p = 42;
        int *q = malloc(sizeof(int));
        *q = 24;
        p = q;
        free(p);
    }

    ✓ Resposta: O vazamento ocorre porque o bloco apontado por p originalmente (com valor 42) não é liberado antes de p ser sobrescrito. Correção:
    void exemplo() {
        int *p = malloc(sizeof(int));
        *p = 42;
        int *q = malloc(sizeof(int));
        *q = 24;
        free(p); // libera o primeiro bloco
        p = q;
        free(p); // libera o segundo
    }
  5. Escreva um programa que aloca uma matriz dinâmica de inteiros com linhas e colunas lidas do usuário, preenche com valores (linha * coluna) e imprime. Não esqueça de liberar a memória.

    ✓ Resposta:
    #include <stdio.h>
    #include <stdlib.h>
    
    int main() {
        int linhas, colunas;
        printf("Linhas e colunas: ");
        scanf("%d %d", &linhas, &colunas);
    
        int **mat = (int **) malloc(linhas * sizeof(int *));
        if (mat == NULL) return 1;
        for (int i = 0; i < linhas; i++) {
            mat[i] = (int *) malloc(colunas * sizeof(int));
            if (mat[i] == NULL) {
                // libera linhas já alocadas
                for (int j = 0; j < i; j++) free(mat[j]);
                free(mat);
                return 1;
            }
        }
    
        for (int i = 0; i < linhas; i++) {
            for (int j = 0; j < colunas; j++) {
                mat[i][j] = i * colunas + j;
            }
        }
    
        for (int i = 0; i < linhas; i++) {
            for (int j = 0; j < colunas; j++) {
                printf("%d ", mat[i][j]);
            }
            printf("\n");
        }
    
        for (int i = 0; i < linhas; i++) free(mat[i]);
        free(mat);
        return 0;
    }