Você está na página 1de 15

Memria dinmica

Por enquanto, a capacidade de armazenamento de um programa estava limitada s variveis


declaradas no cdigo fonte. O espao destinado estas variveis denominado memria
esttica, pois seus tamanhos foram definidos no cdigo fonte e foram fixados durante a
compilao. Portanto, o tamanho dessa memria no pode ser modificado durante a
execuo, mesmo que a demanda de memria do programa venha a crescer. Por exemplo,
com memria esttica apenas, o programa no pode modificar o tamanho de um vetor j
declarado. O gerenciamento da memria esttica realizado automaticamente pelo
compilador, em tempo de compilao, e pelo sistema operacional, em tempo de execuo.
Note que variveis locais em rotinas tambm se enquadram como memria esttica, pois
seu tamanho definido e fixado em tempo de compilao, embora possam ser alocadas e
desalocadas, quando as rotinas iniciam e terminam.
Neste captulo estudaremos os mecanismos da linguagem C para escrever cdigo que
gerencia a memria de forma dinmica, i.e., durante a execuo do programa. O programa
poder aumentar ou diminuir a quantidade de memria em uso a cada instante. Por
exemplo, a quantidade de e memria alocada para estruturas e vetores podero variar ao
longo da execuo do programa. Este novo estilo de gerenciamento de memria ser
chamado de memria dinmica.
Tipicamente, os apontadores sero o veculo atravs do qual as limitaes da memria
esttica sero contornadas. O ganho proporcionado por esta flexibilidade exige uma
compreenso melhor do funcionamento da memria, e ensejar o aparecimento de novos
algoritmos, mais elaborados, e de novas estruturas de dados.

Gerenciar memria dinmica


O programa precisar invocar funes especiais para solicitar mais espao na memria.
Uma vez obtido o novo espao de memria,em tempo de execuo, o programa torna-se
proprietrio deste espao, no sentido de que apenas instrues desse programa podem
alterar os valores dentro dessas novas posies de memria, obtidas como resultado da
execuo destas funes especiais.
Dentro de um espao que foi alocado como memria dinmica, o programa poder
armazenar qualquer dado (nmeros inteiros ou fracionrios, vetores, estruturas, etc), como
melhor convier ao programador. Portanto, um certo planejamento necessrio para que o
programa implemente sua prpria lgica de controle da memria alocadca dinmicamente.
A qualquer instante, o programa pode tambm liberar partes da memria que requisitou
dinamicamente, se no for mais usar aqueles espaos. Em particular, ao final de sua
execuo, o programa deve devolver (liberar) a memria dinmica que requisitou e ainda
no liberou. Se o programa requisitar memria dinmica e no a for liberando medida que
no necessitar mais dela, ento, aos poucos e se esta situao for se repetindo, o programa
poder se apropriar de boa parte da memria disponvel na mquina podendo, inclusive, at
travar o sistema operacional (dependendo das rotinas internas do sistema operacional, isso
pode no ocorrer por interferncia do prprio sistema operacional que recusar entregar
mais memria para o programa que requisita, desde que o total livre esteja demasiadamente
baixo).

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);

O endereo retornado por malloc totalmente genrico e no possui um tipo especificado.


Por este motivo, o apontador que guarda este endereo foi declarado como void *, ou seja,
um apontador para um dado cujo tipo ainda no conhecemos.
Ler e atribuir na memria dinmica
Devido a falta do tipo para o apontador, o programa ainda no sabe como deve tratar os
dados armazenados nesse trecho de memria dinmica. Um apontador de tipo void * no
aceita o operador de dereferncia * para atribuio ou leitura.
O programa precisa converter explicitamente o endereo obtido por
malloc para um apontador para o tipo de dados que desejamos armazenar
nesta rea de memria dinmica.

Por exemplo, se desejamos armazenar um nmero inteiro na memria dinmica, devemos


faz-lo atravs de um apontador para um nmero inteiro. Para isso, convertemos apontador
genrico obtido com malloc para um apontador para nmero inteiro:

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;

Convertendo para um apontador de outro tipo, podemos forar o


programa a interpretar o contedo naquele endereo de memria como
um tipo de dado diferente.

Podemos armazenar o endereo obtido por malloc diretamente no apontador numero, sem
necessidade do apontador ap:

int *numero;
numero = (int *)malloc(1000);
*numero = 10;

Observao 1: Suponhamos que o armazenamento de um nmero inteiro requeira apenas 4


bytes. Como solicitamos 1000 bytes, o nmero ser armazenado nos primeiros 4 bytes do
trecho de memria dinmica e os demais 996 bytes esto em excesso. importante solicitar
o nmero correto de bytes com malloc, para evitar desperdcio. Ou ento oprogramador
pode lidar com esses 1000 bytes, indexando-os de 4 em 4 bytes, como em um vetor de
nmeros inteiros. Por exemplo *(numero+1) vai acessar o segundo bloco de 4 bytes da
memria alocada.
Observao 2: O apontador retornado pela funo malloc a nica forma de acessar este
novo espao de memria dinmica. Atravs dele, o programa pode escrever e ler dados
nesse espao. necessrio armazenar este apontador em uma varivel convencional. Se,
durante a execuo do programa, este apontador se perder em decorrncia de um erro de
programao, este espao de memria dinmica permanecer alocado na memria at o
final do programa, mas ser impossvel recuperar sua posio.

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

O contedo da nova varivel poder ser acessado atravs do apontador p:

*p = 5;
printf(%d, *p);

Liberar memria dinmica


Um espao de memria solicitado por malloc permanece sob propriedade do programa por
tempo indeterminado. preciso devolver este espao explicitamente ao sistema, quando
no precisarmos mais dele. Para isso, devemos usar a funo free. Ela recebe como
parmetro um apontador para o espao de memria que deve ser liberado.
Uma vez liberado, impossvel (ou muito perigoso) acessar novamente este espao de
memria dinmica. Aps a execuo da funo free, todos os apontadores referentes a este
espao tornam-se potencialmente invlidos e o melhor assumir que seria um erro tentar
ler ou atribuir atravs deles (alguns sistemas operacionais podem, ainda, permitir um acesso
memria liberada, especialmente se for um acesso de leitura apenas).
At o final de sua execuo, conveniente que o programa execute uma chamada para
free para cada apontador obtido por malloc, assim liberando toda a memria requisitada.
Para liberar a memria dinmica criada no exemplo anterior:

free(p);

Criar um vetor em memria dinmica


No exemplo anterior, utilizamos malloc para obter um apontador para a um pequeno
espao na memria, suficiente para armazenar um valor de tipo inteiro. Um uso mais
interessante de memria dinmica seria criar vetores com tamanho exatamente igual ao
desejado pelo programa. Bastaria invocar malloc e assim obter o endereo para um trecho
de memria suficientemente grande para armazenar o vetor completo.
muito conveniente a forma como a linguagem C representa vetores. Na prtica, o
compilador no distingue entre um vetor declarado no incio do programa ou um apontador
obtido com malloc. Ambos so tratados como apontadores para o primeiro elemento do
vetor. Para acessar os demais elementos do vetor, utilizamos o habitual operador de ndices,
[ ], ou usamos aritmtica de apontadores para percorrer o vetor.
Antes de invocar a funo malloc, o programa precisa calcular o tamanho total do vetor
para garantir que o espao solicitado ser suficiente para armazenar todos os elementos.
Para um vetor de 10 inteiros, cada qual ocupando 4 bytes, precisamos chamar malloc para
pedir 40 bytes de memria dinmica.
Uso de sizeof
Na solicitao de memria para vetores necessrio saber o tamanho de cada elemento, em
bytes, para calcular o tamanho total do vetor. Isto pode ser uma tarefa no trivial, pois os
tipos de dados podem apresentar tamanhos diferentes em diferentes sistemas. Alm disso,

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

Acesso aos elementos do vetor


Os elementos podem ser acessados de duas formas diferentes. A primeira opo utilizar o
operador []. Lembre que o compilador entende o apontador como contendo o endereo do
primeiro elemento do vetor, e calcula automaticamente o endereo do elemento desejado.
Continuando o exemplo anterior, vamos somar o terceiro e quarto elemento e guardar o
resultado no segundo:

vetor[1] = vetor[2] + vetor[3];

A segunda forma de acesso utilizar aritmtica de apontadores:

*(vetor+1) = *(vetor+2) + *(vetor+3);

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);

Alterar tamanho de um espao de memria dinmica


Com auxlio de memria dinmica, foi possvel criar um vetor exatamente no tamanho
necessrio. No entanto, uma vez chamada a funo malloc, o programa no pode mais
alterar o tamanho do vetor.
A funo realloc (reallocate memory) solicita ao sistema redimensionar um espao de
memria adquirido previamente com malloc. Esta funo recebe como parmetro o
apontador para o espao de memria que desejamos redimensionar e um nmero inteiro
que o novo tamanho desejado, em bytes. O resultado da solicitao ser um apontador
para um novo espao de memria dinmica com as dimenses desejadas, ou NULL se a
solicitao no puder ser atendida por falta de memria disponvel.
Se o novo tamanho for maior, realloc copia os dados j existentes do espao atual para o
novo espao alocado. Se o novo tamanho solicitado for menor que o tamanho do espao
original, ento realloc copia tantos bytes quanto possveis do espao original para o novo
espao.
A funo retorna o apontador para o novo espao de memria, que pode estar em posio
diferente daquela ocupada pelo espao alocado antes da chamada a realloc.
Note que o programa precisa garantir que todos os apontadores que operavam sobre o
espao original sejam atualizados, uma vez que agora opeamos sobre uma nova rea de
memria.
Exemplo
O programa parecido com o exemplo anterior. Em uma segunda etapa, ele permite
redimensionar o vetor:

int *vetor, *elemento, i;


int tamanho, novo_tamanho;

L o tamanho do vetor e cria memria dinmica para armazenar o mesmo

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

for (elemento = vetor; elemento < vetor + tamanho; elemento++) {


scanf("%d", elemento);
}

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

Criar uma estrutura na memria dinmica


Outro uso interessante da combinao apontadores e memria dinmica alocar espao
para armazenar uma estrutura (struct). Este procedimento anlogo criao de vetores
em memria dinmica. Primeiro, determina-se o tamanho da estrutura com a pseudo-funo
sizeof. Em seguida, executa-se malloc para criar um espao de memria dinmica para
armazenar esta estrutura.
Exemplo
O programa define a estrutura nomeada complexo:

struct complexo {
float real;
float imaginario;
}

Declara um apontador para um nmero complexo:

struct complexo *numero_complexo;

Solicita um trecho na memria dinmica para armazenar o nmero. O resultado ser um


apontador para o espao que ser alocado. O apontador precisa ser convertido para um
apontador para o tipo apropriado:

numero_complexo = (struct complexo *)malloc(sizeof(struct complexo));


if (numero_complexo == NULL) {
printf(Nao ha memoria dinamica suficiente para esta operacao\n);
return;
}

Acesso aos atributos da estrutura


Os atributos da estrutura so acessados atravs do operador para dereferenciao (*), tal
como feito para qualquer apontador. Por exemplo, para acessar a estrutura cujo endereo
dado pelo apontador numero_complexo, escrevemos *numero_complexo.
Para acessar atributos da estrutura, utilizamos o operador de seleo de atributo (.). Note o
uso correto dos parnteses:

17/5/2010 13:46 7
(*numero_complexo).real = 10.0;
(*numero_complexo).imaginario = 10.0;

Os parnteses so necessrios pois sem eles, o compilador no saberia se deve aplicar o


operador * sobre numero_complexo ou sobre numero_complexo.real (idem para
numero_complexo.imaginario). Neste caso, indicamos que o operador * deve ser
aplicado sobre numero_complexo para obtermos o endereo da estrutura para qual ele
aponta, e s ento aplicarmos o operador de seleo de atributo (.) sobre o resultado.
Existe um atalho para acessar atributos de uma estrutura cujo endereo est armazenado em
um apontador: trata-se do operador ->. As linhas abaixo so equivalentes s anteriores:

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);

Estruturas ligadas em memria dinmica


Um vetor alocado em memria dinmica contorna a limitao do tamanho fixo. O
programa solicita a quantidade de memria necessria assim que souber o tamanho
desejado do vetor. Como estas operaes precisam ser programadas manualmente, isto
requer um esforo de programao adicional e muita ateno do programador.
Agora, desejamos tratar uma forma alternativa para armazenar na memria uma coleo
com elementos de mesmo tipo. Um vetor armazenaria todos os elementos em posies
consecutivas na memria, formando um nico bloco.
O vetor possui algumas desvantagens:
Seu tamanho fixo, e se o estamos armazenando em memria dinmica, isto exige
algum esforo extra de programao para ir redefinindo seu tamanho.
difcil adicionar elementos no comeou ou no meio do vetor. Por exemplo, para
adicionar um elemento no incio, necessrio mover todos os elementos uma
posio para frente, abrindo espao para o novo elemento.
Pelo mesmo motivo difcil retirar elementos ou troc-los de posio.
O vetor define uma ordem para percorrer os elementos.
O modelo de lista ligada prope reservar memria de forma independente para cada
elemento, em posies que no so necessariamente consecutivas. A coleo estruturada
com apontadores conectando um elemento ao outro como em uma corrente. Cada elemento
possui um apontador que indica o endereo onde se encontra o prximo elemento. A Figura
2 ilustra uma lista ligada na memria.

17/5/2010 13:46 8
inicio_lista dados dados dados NULL

Figura 2 Representao da lista ligada

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;

struct elemento *proximo;


}

A estrutura composta pelos atributos descricao, quantidade, valor_unitario e


proximo. Os trs primeiros so informaes comuns que sero armazenadas em cada
elemento da lista ligada. O atributo proximo armazenar um apontador para o prximo
elemento da lista ligada. Note que ele do tipo struct elemento *, ou seja, um
apontador para uma estrutura que armazena um (outro) elemento do mesmo tipo. A
linguagem C permite declarar apontadores nestas situaes, mesmo que a declarao do
elemento atual ainda no esteja completa.
O elemento apontado pelo atributo proximo, por sua vez, conter um outro atributo
proximo que, por sua vez, apontar para um terceiro elemento, e assim por diante,
formando uma seqncia encadeada de estruturas do mesmo tipo, todas interligados pelo
campo proximo.
Alm da declarao de tipo para a estrutura, o programa precisa conhecer o apontador para
o primeiro elemento da lista ligada. Sem ele, o programa no poderia encontrar o primeiro
elemento, e por conseqncia, no poderia encontrar os demais elementos.
Portanto declaramos tambm uma varivel que apontar para o primeiro elemento:

struct elemento * inicio_lista;

Em situaes mais complexas, um elemento poder ter mais de um apontador declarados


como atributo da estrutura. Isso permite com que os elementos sejam encadeados segundo
estratgias diferentes, o que pode facilitar o acesso a cada um deles. Em contrapartida, ser
preciso alocar mais memria para acomodar cada um dos atributos que so de tipo
apontador. Como cada apontador usa (tipicamente) 4 bytes de memria, em muitos casos
este um preo leve, em face dos benefcios que mais de um apontador podem trazer.
Percorrer a lista ligada
Desejamos percorrer a lista, com objetivo de encontrar um elemento ou imprimir todos eles
individualmente. Para isso, mantemos um apontador (chamado atual) para o elemento que
estamos visitando no momento. Este apontador comea apontando para o primeiro
elemento da lista, cujo endereo encontramos em inicio_lista.
Para cada elemento, verificamos os dados armazenados na estrutura. Em seguida,
atualizamos o apontador atual para o endereo do prximo elemento (atual->proximo).
Repetimos este processo at que o apontador atual receba NULL, sinalizando que o fim da
seqncia foi alcanado.
Note que no precisamos conhecer o nmero de elementos que esto encadeados na lista.
Tambm, no h necessidade de uma varivel contadora ou ndice, como era o caso quando
lidvamos com vetores.

17/5/2010 13:46 10
Exemplo:

struct elemento *atual;


atual = inicio_lista;
while (atual != NULL) {
// Verifica, processa, etc o dado contido neste elemento

. . .

// Segue para o proximo elemento da sequencia


atual = autal->proximo;
}

Inserir um novo elemento


Desejamos adicionar um novo elemento lista. Nesses casos, comeamos reservando
memria para este elemento (atravs de malloc). O apontador para este novo elemento
armazenado em uma varivel, novo. Em seguida, o programa preenche este elemento com
seus dados:

struct elemento *novo;


novo = (struct elemento *)malloc(sizeof(struct elemento));
...
novo->quantidade = 10;
novo->valor_unitario = 1.99;

Insero em lista vazia


A insero possui uma condio importante que precisa ser verificada: se atualmente a lista
est vazia.

inicio_lista NULL

novo dados NULL

Figura 3 Inserso em lista vazia

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

inicio_lista dados dados dados NULL

novo dados

Figura 4 - Inserso no incio da lista

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;

Insero no fim da lista

inicio_lista dados dados dados

novo dados NULL

Figura 5 Inserso no fim da lista

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:

struct elemento *ultimo;


ultimo = inicio_lista;
while (ultimo->proximo != NULL) {
ultimo = ultimo->proximo;
}

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:

struct elemento *ultimo, *novo;


novo = (struct elemento *)malloc(sizeof(struct elemento));
...
novo->quantidade = 10;
novo->valor_unitario = 1.99;

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;
}

Insero no meio da lista

anterior

inicio_lista dados dados dados dados NULL

novo dados

Figura 6 Inserso no meio da lista

Para inserir o elemento no meio da seqncia, precisamos localizar o elemento (apontado


pela varivel anterior) que anteceder o novo elemento segundo o critrio de ordenao
adotado. A insero realizada fazendo o novo elemento apontar para o elemento que
segue o anterior, e o elemento anterior apontar para o novo elemento.
Antes, necessrio verificar se o critrio de ordenao implicaria em adicionar o elemento
no incio da lista. Se este for o caso, executa-se os passos discutidos anteriormente.
Suponha que os elementos possuem um atributo numrico valor. Devemos adicionar o
elemento de tal forma que lista permanea ordenada de forma crescente com base nesse
atributo:

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

inicio_lista dados dados dados NULL

alvo

Figura 7 Remoo do incio da lista

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;

Agora liberamos a memria:

17/5/2010 13:46 14
free(alvo);
alvo = NULL;

Observao: Cuidado para o caso particular quando a lista vazia!


Remoo do meio ou fim da lista

inicio_lista dados dados dados NULL

anterior alvo

Figura 8 Remoo do meio ou final da lista

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;

Agora liberamos a memria:

free(alvo);
alvo = NULL;

De novo, verifique antes os casos particulares de remoo no incio e fim da lista.

17/5/2010 13:46 15

Você também pode gostar