CCT-UFCA/Ciência da Computação/Introdução à Programação/Alocação Dinâmica
Alocação Dinâmica
[editar | editar código]Visão geral
[editar | editar código]A alocação dinâmica, em conjunto com ponteiros, é amplamente utilizada ao programar estruturas de dados. Na linguagem C, esse mecanismo serve para reservar memória em tempo de execução e é feito por meio de funções da biblioteca <stdlib.h>, como malloc(), calloc() e realloc(). Neste material, focaremos inicialmente apenas na função malloc(), mas recomendamos ao leitor consultar a documentação das demais funções para aprofundar o entendimento do tema.
Tipos de alocação
[editar | editar código]A alocação de memória para estruturas de dados pode ocorrer de duas formas: estática e dinâmica. Embora a alocação estática seja bastante comum, a alocação dinâmica será o foco principal no desenvolvimento dos algoritmos em disciplinas como Estruturas de Dados. Ainda assim, é fundamental dominar ambas as abordagens para avaliar corretamente, em cada situação, qual delas deve ser utilizada.
Alocação Estática vs Dinâmica
[editar | editar código]Na alocação estática, o espaço de memória é reservado em tempo de compilação, geralmente por meio de declarações como int v[100]; ou variáveis globais e locais cujos tamanhos são fixos. Esse tipo de alocação é simples, pois o compilador já conhece, antes da execução, quanto de memória será necessário. No entanto, as estruturas de tamanho estático não podem crescer ou encolher em tempo de execução; seu tamanho permanece imutável durante a vida do programa.
Já na alocação dinâmica, a memória é alocada em tempo de execução, por meio de funções como malloc(), calloc() e realloc(). Por exemplo, chamar malloc(100 * sizeof(int)); reserva, no heap, espaço suficiente para 100 inteiros. O programador passa a ter controle sobre quando e quanto de memória deseja alocar ou liberar (com free), permitindo a criação de estruturas como listas encadeadas, vetores redimensionáveis ou árvores dinâmicas, cujo tamanho pode ser definido em tempo de execução, conforme a necessidade.
Vantagens e desvantagens
[editar | editar código]- Estática:
- Vantagens: simplicidade na declaração; desalocação automática ao sair do escopo; menor risco de vazamento de memória.
- Desvantagens: tamanho fixo; desperdício potencial de memória se reservada demais; limitação quando não se conhece antecipadamente a quantidade exata de elementos.
- Dinâmica:
- Vantagens: flexibilidade para criar estruturas de tamanho variável; alocação somente quando necessário; pode aproveitar melhor a memória disponível; os dados alocados dinamicamente permanecem acessíveis após o término da função que os alocou, até que sejam liberados com free().
- Desvantagens: mais complexa, pois exige chamadas explícitas a malloc() / calloc() / realloc() e free(); risco de vazamentos (não liberar memória) ou de uso de áreas já liberadas (dangling pointers); risco de fragmentação do heap.
Quando usar cada uma
[editar | editar código]- Use alocação estática quando o tamanho da estrutura for conhecido e constante em todas as execuções ou quando a simplicidade for o principal objetivo.
- Use alocação dinâmica quando for necessário flexibilidade para crescer ou reduzir a estrutura em tempo de execução, ou quando não se sabe de antemão quantos elementos serão necessários.
Alocando Memória
[editar | editar código]A alocação dinâmica permite que o programa reserve memória em tempo de execução, de acordo com a necessidade. Na linguagem C, a função mais comum para isso é o malloc() (memory allocation), presente na biblioteca padrão <stdlib.h>.
ponteiro = (tipo *) malloc(quantidade * sizeof(tipo));
- tipo: indica o tipo de dado que o ponteiro irá armazenar.
- quantidade: número de elementos.
- sizeof(tipo): retorna o tamanho em bytes do tipo especificado.
Exemplo:
#include <stdio.h>
#include <stdlib.h>
#define MAX_VET 10
int main () {
int *vet = (int *) malloc(sizeof(int) * MAX_VET);
if (vet == NULL) {
printf("Erro na alocação de memória!\n");
return 1;
}
for (int i = 0; i < MAX_VET; i++) {
vet[i] = i;
}
for (int i = 0; i < MAX_VET; i++) {
printf("%d, ", vet[i]);
}
puts("");
return 0;
}
Esse exemplo consiste em:
- malloc(sizeof(int) * MAX_VET): aloca espaço suficiente na memória para armazenar 10 inteiros.
- (int *): converte, de forma explícita, o ponteiro genérico retornado por malloc() para um ponteiro do tipo int.
- int *vet: declara a variável ponteiro que irá armazenar o endereço da memória alocada.
Após a alocação, o código preenche o vetor com uma sequência de números de 0 a 9. Em seguida, o segundo laço imprime esses valores na tela.
⚠️ Importante: Sempre verifique se o ponteiro retornado por malloc() é diferente de NULL antes de usar a memória, pois um retorno NULL indica falha na alocação. Observe o teste condicional presente na linha 9.
Liberando Memória Alocada
[editar | editar código]Diferentemente da alocação estática, na alocação dinâmica o programador é responsável por liberar a memória que não será mais utilizada, evitando vazamentos de memória.
Para isso, utiliza-se a função free(), também da biblioteca <stdlib.h>.
Sintaxe:
free(ponteiro);
ponteiro: é o endereço da memória previamente alocada com malloc, calloc ou realloc. O exemplo anterior está correto, mas falta incluir a liberação da memória. Para finalizá-lo, vamos incluir a liberação da memória:
#include <stdio.h>
#include <stdlib.h>
#define MAX_VET 10
int main () {
int *vet = (int *) malloc(sizeof(int) * MAX_VET);
if (vet == NULL) {
printf("Erro na alocação de memória!\n");
return 1;
}
for (int i = 0; i < MAX_VET; i++) {
vet[i] = i;
}
for (int i = 0; i < MAX_VET; i++) {
printf("%d, ", vet[i]);
}
puts("");
// Libera a memória previamente alocada
free(vet);
return 0;
}
- free(vet): libera a memória que foi previamente alocada com malloc(). Esse passo é essencial para evitar vazamentos de memória em programas C.