← Aulas

Apontadores e Alocação Dinâmica

Compreender a memória, manipular endereços e gerir recursos em tempo de execução com malloc, calloc, realloc e free.

Disciplina:Introdução à Programação em C (EET126) Público-alvo:Licenciatura em Eng. Electrónica e Telecomunicações Carga Horária:2 horas (1h T + 1h P) Docente:Isac Osvaldo de Venâncio Daniel

Objectivos da Aula

02

Ao final desta aula, o estudante deverá ser capaz de:

  • Compreender o conceito de ponteiros e como a memória é endereçada.
  • Realizar operações e manipulações corretas com ponteiros em C.
  • Entender a importância e aplicar os mecanismos de alocação dinâmica de memória.
  • Utilizar as funções malloc, calloc, realloc e free.
🎯
O domínio de apontadores e memória dinâmica é o que separa um programador iniciante de um engenheiro de sistemas embarcados.

Conteúdo Programático

03

1 · Conceito de Ponteiros

  • O que é um ponteiro?
  • Sintaxe e declaração

2 · Manipulação de Ponteiros

  • Operador de endereço (&)
  • Operador de indireção (*)
  • Cuidados essenciais

3 · Alocação Dinâmica

  • malloc, calloc, realloc, free
  • Boas práticas

4 · Exemplos e Exercícios

  • Alocação de turma (prática)
  • Encontrar Maior com ponteiros

O que é um Ponteiro?

04

Toda variável em C é armazenada numa posição específica da memória RAM. O número que identifica essa posição é o endereço.

📦 Variável Comum

Armazena um valor (ex.: um número, um caractere).

int idade = 25;
// idade contém o valor 25

📍 Ponteiro

Armazena um endereço de memória onde está o valor de outra variável.

int *ptr = &idade;
// ptr contém o endereço de idade
🧠
Pensa num ponteiro como um papel com o número da casa. A casa é a variável; o papel é o ponteiro.

Declaração de Ponteiros

05

Para declarar um ponteiro, usamos o operador asterisco (*) logo antes do nome, indicando o tipo de dado para o qual ele vai apontar.

tipo *nomeDoPonteiro;

// Exemplo:
int *ptrInt;    // Aponta para um inteiro
float *ptrFloat; // Aponta para um float
char *ptrChar;   // Aponta para um caractere
⚠️
O tipo do ponteiro tem de corresponder ao tipo da variável que ele aponta. int* não pode apontar directamente para um float sem cast.

Operadores Fundamentais

06

🔗 Endereço (&)

Retorna o endereço de memória de uma variável.

int x = 10;
int *p = &x;
// p = endereço de x

🎯 Indireção (*)

Acessa ou modifica o valor armazenado no endereço apontado.

int x = 10;
int *p = &x;
*p = 20;
// x agora vale 20
✅
Regra mental: & pergunta "onde moras?" · * pergunta "o que tens em casa?"

Manipulação Prática

07
#include <stdio.h>

int main(void) {
    int valorOriginal = 42;
    int *ptr;

    ptr = &valorOriginal;   // ptr recebe o endereço

    printf("Valor: %d\n", valorOriginal);  // 42
    printf("Endereço: %p\n", (void *)ptr); // 0x7ff...
    printf("Via ponteiro: %d\n", *ptr);   // 42

    *ptr = 100;  // Altera o valor original!

    printf("Novo valor: %d\n", valorOriginal); // 100
    return 0;
}

Cuidados Essenciais

08

🚫 Lixo de Memória

Um ponteiro declarado e não inicializado aponta para um endereço aleatório (lixo).

int *ptr;   // PERIGO!
*ptr = 5;    // Corrompe memória!

✅ Ponteiro Nulo

Inicialize sempre com NULL quando não aponta para nada.

int *ptr = NULL;
if (ptr != NULL) {
    *ptr = 5;  // Seguro
}
💥
Acessar um ponteiro NULL ou não inicializado causa segmentation fault — encerramento abrupto do programa ou travamento do microcontrolador.

Estática vs. Dinâmica

09

📌 Estática

  • Tamanho fixo em tempo de compilação
  • Guardada na Stack
  • Ex.: int v[10];

🔄 Dinâmica

  • Tamanho definido em tempo de execução
  • Guardada no Heap
  • Ex.: malloc(10 * sizeof(int))
  • Requer #include <stdlib.h>
💡
Em sistemas embarcados, a Stack é pequena (ex.: 2 KB no ATmega328P). O Heap permite alocar dinamicamente o que precisares, mas exige disciplina.

A Função malloc

10

Memory Allocation — reserva um bloco contínuo de bytes na RAM e devolve um ponteiro genérico (void*) para o primeiro byte.

void* malloc(size_t tamanhoEmBytes);
#include <stdlib.h>

int *vetor = (int *) malloc(10 * sizeof(int));

if (vetor == NULL) {
    printf("Erro: Memória insuficiente!\n");
    return 1;
}
⚠️
A memória não é inicializada — contém lixo. Se precisares de zeros, usa calloc.

A Função calloc

11

Contiguous Allocation — aloca memória para um array e inicializa tudo a zero.

void* calloc(size_t num_elementos, size_t tamanho);

// Exemplo: 5 inteiros, todos a 0
int *vetor = (int *) calloc(5, sizeof(int));

if (vetor == NULL) {
    printf("Erro ao alocar!\n");
    return 1;
}
✅
malloc → memória suja · calloc → memória limpa (zeros).

A Função realloc

12

Reallocation — redimensiona um bloco de memória já alocado (para mais ou para menos).

void* realloc(void *ptr, size_t novo_tamanho);

⚠️ A armadilha do realloc

int *temp = (int *) realloc(ptr, novo_tamanho * sizeof(int));

if (temp == NULL) {
    // ptr AINDA é válido — liberta-o
    free(ptr);
    return 1;
}

ptr = temp;  // Só actualiza depois de confirmar sucesso
💥
Nunca faças ptr = realloc(ptr, ...) directamente. Se realloc falhar, perdes a referência ao bloco original → memory leak.

A Função free

13

Devolve o bloco de memória alocado ao sistema. Toda memória dinâmica deve ser libertada.

free(nomeDoPonteiro);
nomeDoPonteiro = NULL;  // Boa prática

🚫 Memory Leak

Esquecer o free → memória perdida até o programa terminar. Crítico em embarcados que ficam ligados meses.

✅ Boa prática

Após o free, atribui NULL ao ponteiro para evitar dangling pointers.

Gestão de Turma — Parte 1

14

Vamos usar calloc, malloc, realloc e free num programa único.

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

int main() {
    int n;

    printf("Quantidade inicial de alunos: ");
    if (scanf("%d", &n) != 1 || n <= 0) return 1;

    // --- 1. CALLOC: notas inicializadas a 0 ---
    int *notas = (int *) calloc(n, sizeof(int));
    if (notas == NULL) {
        printf("Erro: calloc falhou!\n");
        return 1;
    }

    // --- 2. MALLOC: código da turma ---
    int *codigo = (int *) malloc(sizeof(int));
    if (codigo == NULL) {
        free(notas);
        return 1;
    }
    *codigo = 101;

Gestão de Turma — Parte 2

15
    // --- 3. REALLOC: aumentar turma ---
    int novo_tamanho = n + 3;
    int *temp = (int *) realloc(notas, novo_tamanho * sizeof(int));

    if (temp == NULL) {
        printf("Erro: realloc falhou!\n");
        free(notas);
        free(codigo);
        return 1;
    }
    notas = temp;

    // Preenche as novas posições (n .. n+2)
    for (int i = n; i < novo_tamanho; i++) {
        printf("Nota do aluno %d: ", i + 1);
        scanf("%d", &notas[i]);
    }

    // --- 4. FREE: libertar tudo ---
    free(notas);   notas = NULL;
    free(codigo); codigo = NULL;

    return 0;
}

Armadilhas Comuns

16

🚫 Esquecer free

Causa memory leaks. Crítico em sistemas embarcados ligados por longos períodos.

🚫 Dangling Pointer

Usar um ponteiro já libertado. Mitigação: atribuir NULL após free.

🚫 Ignorar NULL

Assumir que malloc funcionou gera segmentation fault em cenários de pouca memória.

🛡️
Regra de ouro: cada malloc / calloc / realloc deve ter um free correspondente. E cada alocação deve ter o seu NULL check.

Imprimir Endereços com %p

17

O especificador %p formata e exibe endereços de memória em hexadecimal.

#include <stdio.h>

int main(void) {
    int numero = 42;
    int *ptr = &numero;

    printf("Valor: %d\n", numero);
    printf("Endereço de numero: %p\n", (void *)&numero);
    printf("Valor guardado em ptr: %p\n", (void *)ptr);
    printf("Endereço de ptr: %p\n", (void *)&ptr);
    printf("Valor via *ptr: %d\n", *ptr);
    return 0;
}
💡
O cast (void *) é recomendado para garantir compatibilidade com o formato esperado por %p.

O Maior Elemento com Ponteiros

18

Escreve um programa que use aritmética de ponteiros para encontrar o maior elemento de um vetor.

Requisitos

  • Função int encontrarMaior(int *v, int tamanho).
  • Percorrer com *(v + i) em vez de v[i].
  • Na main: vetor estático, calcular tamanho com sizeof.
💡
Em C, o nome de um vetor é, na prática, um ponteiro constante para o seu primeiro elemento.

Solução — Maior Elemento

19
#include <stdio.h>

int encontrarMaior(int *v, int tamanho) {
    int maior = *v;
    for (int i = 1; i < tamanho; i++) {
        if (*(v + i) > maior) {
            maior = *(v + i);
        }
    }
    return maior;
}

int main(void) {
    int numeros[] = {14, 85, 32, 9, 67, 41};
    int tam = sizeof(numeros) / sizeof(numeros[0]);
    printf("O maior é: %d\n", encontrarMaior(numeros, tam));
    return 0;
}

Exercícios Propostos

20

1 · Conceito

Explica a principal diferença entre uma variável int e um ponteiro int*.

2 · Manipulação

Declara uma variável com 50, cria um ponteiro para ela, altera o valor para 100 via ponteiro e imprime.

3 · malloc/free

Aloca dinamicamente um vetor de 50 float, verifica sucesso e liberta a memória.

4 · Turma Completa

Implementa o exemplo do slide 14–15: calloc + malloc + realloc + free.

5 · Números Pares

Cria um vetor dinâmico de N inteiros, pede ao utilizador e imprime só os pares.

6 · Inverter Vetor

Usa apenas aritmética de ponteiros para inverter um vetor de 10 inteiros.

Resumo e Considerações Finais

21

📌 Conceitos-Chave

  • Ponteiro = variável que guarda um endereço.
  • & obtém o endereço; * acede o valor.
  • malloc / calloc / realloc alocam; free liberta.

🛡️ Regras de Ouro

  • Inicializar ponteiros a NULL.
  • Verificar NULL após alocação.
  • Nunca esquecer o free.
  • No realloc, usar ponteiro temporário.

⚠️ Erros Fatais

  • Segmentation fault (usar ponteiro nulo).
  • Memory leak (esquecer free).
  • Dangling pointer (usar após free).

🚀 Próximos Passos

  • Estruturas de dados dinâmicas (listas, árvores).
  • Ponteiros para funções (callbacks).
  • Depuração com Valgrind.

Bibliografia

22

📚 Principal

ASCENCIO, Ana F. G.; VENERUCH, Edilene A.
Fundamentos da Programação de Computadores. 3. ed. Pearson, 2014.

📚 Complementar

ALMEIDA, Rodrigo M. A.; MORAES, Carlos H. V.; SERAPHIM, Thatyana F. P.
Programação de Sistemas Embarcados. Elsevier, 2016.

📚 Material Didático

Material do ISPSongo — Plano Analítico de Introdução à Programação em C.

Fim da Aula

Obrigado! Bons estudos e boa programação em C.

Próxima aula:Estruturas (struct) e Ficheiros Prática:Resolver exercícios 1 a 6
← Voltar à lista de aulas