Recentemente o Felipe Neves apresentou neste artigo um sistema para gerenciamento de memória chamado TLSF. Além disso, destacou que esse tópico é bem discutido por quem desenvolve sistemas embarcados, apresentando opiniões bem divergentes. No entanto, independente do sistema de alocação dinâmica de memória, será sempre necessário operar com endereços de memória! Assim sendo, neste artigo, será destacada a função do ponteiro.
Alocação Dinâmica de Memória
A alocação dinâmica de memória é um mecanismo utilizado para possibilitar que uma quantidade de memória seja reservada durante o tempo de execução de uma aplicação. A região de memória utilizada para alocação dinâmica é conhecida como heap. Para saber mais sobre como um programa é organizado em memória consulte o primeiro artigo sobre ponteiros ou artigo sobre TLSF do Felipe Neves.
A biblioteca C padrão define 4 funções para alocação dinâmica de memória. Essas quatro funções são definidas na biblioteca STDLIB.H:
- malloc;
- calloc;
- realloc;
- free.
Veremos que em todas essas funções os ponteiros são utilizados! Mas antes será necessário apresentar um tipo de ponteiro que ainda não foi discutido nos artigos anteriores.
O Ponteiro void
Um ponteiro declarado como void é um ponteiro sem tipo! A função de um ponteiro void é simplesmente armazenar um endereço, pois nenhuma operação aritmética ou manipulação do conteúdo pode ser realizada. Um ponteiro do tipo void tem a seguinte forma:
void * nome_do_ponteiro;
Então, se em determinado momento é necessário apenas manter um endereço, um ponteiro void pode ser utilizado. Nas funções de alocação dinâmica essa característica pode ser observada uma vez que a função é genérica para qualquer tipo de dados.
Funções de Alocação Dinâmica de Memória
Começando pelas duas principais funções, malloc e free, temos as seguintes assinaturas:
void *malloc (size_t); void free (void *);
O parâmetro size_t é utilizado para definir o tamanho de um bloco de dados que será alocado. Cabe ressaltar que o tamanho do bloco deve ser definido em bytes, sendo o operador sizeof, discutido no segundo artigo, essencial para garantir a portabilidade. Note que o retorno da função malloc é um ponteiro do tipo void. Esse ponteiro representa o endereço do bloco de dados alocado.
int * pt; //declaração do ponteiro para armazenar o endereço do bloco alocado pt = (int *) malloc(10*sizeof(int)); //chamada da função malloc para reservar espaço para 10 elementos do tipo int
A função free, ao contrário da função malloc, deve ser utilizada para liberar um bloco alocado. É importante ressaltar que o endereço passado para função free deve ser um endereço obtido por uma das funções de alocação. Qualquer valor informado que não corresponda ao endereço inicial de um bloco alocado pode destruir o sistema de alocação.
int * pt; //declaração do ponteiro para armazenar o endereço do bloco alocado
pt = (int *) malloc(10*sizeof(int)); //chamada da função malloc para reservar espaço para 10 elementos do tipo int
//verifica se o endereço de retorno é válido
if(pt != NULL)
{
//…operações com o ponteiro
}
free(pt); //chama a função free para liberar o bloco indicado por pt
pt = NULL; //atribui NULL para indicar que o ponteiro não tem um endereço válido
As outras duas funções de alocação são mostradas abaixo:
void *calloc (size_t, size_t); void *realloc (void *, size_t);
A função calloc tem a mesma operação malloc, isto é, reserva um bloco de memória com base na quantidade de bytes informada. A diferença está nos parâmetros recebidos. Essa função recebe no primeiro parâmetro a quantidade de elementos que serão alocados, já no segundo parâmetro é informado o tamanho do tipo de dado.
int * pt; //declaração do ponteiro para armazenar o endereço do bloco alocado pt = (int *) calloc(10, sizeof(int)); //chamada da função calloc para reservar espaço para 10 elementos do tipo int
A função realloc pode ser utilizada para redimensionar um bloco. Deste modo, um bloco pode aumentar ou diminuir o seu tamanho em tempo de execução. Para esse tipo de operação a função realloc deve receber o endereço do bloco alocado e o tamanho em bytes que será definido na operação. O retorno da função é o endereço do novo bloco alocado.
int * pt; //declaração do ponteiro para armazenar o endereço do bloco alocado
pt = (int *) malloc(10*sizeof(int)); //chamada da função malloc para reservar espaço para 10 elementos do tipo int
if (pt == NULL)
{
//memória insuficiente
}
else
{
pt = (int *) realloc(pt, 10*sizeof(int)); //chama a função realloc para reservar espaço para mais 10 elementos
if (pt == NULL)
{
//memória insuficiente
}
}
free(pt); //chama a função free para liberar o bloco indicado por pt
pt = NULL; //atribui NULL para indicar que o ponteiro não tem um endereço válido
Dever do programador
Cabe ressaltar que em todas as funções de alocação dinâmica é necessário validar o endereço retornado, pois essas funções retornam NULL se a quantidade informada não pode ser alocada. Lembre-se, do primeiro artigo, que o valor NULL não impede erros de execução, porém fornece uma alternativa para testar se o ponteiro armazena um endereço válido. Em alguns compiladores é necessário realizar o cast do endereço retornado. No padrão ANSI não é necessário, ficando a critério do programador realizar o cast de forma explícita.
Outro ponto muito importante é que a linguagem C não possui um gerenciamento automático do heap. Diante disso, é dever do programador liberar toda memória alocada na aplicação, evitando o que é chamado memory leak. O Sergio Prado já discutiu esse assunto e apresentou algumas ferramentas de teste. Vale a pena conferir!
Conclusão
Independente do mecanismo utilizado num sistema de alocação dinâmica os ponteiros têm uma função especial. Por tratar-se de um mecanismo dinâmico, isto é, que varia conforme a execução do programa, os ponteiros oferecem os recursos necessários para manipular endereços de blocos de memória. Esse assunto torna-se importante para discutir um tópico conhecido como tipo de dados abstrato.
Referências
[1] – Livro: C, completo e total – 3ª edição revista e atualizada. Herbert Schildt.Fonte da imagem destacada: https://listamaze.com/top-10-programming-languages-for-job-security/










Ótimo artigo Fernando! Eu utilizo o malloc, realloc e free para alocação de objetos e recusos gráficos (gfx proprietária) em microcontroladores.
Você conhece alguma lib de código fonte aberto para alocação dinâmica em memória externa? Antes que você responda que posso apontar o Heap para região externa eu preciso fazer alocação dinâmica na memória interna e na memória externa do microcontrolador. Encontrei dois mas achei muito intricado o código. Obrigado!
Olá, Rafael.
Não conheço outra lib. Neste link tem algumas configurações que podem ser realizadas com memória interna x memória externa.
https://www.nongnu.org/avr-libc/user-manual/malloc.html
Quando precisei trabalhar com recursos gráficos montei um pequeno gerenciador de memória, mas não precisava alocar espaço para muitos objetos. Pois, o principal objetivo era poder criar o objeto em tempo de execução….
Obrigado pelo retorno!
Obrigado pelo retorno Fernando! Eu também fiz um mini gerenciador de recursos para a memória externa… basicamente eu dividi uma região da memória externa em blocos de mesmo tamanho (como se fosse clusters de uma FAT) e quando vou carregar um recurso (seja uma imagem ou uma fonte e etc) eu varro a memória buscando por blocos vazios e retorno o ponteiro do endereço (como uma xmalloc). Mas não estou gerenciando um xfree e nem um xrealloc.
Obrigado!
Mandou bem Fernando!
Obrigado, Eder.
Fernando, dá até para você expandir o assunto, ou seja, ponteiro para ponteiro, alocação contínua ou não contínua de vetor e matriz. Isto aqui é show de bola 🙂
boa tarde pode me ajudar no ponteiro e alocação dinâmica Um determinado paciente permaneceu internado em um hospital em um período de tempo. Durante sua internação vários procedimentos foram efetuados. Para cada procedimento, as seguintes informações foram armazenadas no sistema: • Nome do procedimento; • Valor do procedimento; • Tempo de execução do procedimento (em dias); #include <stdio.h> #include <stdlib.h> float pacienteinfo (float *V, int procedimentos) { int i; float soma_procedimentos=0; for ( i=0; i< procedimentos; i++) { soma_procedimentos = soma_procedimentos + V[i]; } return soma_procedimentos / procedimentos; } int main () { int i, n; float *info; printf(“Qual é… Leia mais »