Alocação Dinâmica de Memória em C: Dominando malloc, calloc, realloc e free
Antes de mergulhar na alocação dinâmica de memória em C, é essencial entender como a memória é gerenciada na linguagem.
Como o Gerenciamento de Memória Funciona em C
Quando você declara uma variável com tipo primitivo, o compilador aloca espaço para ela na stack automaticamente. Por exemplo, um float tipicamente ocupa 4 bytes (dependente da plataforma). Você pode confirmar isso com o operador sizeof:
#include <stdio.h>
int main() {
float x;
printf("O tamanho de float é %d bytes", sizeof(x));
return 0;
}
A saída será:
O tamanho de float é 4 bytes
Um array de tamanho especificado também é alocado contiguamente na stack, com cada elemento ocupando o tamanho de seu tipo:
#include <stdio.h>
int main() {
float arr[10];
printf("O tamanho do array float com 10 elementos é %d", sizeof(arr));
return 0;
}
O resultado é:
O tamanho do array float com 10 elementos é 40
Embora a alocação na stack seja automática, muitos programas requerem memória cujo tamanho não é conhecido até o tempo de execução. Essa necessidade é atendida pela alocação dinâmica de memória, que usa o heap em vez da stack.
Alocação Dinâmica de Memória em C
A alocação dinâmica envolve solicitar e liberar memória manualmente conforme necessário, com ponteiros rastreando os blocos alocados no heap. A biblioteca padrão C fornece várias funções para este propósito:
- malloc() – Aloca um bloco contíguo de bytes e retorna um ponteiro para seu primeiro byte.
- calloc() – Aloca um array de elementos, inicializa todos os bytes com zero e retorna um ponteiro para o primeiro elemento.
- realloc() – Redimensiona um bloco previamente alocado, preservando seu conteúdo.
- free() – Libera um bloco previamente alocado de volta ao heap.
Vamos explorar cada função em detalhes.
Função malloc()
A função malloc() significa "memory allocation" (alocação de memória). Ela reserva um bloco de memória de tamanho especificado e retorna um ponteiro para ele. O ponteiro é tipicamente convertido para o tipo desejado:
ptr = (tipo_cast *) malloc(tamanho_em_bytes);
Exemplo:
#include <stdlib.h>
int main(){
int *ptr;
ptr = malloc(15 * sizeof(*ptr)); /* 15 inteiros */
if (ptr != NULL) {
*(ptr + 5) = 480; /* sexto elemento */
printf("Valor do 6º inteiro é %d", *(ptr + 5));
}
}
Saída:
Valor do 6º inteiro é 480
Pontos-chave:
- Use
sizeof(*ptr)para tornar o código robusto contra mudanças de tipo. - Verifique o valor de retorno
NULLpara detectar falha de alocação. - Memória alocada é contígua; trate-a como array e use aritmética de ponteiros.
Alocação dinâmica não se limita a tipos primitivos; funciona com estruturas, arrays de caracteres e mais.
Função free()
Memória alocada com malloc(), calloc() ou realloc() deve ser liberada explicitamente para evitar vazamentos. A função free() realiza esta tarefa:
#include <stdio.h>
int main() {
int* ptr = malloc(10 * sizeof(*ptr));
if (ptr != NULL){
*(ptr + 2) = 50;
printf("Valor do 2º inteiro é %d", *(ptr + 2));
}
free(ptr);
}
Saída:
Valor do 2º inteiro é 50
Função calloc()
calloc() aloca múltiplos blocos de tamanho igual e inicializa cada byte com zero, tornando-o ideal para arrays e estruturas que requerem memória zerada:
ptr = (tipo_cast *) calloc(n, tamanho);
Exemplo – somando uma sequência aritmética:
#include <stdio.h>
int main() {
int i, *ptr, sum = 0;
ptr = calloc(10, sizeof(int));
if (ptr == NULL) {
printf("Erro! memória não alocada.");
exit(0);
}
for (i = 0; i < 10; ++i) {
*(ptr + i) = i;
sum += *(ptr + i);
}
printf("Soma = %d", sum);
free(ptr);
return 0;
}
Resultado:
Soma = 45
calloc() vs. malloc()
Diferenças principais:
- Inicialização:
calloc()zera a memória;malloc()a deixa não inicializada (lixo). - Uso:
calloc()é preferível para arrays ou estruturas que precisam de valores zerados;malloc()é mais simples quando nenhuma inicialização é necessária. - Desempenho: Ambos têm desempenho similar, mas
calloc()pode ser ligeiramente mais lento devido à zeragem.
Função realloc()
A função realloc() altera o tamanho de uma alocação existente preservando seu conteúdo:
ptr = realloc(ptr, novo_tamanho);
Exemplo – estendendo um buffer de string:
#include <stdio.h>
#include <string.h>
int main () {
char *ptr = malloc(10);
strcpy(ptr, "Programming");
ptr = realloc(ptr, 20); // aumenta
strcat(ptr, " In 'C'");
printf("%s, Endereço = %u", ptr, ptr);
free(ptr);
return 0;
}
Se a realocação falhar, realloc() retorna NULL e o bloco original permanece válido.
Arrays Dinâmicos
Arrays dinâmicos permitem que o número de elementos cresça durante a execução, um padrão comum em algoritmos e estruturas de dados:
#include <stdio.h>
#include <stdlib.h>
int main() {
int *arr_dynamic = NULL;
int elements = 2, i;
arr_dynamic = calloc(elements, sizeof(int));
for (i = 0; i < elements; i++)
arr_dynamic[i] = i;
for (i = 0; i < elements; i++)
printf("arr_dynamic[%d]=%d\n", i, arr_dynamic[i]);
elements = 4;
arr_dynamic = realloc(arr_dynamic, elements * sizeof(int));
printf("Após realloc\n");
for (i = 2; i < elements; i++)
arr_dynamic[i] = i;
for (i = 0; i < elements; i++)
printf("arr_dynamic[%d]=%d\n", i, arr_dynamic[i]);
free(arr_dynamic);
}
Resultado:
arr_dynamic[0]=0 arr_dynamic[1]=1 Após realloc arr_dynamic[0]=0 arr_dynamic[1]=1 arr_dynamic[2]=2 arr_dynamic[3]=3
Resumo
- Memória dinâmica permite alocar e desalocar espaço no heap em tempo de execução.
- Use
malloc()para blocos únicos,calloc()para arrays inicializados com zero. - Sempre verifique
NULLpara detectar falhas de alocação. - Libere memória com
free()para evitar vazamentos. - Redimensione alocações com
realloc()quando mais espaço for necessário. - Arrays dinâmicos são um padrão poderoso para estruturas de dados flexíveis.
Linguagem C
- Entendendo ROM, PROM, EPROM e EEPROM: Projeto, Programação e Identificação
- Fundamentos de C#: Essenciais de Entrada e Saída
- Dominando Declarações Using em C#: Imports, Aliases e Diretivas Estáticas
- Dominando Gerenciamento de Memória em C++: Uso Expert de new e delete
- Quatro Padrões Comprovados para Funções Definidas pelo Usuário em C
- Alocação Dinâmica de Memória em C: Dominando malloc, calloc, realloc e free
- Dominando Funções da Biblioteca Padrão C: Guia Prático
- Dominando Alocação Dinâmica de Arrays em C++: Guia Prático com Exemplos de Código
- Funções em C++ Explicadas com Exemplos Práticos de Código
- Dominando Funções em C: Estrutura, Declaração e Boas Práticas