ALOCAÇÃO DINÂMICA
Alocação Dinâmica é o processo de solicitar e utilizar memória durante a execução de um programa. Ela é utilizada para que um programa em C utilize apenas a memória necessária para sua execução, sem desperdícios de memória.
A área de memória disponível e ainda não utilizada é conhecida como HEAP e indica a quantidade de memória livre ou disponível para uso do computador.
Um exemplo de desperdício de memória pode ser identificado quando um vetor de 500 posições é declarado e não se sabe realmente se todas estas posições são necessárias à solução desejada. Com o uso dos recursos corretos para alocação dinâmica as áreas de memória usadas para guardar valores serão reservadas exatamente no tamanho que seja necessário para o programa em execução.
Sendo assim, a alocação dinâmica de memória deve ser utilizada quando não se sabe, por algum motivo ou aplicação, quanto espaço de memória será necessário para o armazenamento de um valor.
A alocação dinâmica permite ao programador criar variáveis e estruturas de dados em tempo de execução, ou seja, alocar memória para novo armazenamento de dado quando o programa está sendo executado.
A Linguagem C oferece um conjunto de funções que permite a alocação ou liberação dinâmica de memória da área de HEAP em um computador. De acordo com o padrão C ANSI são definidas somente quatro funções para o sistema de alocação dinâmica, disponíveis na biblioteca stdlib.h, sendo elas:
Esta função recebe um parâmetro com a quantidade de bytes que serão alocados dinamicamente por um programa e retorna um endereço de memória reservado para o armazenamento de um tipo de dado void através de seu primeiro byte alocado. Este endereço void pode ser atribuído a qualquer tipo de dado, sendo necessário moldá-lo (casting) para poder usá-lo corretamente em memória. Caso não exista área de memória disponível suficiente no HEAP a função malloc() retorna a macro NULL (nulo) que corresponde ao valor inteiro zero.
Portanto, esta primeira função apresentada como recurso de alocação dinâmica da Linguagem C padrão ANSI malloc() é utilizada para alocar espaço em memória, como se fosse um empréstimo para uso do programa que está em execução.
Representação do protótipo da função malloc():
void * malloc(size_t quantidadeBytes);
Geralmente, uma alocação dinâmica eficiente emprega o operador sizeof que calcula e informa a quantidade de bytes de um tipo de dado ou estrutura de dados em C, por exemplo:
Exemplo I:
char *p_char; p_char = (char *) malloc(3);
É importante conhecer o tamanho em bytes de cada tipo de dado escalar (original) da Linguagem C, pois somente assim será possível alocar e manipular as áreas de memória corretamente. Por exemplo, a alocação sugerida no Exemplo 1 conseguirá 3 bytes da área de HEAP, onde poderão ser armazenado 3 valores do tipo char ou 1 valor do tipo int e nenhum float porque para guardar um float são necessário 4 bytes (definições na Linguagem C padrão ANSI).
No exemplo acima o ponteiro p_char receberá o endereço do primeiro byte que foi alocado dinamicamente para manipulação de 3 bytes que estarão disponíveis na memória do computador para armazenar somente 3 caracteres continuamente, pois o mesmo foi moldado (ou convertido) para o tipo de dado char. Esta área é alocada como um vetor, ou seja, de maneira contínua na memória após sua alocação dinâmica, onde o ponteiro só armazena o endereço do primeiro byte disponibilizado de toda esta área.
Exemplo II:
int *p_int; p_int = (int *) malloc(3 * sizeof(int));
Importante
int são 2 bytes na linguagem C padrão ANSII.
Neste exemplo o ponteiro p_int receberá o endereço de memória do primeiro byte que foi alocado pela função malloc(). Porém, nesta alocação foram solicitados ao computador 3 vezes o tamanho de um tipo de dado inteiro, ou seja, foram alocados 6 bytes contínuos na memória do computador como se fosse um vetor de inteiros, onde 6 bytes possuem a capacidade de armazenamento de somente 3 valores inteiros, pois cada inteiro utiliza 2 bytes na memória do computador, conforme a definição ANSI.
Observe o programa de exemplo abaixo utilizando a função malloc().
Exemplo III:
/* Sintese Objetivo: alocar e armazenar n valores dinamicamente Entrada : quantidade de valores que deseja alocar Saida : valores alocados */ #include <stdio.h> #include <conio.h> #include <stdlib.h> int main(void) { // Declaracoes int *p; int aux, quantidade; // Instrucoes printf("Informe a quantidade de valores que deseja alocar: "); scanf("%d", &quantidade); if((p = (int *) malloc (quantidade * sizeof(int)))==NULL){ printf("** Memoria insuficiente **"); getch(); exit(1); } for(aux = 0; aux < quantidade; aux++){ printf("Informe o valor %d: ", aux+1); scanf("%d", p+aux); } printf("Valor(es) alocado(s): "); for(aux = 0; aux < quantidade; aux++){ printf("%d\t", *(p+aux)); } getch(); return 0; }
Veja o resultado apresentado no monitor de vídeo do computador que está executando este programa.
O endereço de memória do tipo void, que malloc() retorna, é convertido (moldado) para um int e é atribuído a p. Este é um ponteiro definido corretamente para o tipo int. A declaração seguinte testa se a operação foi bem sucedida e caso não tenha sido p receberá NULL, o que fará com que p retorne verdadeiro. Se a operação tiver sido bem sucedida uma área correspondente a um vetor de inteiros poderá ser manipulada através do uso correto da aritmética de ponteiro sobre p.
A macro NULL na Linguagem C corresponde ao valor inteiro zero e indica que uma operação ou atribuição não foi bem sucedida. O uso desta macro em alocação dinâmica normalmente procura identificar se a alocação teve sucesso ou não para que a área de memória possa ser usada.
Esta função pertencente a Linguagem C padrão, devolve a área de memória alocada dinamicamente e deve ser realizada antes do término do programa que a solicitou, ou quando o programador achar conveniente, pois não será mais utilizada no programa. Esta devolução permite que o computador possa utilizá-la para outros processamentos, inclusive uma nova alocação do mesmo programa em execução. Caso esta devolução não aconteça até o final do programa a área alocada não estará mais disponível para o uso de outras aplicações ou programas que venham a ser executados neste mesmo computador, possivelmente prejudicando o desempenho do mesmo.
Uma representação do protótipo da função free() é apresentada a seguir:
void free (void *ponteiroAlocado);
ponteiroAlocado: nome do ponteiro anteriormente alocado dinamicamente
Após a alocação de uma área de memória não será possível que a mesma seja novamente alocada, pois está em uso e não pertence mais a área de memória disponível (HEAP). Uma nova alocação desta mesma área só será possível depois que a função free() devolvê-la a área de HEAP, sendo sempre necessária esta devolução antes do final do programa que a alocou. Isso só é possível pela função free() tendo como parâmetro o endereço inicial da área previamente alocada e armazenada em um ponteiro pela função malloc().
Exemplo I:
/* Sintese Objetivo: alocar e armazenar n valores dinamicamente Entrada : quantidade de valores que deseja alocar Saida : valores alocados */ #include <stdio.h> #include <conio.h> #include <stdlib.h> int main(void) { // Declaracoes int *p; int aux, quantidade; // Instrucoes printf("Informe a quantidade de valores que deseja alocar: "); scanf("%d", &quantidade); if((p = (int *) calloc (quantidade, sizeof(int)))==NULL){ printf("** Memoria insuficiente **"); getch(); exit(1); } for(aux = 0; aux < quantidade; aux++){ printf("Informe o valor %d: ", aux+1); scanf("%d", p+aux); } printf("Valor(es) alocado(s): "); for(aux = 0; aux < quantidade; aux++){ printf("%d\t", *(p+aux)); } free(p); getch(); return 0; }
Veja o resultado apresentado no monitor de vídeo do computador que está executando este programa.
Quando for feita uma alocação dinâmica da memória será sempre necessário que a liberação desta área aconteça, quando a mesma não for mais necessária e antes do final da execução do programa.
No exemplo acima foi mostrado que p foi a variável a ser alocada. Depois de uma alocação sempre deve se feita a liberação da memória, como é mostra no final do programa de exemplo acima com a função free(p) (p = variável que foi alocada) para liberar a memória.
Existe uma outra função na Linguagem C que também realiza a alocação dinâmica de memória disponível, sendo esta denomiada calloc(). Diferentemente de malloc() esta função aloca uma quantidade de memória resultante da operação aritmética de multiplicação entre seus dois parâmetros. A representação de seu protótipo pode ser analisada a seguir:
void * calloc(int quantidadeElemento, int tamanhoCadaElemento);
A calloc() também retorna um ponteiro void para o primeiro byte alocado, podendo ser atribuído corretamente a qualquer tipo de dado de um ponteiro após ser moldado ou convertido como é feito em malloc(). Se não houver memória suficiente para alocar a memória requisitada a função calloc() também retornará o valor NULL para o ponteiro.
Observe o exemplo a seguir usando o tipo de dado int modificado por long (long int) que transformará cada elemento int de 2 bytes em 4 bytes.
long int *p; p = (long int*) calloc (50, sizeof(long int));
Por meio das instruções acima o programa realizará a alocação de 50 elementos contendo 4 bytes cada um, ou seja, esta alocação dinâmica solicitará 200 bytes para área de HEAP do computador que será usado pelo programa em execução como um vetor de 50 elementos long int através do ponteiro p.
Uma característica da função calloc() é sempre inicializar todo o conteúdo da área alocada dinamicamente com o valor zero.
Veja a seguir um programa de exemplo utilizando esta nova função:
Exemplo I
/* Sintese Objetivo: alocar e armazenar n valores dinamicamente Entrada : quantidade de valores que deseja alocar Saida : valores alocados */ #include <stdio.h> #include <conio.h> #include <stdlib.h> int main(void) { // Declaracoes int *p; int aux, quantidade; // Instrucoes printf("Informe a quantidade de valores que deseja alocar: "); scanf("%d", &quantidade); if((p = (int *) calloc (quantidade, sizeof(int)))==NULL){ printf("** Memoria insuficiente **"); getch(); exit(1); } for(aux = 0; aux < quantidade; aux++){ printf("Informe o valor %d: ", aux+1); scanf("%d", p+aux); } printf("Valor(es) alocado(s): "); for(aux = 0; aux < quantidade; aux++){ printf("%d\t", *(p+aux)); } free(p); getch(); return 0; }
Veja o resultado apresentado no monitor de vídeo do computador que está executando este programa.
A função realloc() é usada para redimensionar um espaço alocado previamente com malloc() ou calloc(). Seus parâmetros são um ponteiro para o início de uma área previamente alocada, e o novo tamanho a ser redimensionado na alocação, podendo este ser maior ou menor que o tamanho inicialmente alocado.
Esta função serve para realocar memória e tem seu protótipo representado abaixo:
void *realloc (void *enderecoAlocado, int novoTamanhoAlocado)
Esta função modifica o tamanho da memória previamente alocada e apontada pelo ponteiro enderecoAlocado para aquele tamanho especificado por novoTamanhoAlocado.
O valor do parâmetro novoTamanhoAlocado pode ser maior ou menor que o inicialmente alocado, sendo retornado pela função realloc() um endereço do tipo void para a nova área realocada.
Uma realocação pode resultar na alteração de todo bloco de dados já armazenado na primeira alocação para um novo endereço, principalmente quando esta realocação solicitar o aumento da área alocada. Se isso ocorrer, o conteúdo do bloco antigo é copiado para nova área alocada e nenhuma informação é perdida na primeira alocação.
Exemplo I:
/* Sintese Objetivo: alocar/realocar e armazenar n valores dinamicamente Entrada : quantidade de valores que deseja alocar e realocar Saida : valores alocados e realocados */ #include <stdio.h> #include <conio.h> #include <stdlib.h> int main(void) { // Declaracoes int *p; int aux, quantidade; // Instrucoes printf("Informe a quantidade de valores que deseja alocar: "); scanf("%d", &quantidade); if((p = (int *) malloc (quantidade * sizeof(int)))==NULL){ printf("** Memoria insuficiente **"); getch(); exit(1); } for(aux = 0; aux < quantidade; aux++){ printf("Informe o valor %d: ", aux+1); scanf("%d", p+aux); } printf("Valor(es) alocado(s): "); for(aux = 0; aux < quantidade; aux++){ printf("%d\t\t", *(p+aux)); } printf("\n\nInforme a quantidade de valores que deseja realocar: "); scanf("%d", &quantidade); if((p = (int *) realloc (p, quantidade * sizeof(int)))==NULL){ printf("** Memoria insuficiente **"); getch(); exit(1); } for(aux = 0; aux < quantidade; aux++){ printf("Informe o valor %d: ", aux+1); scanf("%d", p+aux); } printf("Valor(es) realocado(s): "); for(aux = 0; aux < quantidade; aux++){ printf("%d\t\t", *(p+aux)); } free(p); getch(); return 0; }
Veja o resultado apresentado no monitor de vídeo do computador que está executando este programa.
No exemplo acima são alocadas 2 posições para uso do programa e depois é feita uma realocação, com uso da função realloc(), para obter uma maior área de memória capaz de armazenar até 3 valores no mesmo programa (aumento da área alocada dinamicamente para 3 elementos).
A função realloc() permite a realocação de uma área de memória alocada por malloc() ou calloc(), devendo a mesma também ser devolvida a área de HEAP no momento mais oportuno pela função free() e antes do fim da execução deste programa.
Com intuito de fixar a aprendizagem sobre Alocação Dinâmica, sugere-se verificar o exemplo para ajudar no entendimento e aperfeiçoamento deste conteúdo. |
No intuito de fixar a aprendizagem iniciada por meio deste módulo e verificar como está sua compreensão sobre o mesmo, são sugeridos alguns exercícios de fixação para serem resolvidos. Selecione no menu ao lado a opção de Exercício Alocação Dinâmica o para dar início a revisão dos conteúdos estudados até este momento.