Escolar Documentos
Profissional Documentos
Cultura Documentos
17/5/2010 13:46 1
Como obter memria dinmicamente
Para solicitar um trecho contguo de memria durante a execuo do programa,
necessrio invocar a funo malloc (memory allocation). O tamanho do espao solicitado,
em bytes, deve ser passado como parmetro para a funo malloc. Esse espao deve ser
suficiente para acomodar as informaes que desejamos armazenar neste trecho de
memria dinmica que ser alocado.
O resultado da solicitao ser o endereo do, i.e. um apontador para o, primeiro byte do
trecho de memria obtido, ou a constante NULL caso a solicitao no puder ser atendida.
necessrio armazenar este endereo em uma varivel tipo apontador, pois ele ser a nica
forma atravs da qual poderemos acessar este trecho de memria dinmica que acaba de ser
alocado. O tipo do apontador determinar como o compilador vai se comportar quando o
programador realizar operaes bsicas com endereos usando apontadores para esse trecho
de memria.
Por exemplo, para solicitar 1000 bytes e guardar o endereo inicial do trecho de memria
obtido em um apontador ap:
void * ap;
ap = malloc(1000);
void * ap;
int *numero;
ap = malloc(1000);
numero = (int *)ap;
Agora, numero aponta para o mesmo endereo de memria que o apontador ap, ou seja,
numero aponta para o incio do trecho de memria dinmica obtida por malloc. Atravs do
apontador numero, o programa trata este trecho de memria como um vetor de nmeros
17/5/2010 13:46 2
inteiros. Podemos atribuir ao endereo apontado por numero da mesma forma como j
estamos familiarizados:
*numero = 10;
Podemos armazenar o endereo obtido por malloc diretamente no apontador numero, sem
necessidade do apontador ap:
int *numero;
numero = (int *)malloc(1000);
*numero = 10;
Exemplo
Desejamos criar uma varivel de Memria esttica Memria dinmica
tipo inteiro na memria dinmica.
680 910
Utilizaremos a funo malloc para
solicitar espao. Uma varivel de p 911 681 911
tipo int ocupa 4 bytes de memria,
682 912
que o valor que precisa ser
passado para a funo. O endereo ... ...
da memria obtida deve ser
Figura 1 Alocao dinmica de memria
armazenado na varivel apontadora
p, de tipo int *. Como malloc retorna um apontador genrico, antes de atribu-lo
varivel p, necessrio realizar uma converso explcita para int *:
int *p;
17/5/2010 13:46 3
p = (int*)malloc(4);
Consulte: MemoriaDinamica\malloc\malloc.vcproj
*p = 5;
printf(%d, *p);
free(p);
17/5/2010 13:46 4
tipos formados por estruturas complexas no necessariamente tm tamanho igual a soma do
tamanho de seus atributos.
Para auxiliar nesta tarefa, a linguagem C possui a pseudo-funo sizeof. Ela recebe como
parmetro um tipo (inteiro, ponto flutuante, caractere, estrutura, enumerao, etc.) ou uma
varivel. A funo sizeof retorna o nmero de bytes necessrios para armazenar dados
deste tipo, ou o numero de bytes que ser alocado para a varivel.
Por exemplo, para saber o tamanho necessrio para armazenar um nmero inteiro, utiliza-se
a expresso sizeof(int). Para conhecer o tamanho necessrio para armazenar um nmero
fracionrio, utilizar-se-ia sizeof(float) ou sizeof(double).
Criar espao para o vetor
O primeiro passo calcular o tamanho total em bytes do vetor, com auxlio da pseudo-
funo sizeof. Em seguida, chama-se a funo malloc que retorna um apontador genrico
para o espao obtido na memria dinmica. Este apontador precisa ser convertido
explicitamente para um apontador para o tipo dos elementos do vetor.
conveniente verificar se o apontador retornado por malloc vlido ou no. Se ele for
NULL, significa que malloc falhou e a memria no pde ser alocada, por exemplo, por que
toda memria disponvel j foi esgotada. No caso de falha, o programa precisa tomar a
atitude apropriada, sabendo que no poder armazenar o vetor.
Exemplo
O programa pergunta ao usurio o tamanho desejado para o vetor e obtm um trecho de
memria dinmica com tamanho exato para armazenar o vetor:
int *vetor;
int tamanho;
scanf("%d", &tamanho);
...
vetor = (int*)malloc(sizeof(int) * tamanho);
if (vetor == NULL) {
printf("Nao ha memoria dinamica suficiente para esta operacao\n");
return;
}
Consulte: MemoriaDinamica\Vetor01\Vetor01.vcproj
17/5/2010 13:46 5
Liberar espao do vetor
No fim do programa, no se deve esquecer de liberar o vetor com a funo free.
free(vetor);
scanf("%d", &tamanho);
vetor = (int*)malloc(sizeof(int) * tamanho);
if (vetor == NULL) {
printf("Nao ha memoria dinamica suficiente para esta operacao\n");
return;
}
L os elementos do vetor
17/5/2010 13:46 6
L o novo tamanho para o vetor. Note que a varivel vetor precisa receber o novo
apontador, pois o endereo de memria anterior que contm o vetor pode ter mudado.
scanf("%d", &novo_tamanho);
vetor = (int *)realloc(vetor, novo_tamanho);
Consulte: MemoriaDinamica\Vetor02\Vetor02.vcproj
struct complexo {
float real;
float imaginario;
}
17/5/2010 13:46 7
(*numero_complexo).real = 10.0;
(*numero_complexo).imaginario = 10.0;
numero->real = 10.0;
numero->imaginario = 5.0;
Como de costume, no final o programa precisa liberar o espao de memria dinmica que
requisitou:
free(numero);
17/5/2010 13:46 8
inicio_lista dados dados dados NULL
O elemento base da lista ser declarado como uma estrutura (struct) formada por um ou
mais atributos de dados, alm de um apontador para o prximo elemento da seqncia. O
ltimo elemento aponta para NULL, indicando o fim da seqncia. Cada elemento criado
com malloc e permanece na memria at ser liberado atravs de free. O programa
mantm um apontador para o primeiro elemento da coleo. Para acessar um determinado
elemento, realizamos uma busca que parte do primeiro elemento e continua seguindo os
apontadores para o prximo elemento, at encontrar aquele que se deseja acessar, ou at
esgotar a lista toda.
A combinao de estruturas ligadas e memria dinmica um dos mecanismos de
armazenamento mais versteis em programao. As estruturas ligadas permitem que novos
elementos sejam agregados ou removidos da coleo de forma rpida e fcil. Alm disso, as
necessidades de uso de memria podem ser controladas pelo programador, que requisita ou
libera memria apenas quando vai agregar mais elementos lista, ou vai retirar elementos
da lista. No preciso reservar logo de incio o mximo de memria que o programa
eventualmente usar.
A versatilidade das estruturas ligadas implica alguns pontos de ineficincia no mecanismo
de armazenamento. Para cada elemento, comparando com vetores, h um acrscimo no
nmero de bytes necessrio para armazen-lo, pois alm dos dados precisamos armazenar o
apontador para o prximo elemento. Alm disso, a estrutura ligada no permite acessar
diretamente um elemento atravs de um ndice, como era o caso de vetores. A lista ligada
sempre exige uma busca sequencial.
Vemos, portanto, que a lista ligada indicada para programas que inserem, alteram e
removem freqentemente elementos da lista, mas poucas vezes buscam elementos pelo
ndice. Por exemplo, listas ligadas no so recomendadas para vetores e matrizes, que so
casos tpicos de dados acessados atravs de ndices.
J a lista ligada til quando precisamos, muitas vezes, inserir, alterar e remover elementos
da lista, ou percorrer a lista desde que isto possa ser feito eficientemente sem o uso de
ndices.
A lista vazia
A Figura 2 mostra uma lista ligada com trs elementos. Precisamos de uma representao
para a lista com zero elementos. A soluo mais simples ser fazer a varivel que aponta
para o primeiro elemento da lista armazenar o valor NULL, apontando, portanto, para
nenhum elemento.
Declarao de elementos de uma lista ligada
Um elemento da lista ligada ser sempre uma estrutura, cujos atributos so os dados do
elemento e o apontador para o prximo elemento. Isto vlido mesmo quando cada
elemento armazena apenas um dado.
Por exemplo, uma lista ligada para armazenar itens de compras poderia ser declarada na
forma:
17/5/2010 13:46 9
struct elemento {
char descricao[100];
int quantidade;
float valor_unitario;
17/5/2010 13:46 10
Exemplo:
. . .
inicio_lista NULL
Se este for caso, a insero trivial. O apontador inicio_lista que era NULL passa a ser
atualizado para o endereo do novo elemento (Figura 3). No devemos esquecer que o
atributo proximo do novo elemento deve receber o valor NULL para sinalizar que agora ele
tambm o ltimo elemento da seqncia:
novo->proximo = NULL;
inicio_lista = novo;
Se a lista no for vazia, ento temos trs opes: inserir no incio, no fim ou no meio da
lista (segundo algum critrio de ordenao).
17/5/2010 13:46 11
Insero no incio da lista
novo dados
A opo mais gil inserir um elemento no incio da lista. O novo elemento deve apontar
para o incio da seqncia j existente, e atualizamos o apontador inicio_lista para
apontar para o endereo do novo elemento:
novo->proximo = inicio_lista;
inicio_lista = novo;
Inserir elementos no final requer mais passos. Precisamos percorrer toda a lista para
descobrir qual o ltimo elemento (Figura 5). Comeamos assumindo que o ltimo
elemento o primeiro da lista. Se o atributo prximo deste elemento no for NULL, ento
significa que precisamos seguir para o prximo elemento, at encontrar o ltimo:
Nesse ponto, o atributo proximo do ltimo elemento conter NULL. Atualizamos este
atributo para o endereo do novo elemento. No podemos esquecer quer o atributo
proximo do novo elemento tambm deve conter o valor NULL, sinalizando que ele agora
o ltimo elemento da seqncia:
novo->proximo = NULL;
ultimo->proximo = novo;
17/5/2010 13:46 12
Porm, cuidado para o caso quando a lista vazia. Voc consegue imaginar como tratar
esse caso particular? Na verdade igual insero na primeira posio:
if (inicio_lista == NULL) {
// Insere o primeiro elemento
novo->proximo = NULL;
inicio_lista = novo;
} else {
// Insere no final da lista
ultimo = inicio_lista;
while (ultimo->proximo != null) {
ultimo = ultimo->proximo;
}
novo->proximo = NULL;
ultimo->proximo = novo;
}
anterior
novo dados
17/5/2010 13:46 13
struct elemento *anterior;
if (novo->valor < inicio_lista->valor) {
// Inserir no incio
novo->proximo = inicio_lista;
inicio_lista = novo;
} else {
anterior = inicio_lista;
while ((anterior-> proximo != NULL)
&& (anterior->valor < novo->valor)) {
anterior = anterior->proximo;
}
novo->proximo = anterior->proximo;
anterior->proximo = novo;
}
Cuidado quando a varivel anterior apontar para o ltimo elemento da lista. Tente
encontrar uma soluo para este caso particular.
Remoo de um elemento da lista
Desejamos remover um elemento da lista. Sempre precisamos identificar o elemento que
desejamos remover atravs de uma busca que percorre a lista. Supondo que esta busca j foi
realizada, assumimos que a varivel alvo contm o endereo do elemento que deve ser
removido da lista.
Verificamos se o elemento o primeiro da lista. Se for, necessrio apenas atualizar a
varivel inicio_lista (Figura 7). Caso contrrio, tambm aproveitamos a busca para
localizar o elemento que antecede aquele que desejamos excluir, armazenando seu endereo
no apontador anterior (Figura 8). Aps remover o elemento da lista, costuma-se liberar a
memria ocupada pelo elemento, usando a funo free.
Remoo do primeiro elemento da lista
alvo
Atualizamos a varivel inicio_lista para ela apontar para o segundo elemento (que o
sucessor do primeiro):
inicio_lista = inicio_lista->proximo;
ou:
inicio_lista = alvo->proximo;
17/5/2010 13:46 14
free(alvo);
alvo = NULL;
anterior alvo
O elemento alvo removido da lista fazendo o elemento anterior apontar para o sucessor do
elemento alvo:
anterior->proximo = alvo->proximo;
ou:
anterior->proximo = anterior->proximo->proximo;
free(alvo);
alvo = NULL;
17/5/2010 13:46 15