LISTAS LIGADAS II - TÓPICOS AVANÇADOS

Vimos, até então, como implementar nossas listas ligadas (pilhas, filas etc) usando ponteiros simples. Contudo, havia um problema. Considere o código abaixo:

#include <stdio.h>
#include <stdlib.h>

#define coord struct s_coord
struct s_coord {
	int x;
	int y;
};

#define impacto struct s_impacto
struct s_impacto {
	coord local;
	int mag;
	impacto *prox;
};

/*
	Cria uma lista vazia. Retorna ponteiro para seu início.
*/
impacto *inicializa() {
	return(NULL);
}


/*
	Retorna o número de elementos da lista.

	Param:
		cab - a cabeça da lista
*/
int tamanho(impacto *cab) {
	impacto *p; /* auxiliar */
	int n = 0;  /* número de elementos da lista */

	/* aponto p para a cabeça da lista */
	p = cab;

	/* corro a lista, incrementando n */
	while (p) {
		p = p->prox;
		n++;
	}

	/* retorno o número de elementos da lista */
	return(n);
}


/*
	Inclui um elemento na n-ésima posição da lista. Se a posição for ilegal, não faz nada.
	Retorna a nova cabeça da lista, ou NULL em caso de erro.

	Param:
		cab - a cabeça da lista
		n - o número da posição
		pos - as coordenadas da queda
		mag - a magnitude do impacto
*/
impacto *inclui(impacto *cab, int n, coord pos, int mag) {
	impacto *p; /* auxiliar */
	impacto *q; /* o novo elemento */
	int i;      /* contador */

	/* verifico se a posição é válida */
	if ((n > 0) && (n <= (tamanho(cab) + 1))) {
		/* aloco o novo elemento */
		if (!(q = (impacto *)malloc(sizeof(impacto)))) return(NULL);

		/* abasteço seus valores */
		q->local = pos;
		q->mag = mag;

		/* verifico se a posição é a primeira na lista */
		if (n == 1) {
			/* faço a cabeça ser o próximo elemento de q */
			q->prox = cab;

			/* retorno a nova cabeça */
			return(q);
		}

		/* n não é 1, aponto p para a cabeça */
		p = cab;

		/* vou ao (n-1)-ésimo elemento */
		for (i=1; i < (n-1); i++) p = p->prox;

		/* faço o n-ésimo elemento ser o próximo de q */
		q->prox = p->prox;

		/* faço q ser o próximo elemento do (n-1)-ésimo elemento */
		p->prox = q;
	}

	/* retorno a cabeça da lista */
	return(cab);
}


/*
	Imprime a lista na tela.

	Param:
		cabeca - o início da lista
*/
void imprime(impacto *cabeca) {
	impacto *p; /* auxiliar */

	/* aponto p para o início da lista */
	p = cabeca;

	/* imprimo cada elemento da lista */
	while (p) {
		printf("\nCoordenadas: %d, %d\n",p->local.x,p->local.y);
		printf("Magnitude: %d\n",p->mag);
		p = p->prox;
	}
}


int main() {
	impacto *cabeca; /* cabeça da lista */
	coord queda;     /* coordenadas da queda */
	int mag;         /* magnitude do impacto */
	int i;           /* contador */

	/* inicio a lista */
	cabeca = inicializa();

	/* pego as 5 quedas */
	for (i=1; i<6; i++) {
		printf("Entre uma coordenada (x y): ");
		scanf("%d %d", &queda.x, &queda.y);
		printf("Qual a magnitude (0 a 10): ");
		scanf("%d", &mag);

		/* incluo o dado na lista, na posição i */
		if (!(cabeca = inclui(cabeca, i, queda, mag))) {
			printf("Erro na inclusão da nota na lista\n");
			exit(1);
		}

		/* mostro o estado atual da lista */
		imprime(cabeca);
	}
}
		

A função inclui nos apresenta 2 problemas básicos. O primeiro é o fato de, se a posição for ilegal, ela simplesmente não fazer nada e não nos avisar do fato. Com isso, podemos tentar incluir um elemento em posição ilegal e achar que ele foi incluído, quando não foi. Uma vez que a função não nos avisa do fato, vamos acabar descobrindo da pior maneira - quando verificarmos os dados. E aí fica realmente difícil achar o problema.

O segundo problema ocorre na alocação. Note que se ela não conseguir alocar espaço para um único elemento ela retorna NULL. Ou seja, ao fazermos:

		if (!(cabeca = inclui(cabeca, i, queda, mag))) {
			printf("Erro na inclusão da nota na lista\n");
			exit(1);
		}
		

cabeca será NULL, caso o elemento não possa ser alocado. Isso faz simplesmente com que percamos a posição na memória de nossa lista, ou seja, perdemos a lista na memória. No caso acima, isso não importa muito, já que saímos do programa caso isso aconteça. Mas e se quiséssemos fazer outra coisa, como pedir ao usuário para liberar mais memória, por exemplo? A única forma seria fazer:

int main() {
	impacto *cabeca; /* cabeça da lista */
	coord queda;     /* coordenadas da queda */
	int mag;         /* magnitude do impacto */
	int i;           /* contador */
	impacto *p;      /* auxiliar */

	/* inicio a lista */
	cabeca = inicializa();

	/* pego as 5 quedas */
	for (i=1; i<6; i++) {
		printf("Entre uma coordenada (x y): ");
		scanf("%d %d", &queda.x, &queda.y);
		printf("Qual a magnitude (0 a 10): ");
		scanf("%d", &mag);

		/* incluo o dado na lista, na posição i */
		p = inclui(cabeca, i, queda, mag);
		if (!p) {
			/* faz o que precisa */
		}
		else /* alocou legal */
			cabeca = p;

		/* mostro o estado atual da lista */
		imprime(cabeca);
	}
}
		

Mas isso não parece muito legal. Outra maneira mais elegante seria fazer com que inclui retorne 1 em caso de sucesso e 0 em caso de erro. Então somente teríamos que testar a saída de inclui. Mas inclui precisa mudar a cabeça da lista, como faríamos isso? Como com qualquer outra variável.

Quanto tínhamos que mudar o valor de uma variável que foi passada como parâmetro, como fazíamos? Usávamos ponteiro, ou seja, passávamos o endereço na memória desta variável, para podermos modificá-la, como abaixo:

void mudaValor(int *a) {
	/* mudo o valor da variável passada como parâmetro */
	*a = 3;
}

int main() {
	int b = 2;

	printf("%d\n",b); /* imprime 2 */
	mudaValor(&b);
	printf("%d\n",b); /* imprime 3 */
}
		

Agimos da mesma forma com ponteiros. Se queremos mudar o valor de um ponteiro, ou seja, mudar para onde ele está apontando, precisamos passar seu endereço à função. O que acontece quando fazemos:

int main() {
	int a;
	int *p;

	a = 2;
	p = &a;

	*p = 3;
}
		

Na memória funciona mais ou menos assim: primeiro alocamos um espaço para a variável "a" grande o suficiente para guardar um inteiro, e cujo endereço o compilador conhece (o 0xff1 na figura):

Em seguida alocamos memória para a variável "p", grande o suficiente para caber um endereço, e cujo endereço o compilador também conhece:

Ao fazermos "a = 2" dizemos ao computador para guardar 2 na região de memória correspondente à variável "a". Isso só é possível porque sabemos o endereço de "a":

Com "p = &a" guardamos em p o endereço na memória da variável "a".

Assim podemos dizer que "p aponta para a", ou, em uma notação simbólica:

Finalmente, com "*p = 3", vamos em "p", pegamos o endereço de memória que lá está guardado, vamos àquela posição de memória e guardamos o valor 3 lá. Ou seja, fazemos com que a região da memória apontada por "p" receba o valor 3. Note que isso também muda o valor de "a":

Até aí isso deve ser algo que você já sabe. Agora o que talvez você não saiba: posso usar o esquema acima com qualquer variável, inclusive ponteiros. Considere o código abaixo:

int main() {
	int a = 2;
	int b = 5;
	int *p;
	int **pp;

	p = &a;
	*p = 3;

	pp = &p;

	**pp = 4;

	*pp = &b;

	*p = 8;

	**pp = 7;
}
		

O que essa complicação toda faz? Inicialmente, alocamos espaço para a s 4 variáveis ("a", "b", "p" e "pp"). "a" e "b" serão grandes o suficiente para guardarem valores inteiros, sendo que de cara armazenamos os valores 2 e 5 nelas, respectivamente. Da mesma forma, "p" e "pp" serão grandes o suficiente para guardarem endereços:

Quando fazemos "p = &a" guardamos o endereço da variável "a" na variável "p" (como acima):

e ao fazermos "*p = 3", guardamos o valor 3 na região de memória apontada por "p", ou seja, na região de memória cujo endereço guardamos em "p":

E agora, o que acontece quando fazemos "pp = &p"? Antes de mais nada, como "pp" é definido? "int **pp". Isso significa que "pp" é ponteiro para ponteiro de inteiro, ou seja, ele guardará o endereço de uma variável que guarda, por sua vez, o endereço de um inteiro na memória. Dessa forma, ao fazermos "pp = &p", estamos armazenando o endereço da variável "p" em "pp":

Isso na nossa notação fica:

Quando fazemos "**pp = 4" é o mesmo que fazermos "*(*pp) = 4". Ou seja, primeiro visitamos a região da memória apontada por "pp" (com "*pp"), pegando o valor lá guardado (no caso, o valor de "p", ou seja, um endereço). Então visitamos a região da memória correspondente a este endereço (com "**pp"), e lá armazenamos o valor 4. Dessa forma mudamos o valor da variável apontada por "p", ou seja, "a":

Ao fazermos "*pp = &b", vamos à região da memória apontada por "pp", ou seja, a região da memória cujo endereço guardamos em "pp" (no caso, "p"), e guardamos lá o endereço da variável "b":

Na prática o que fizemos? O mesmo que "p = &b", ou seja, fizemos "p" apontar para "b". Para provar isso, o que acontece quando fazemos "*p = 8"? Vamos à posição de memória apontada por p e armazenamos o valor 8 lá. Ou seja, armazenamos o valor 8 na variável "b":

Finalmente, o que acontece quando fazemos "**pp = 7;"? Visitamos a região da memória apontada por "pp" (com "*pp"), pegando o valor lá guardado (no caso, o valor de "p", ou seja, um endereço). Então visitamos a região da memória correspondente a este endereço (com "**pp"), e lá armazenamos o valor 4. Dessa forma mudamos o valor da variável apontada por "p", ou seja, "b":

Estude um pouco o programa acima até ter certeza de que entendeu como a coisa funciona.

Agora, e se a variável apontada fosse uma estrutura? Agimos de modo semelhante:

#include <stdio.h>
#include <stdlib.h>

#define coord struct s_coord
struct s_coord {
	int x;
	int y;
};

int main() {
	coord c1;
	coord c2;
	coord *p;
	coord **pp;

	c1.x = 2;
	c1.y = 3;
	c2.x = 9;
	c2.y = 10;

	printf("c1 = (%d,%d)\n", c1.x, c1.y);
	printf("c2 = (%d,%d)\n", c2.x, c2.y);

	p = &c1;
	p->x = 4;
	(*p).y = 5; /* alternativa a p->x */

	printf("c1 = (%d,%d)\n", c1.x, c1.y);
	printf("c2 = (%d,%d)\n", c2.x, c2.y);

	pp = &p;

	(*pp)->x = 7;
	(*pp)->y = 8;

	printf("c1 = (%d,%d)\n", c1.x, c1.y);
	printf("c2 = (%d,%d)\n", c2.x, c2.y);

	*pp = &c2;

	printf("c1 = (%d,%d)\n", c1.x, c1.y);
	printf("c2 = (%d,%d)\n", c2.x, c2.y);
	printf("c2 = (%d,%d)\n", p->x, p->y);
	printf("c2 = (%d,%d)\n", (*pp)->x, (*pp)->y);
}
		

A saída deste programa é:

c1 = (2,3)
c2 = (9,10)
c1 = (4,5)
c2 = (9,10)
c1 = (7,8)
c2 = (9,10)
c1 = (7,8)
c2 = (9,10)
c2 = (9,10)
c2 = (9,10)
		

Repare como é feito: "*pp" é um endereço para uma estrutura. Quando fazemos "(*pp)->x = 7", pegamos o endereço na memória da estrutura ("*pp"), visitamos esta região da memória e, então, vamos ao local correspondente ao campo x dessa região, lá armazenando o valor 7.

Ao fazermos "*pp = &c2" estamos guardando o endereço de "c2" na região da memória apontada por "pp", ou seja, em "p". Dessa forma estamos fazendo "p" apontar para "c2", o que é comprovado pelos 3 últimos printf.

Agora nosso próximo passo é pegar a função inclui, abaixo:

/*
	Inclui um elemento na n-ésima posição da lista. Se a posição for ilegal, não faz nada.
	Retorna a nova cabeça da lista, ou NULL em caso de erro.

	Param:
		cab - a cabeça da lista
		n - o número da posição
		pos - as coordenadas da queda
		mag - a magnitude do impacto
*/
impacto *inclui(impacto *cab, int n, coord pos, int mag) {
	impacto *p; /* auxiliar */
	impacto *q; /* o novo elemento */
	int i;      /* contador */

	/* verifico se a posição é válida */
	if ((n > 0) && (n <= (tamanho(cab) + 1))) {
		/* aloco o novo elemento */
		if (!(q = (impacto *)malloc(sizeof(impacto)))) return(NULL);

		/* abasteço seus valores */
		q->local = pos;
		q->mag = mag;

		/* verifico se a posição é a primeira na lista */
		if (n == 1) {
			/* faço a cabeça ser o próximo elemento de q */
			q->prox = cab;

			/* retorno a nova cabeça */
			return(q);
		}

		/* n não é 1, aponto p para a cabeça */
		p = cab;

		/* vou ao (n-1)-ésimo elemento */
		for (i=1; i < (n-1); i++) p = p->prox;

		/* faço o n-ésimo elemento ser o próximo de q */
		q->prox = p->prox;

		/* faço q ser o próximo elemento do (n-1)-ésimo elemento */
		p->prox = q;
	}

	/* retorno a cabeça da lista */
	return(cab);
}
		

e modificá-la de modo que ela retorne 1 em caso de sucesso, 0 em caso de posição ilegal, e -1 em caso de erro de alocação, sempre atualizando a cabeça da lista. Conforme o que foi discutido, a nova função fica:

/*
	Inclui um elemento na n-ésima posição da lista. Retorna 1 em caso de sucesso, 0 se a posição for ilegal e -1 caso não consiga alocar espaço para o novo elemento.

	Param:
		pcab - ponteiro para a cabeça da lista
		n - o número da posição
		pos - as coordenadas da queda
		mag - a magnitude do impacto
*/
int inclui(impacto **pcab, int n, coord pos, int mag) {
	impacto *p; /* auxiliar */
	impacto *q; /* o novo elemento */
	int i;      /* contador */

	/* verifico se a posição é válida */
	if ((n > 0) && (n <= (tamanho(cab) + 1))) {
		/* aloco o novo elemento */
		if (!(q = (impacto *)malloc(sizeof(impacto)))) return(-1);

		/* abasteço seus valores */
		q->local = pos;
		q->mag = mag;

		/* verifico se a posição é a primeira na lista */
		if (n == 1) {
			/* faço a cabeça ser o próximo elemento de q */
			q->prox = *pcab;

			/* atualizo a cabeça */
			*pcab = q;

			/* retorno sucesso */
			return(1);
		}

		/* n não é 1, aponto p para a cabeça */
		p = *cab;

		/* vou ao (n-1)-ésimo elemento */
		for (i=1; i < (n-1); i++) p = p->prox;

		/* faço o n-ésimo elemento ser o próximo de q */
		q->prox = p->prox;

		/* faço q ser o próximo elemento do (n-1)-ésimo elemento */
		p->prox = q;

		/* retorno sucesso (note que a cabeça não precisou ser atualizada) */
		return(1);
	}

	/* deu erro */
	return(0);
}
		

Vamos ver o programa como um todo:

#include <stdio.h>
#include <stdlib.h>

#define coord struct s_coord
struct s_coord {
	int x;
	int y;
};

#define impacto struct s_impacto
struct s_impacto {
	coord local;
	int mag;
	impacto *prox;
};

/*
	Cria uma lista vazia. Retorna ponteiro para seu início.
*/
impacto *inicializa() {
	return(NULL);
}


/*
	Retorna o número de elementos da lista.

	Param:
		cab - a cabeça da lista
*/
int tamanho(impacto *cab) {
	impacto *p; /* auxiliar */
	int n = 0;  /* número de elementos da lista */

	/* aponto p para a cabeça da lista */
	p = cab;

	/* corro a lista, incrementando n */
	while (p) {
		p = p->prox;
		n++;
	}

	/* retorno o número de elementos da lista */
	return(n);
}


/*
	Inclui um elemento na n-ésima posição da lista. Retorna 1 em caso de sucesso, 0 se a posição for ilegal e -1 caso não consiga alocar espaço para o novo elemento.

	Param:
		pcab - ponteiro para a cabeça da lista
		n - o número da posição
		pos - as coordenadas da queda
		mag - a magnitude do impacto
*/
int inclui(impacto **pcab, int n, coord pos, int mag) {
	impacto *p; /* auxiliar */
	impacto *q; /* o novo elemento */
	int i;      /* contador */

	/* verifico se a posição é válida */
	if ((n > 0) && (n <= (tamanho(*pcab) + 1))) {
		/* aloco o novo elemento */
		if (!(q = (impacto *)malloc(sizeof(impacto)))) return(-1);

		/* abasteço seus valores */
		q->local = pos;
		q->mag = mag;

		/* verifico se a posição é a primeira na lista */
		if (n == 1) {
			/* faço a cabeça ser o próximo elemento de q */
			q->prox = *pcab;

			/* atualizo a cabeça */
			*pcab = q;

			/* retorno sucesso */
			return(1);
		}

		/* n não é 1, aponto p para a cabeça */
		p = *pcab;

		/* vou ao (n-1)-ésimo elemento */
		for (i=1; i < (n-1); i++) p = p->prox;

		/* faço o n-ésimo elemento ser o próximo de q */
		q->prox = p->prox;

		/* faço q ser o próximo elemento do (n-1)-ésimo elemento */
		p->prox = q;

		/* retorno sucesso (note que a cabeça não precisou ser atualizada) */
		return(1);
	}

	/* deu erro */
	return(0);
}


/*
	Imprime a lista na tela.

	Param:
		cabeca - o início da lista
*/
void imprime(impacto *cabeca) {
	impacto *p; /* auxiliar */

	/* aponto p para o início da lista */
	p = cabeca;

	/* imprimo cada elemento da lista */
	while (p) {
		printf("\nCoordenadas: %d, %d\n",p->local.x,p->local.y);
		printf("Magnitude: %d\n",p->mag);
		p = p->prox;
	}
}


int main() {
	impacto *cabeca; /* cabeça da lista */
	coord queda;     /* coordenadas da queda */
	int mag;         /* magnitude do impacto */
	int i;           /* contador */

	/* inicio a lista */
	cabeca = inicializa();

	/* pego as 5 quedas */
	for (i=1; i<6; i++) {
		printf("Entre uma coordenada (x y): ");
		scanf("%d %d", &queda.x, &queda.y);
		printf("Qual a magnitude (0 a 10): ");
		scanf("%d", &mag);

		/* incluo o dado na lista, na posição i */
		switch (inclui(&cabeca, i, queda, mag)) {
			case 0:
				printf("Posição inválida\n");
				break;
			case -1:
				printf("Não conseguiu alocar\n");
				break;
			default:
				printf("Tudo ok\n");
		}

		/* mostro o estado atual da lista */
		imprime(cabeca);
	}
}
		

Da mesma forma, a exclusão de um elemento na lista fica:

#include <stdio.h>
#include <stdlib.h>

#define coord struct s_coord
struct s_coord {
	int x;
	int y;
};

#define impacto struct s_impacto
struct s_impacto {
	coord local;
	int mag;
	impacto *prox;
};

/*
	Cria uma lista vazia. Retorna ponteiro para seu início.
*/
impacto *inicializa() {
	return(NULL);
}


/*
	Retorna o número de elementos da lista.

	Param:
		cab - a cabeça da lista
*/
int tamanho(impacto *cab) {
	impacto *p; /* auxiliar */
	int n = 0;  /* número de elementos da lista */

	/* aponto p para a cabeça da lista */
	p = cab;

	/* corro a lista, incrementando n */
	while (p) {
		p = p->prox;
		n++;
	}

	/* retorno o número de elementos da lista */
	return(n);
}


/*
	Inclui um elemento na n-ésima posição da lista. Retorna 1 em caso de sucesso, 0 se a posição for ilegal e -1 caso não consiga alocar espaço para o novo elemento.

	Param:
		pcab - ponteiro para a cabeça da lista
		n - o número da posição
		pos - as coordenadas da queda
		mag - a magnitude do impacto
*/
int inclui(impacto **pcab, int n, coord pos, int mag) {
	impacto *p; /* auxiliar */
	impacto *q; /* o novo elemento */
	int i;      /* contador */

	/* verifico se a posição é válida */
	if ((n > 0) && (n <= (tamanho(*pcab) + 1))) {
		/* aloco o novo elemento */
		if (!(q = (impacto *)malloc(sizeof(impacto)))) return(-1);

		/* abasteço seus valores */
		q->local = pos;
		q->mag = mag;

		/* verifico se a posição é a primeira na lista */
		if (n == 1) {
			/* faço a cabeça ser o próximo elemento de q */
			q->prox = *pcab;

			/* atualizo a cabeça */
			*pcab = q;

			/* retorno sucesso */
			return(1);
		}

		/* n não é 1, aponto p para a cabeça */
		p = *pcab;

		/* vou ao (n-1)-ésimo elemento */
		for (i=1; i < (n-1); i++) p = p->prox;

		/* faço o n-ésimo elemento ser o próximo de q */
		q->prox = p->prox;

		/* faço q ser o próximo elemento do (n-1)-ésimo elemento */
		p->prox = q;

		/* retorno sucesso (note que a cabeça não precisou ser atualizada) */
		return(1);
	}

	/* deu erro */
	return(0);
}


/*
	Imprime a lista na tela.

	Param:
		cabeca - o início da lista
*/
void imprime(impacto *cabeca) {
	impacto *p; /* auxiliar */

	/* aponto p para o início da lista */
	p = cabeca;

	/* imprimo cada elemento da lista */
	while (p) {
		printf("\nCoordenadas: %d, %d\n",p->local.x,p->local.y);
		printf("Magnitude: %d\n",p->mag);
		p = p->prox;
	}
}


/*
	Apaga elemento na posição n da lista. Retorna 1 se ok e 0 em caso de posição inválida.

	Param:
		pcab - ponteiro para a cabeça da lista
		n - a posição a ser apagada
*/
int exclui(impacto **pcab, int n) {
	impacto *p; /* auxiliar */
	impacto *q; /* auxiliar */
	int i;      /* contador */

	/* verifico se a posição é válida */
	if ((n > 0) && (n <= tamanho(*pcab))) {
		/* aponto p para o início da lista */
		p = *pcab;

		/* verifico se a posição é a primeira na lista */
		if (n == 1) {
			/* aponto a cabeça para o segundo elemento na lista */
			*pcab = (*pcab)->prox;

			/* mato a antiga cabeça */
			free(p);

			/* retorno ok */
			return(1);
		}

		/* n não é 1, vou ao n-ésimo elemento com p */
		for (i=1; i < n; i++) p = p->prox;


		/* aponto q para a cabeça */
		q = *pcab;

		/* vou ao (n-1)-ésimo elemento com q */
		for (i=1; i < (n-1); i++) q = q->prox;

		/* faço o próximo elemento de q ser o elemento que está após p */
		q->prox = p->prox;

		/* elimino p */
		free(p);

		/* retorno ok */
		return(1);
	}

	/* deu erro */
	return(0);
}


int main() {
	impacto *cabeca; /* cabeça da lista */
	coord queda;     /* coordenadas da queda */
	int mag;         /* magnitude do impacto */
	int i;           /* contador */

	/* inicio a lista */
	cabeca = inicializa();

	/* pego as 5 quedas */
	for (i=1; i<6; i++) {
		printf("Entre uma coordenada (x y): ");
		scanf("%d %d", &queda.x, &queda.y);
		printf("Qual a magnitude (0 a 10): ");
		scanf("%d", &mag);

		/* incluo o dado na lista, na posição i */
		switch (inclui(&cabeca, i, queda, mag)) {
			case 0:
				printf("Posição inválida\n");
				break;
			case -1:
				printf("Não conseguiu alocar\n");
				break;
			default:
				printf("Tudo ok\n");
		}

		/* mostro o estado atual da lista */
		imprime(cabeca);
	}

	/* excluo o 3o elemento */
	if (exclui(&cabeca, 3))
		printf("Exclusão feita com sucesso\n");
	else
		printf("Posição inválida\n");

	/* imprimo a lista */
	imprime(cabeca);
}
		



Copyright© 2005 Norton Trevisan Roman - Direitos Autorais Reservados.