Listas Ligadas em C
Alocando e liberando variáveis
dinâmicas
Se x é um objeto qualquer
&x é um ponteiro para x
Se p é um ponteiro em C
*p é o objeto apontado por p
Podemos usar os ponteiros de C para
Alocando e liberando variáveis
dinâmicas
Uma variável ponteiro para inteiro pode ser criada
int *p;
Se uma variável p for declarada como um ponteiro para um tipo de objeto específico será possível criar dinamicamente um objeto desse tipo em específico e atribuir seu endereço a p. Isso pode ser feito usando a bib. padrão
malloc(size) : Aloca dinamicamente um parte da
memória de tamanho size e retorna um ponteiro para um item de tipo char (No C original no padrão atual devolve um ponteiro a void)
Exemplo
Declarações
int *pi; float *pr;
Comandos
pi = (int *) malloc(sizeof (int))
pr = (float *) malloc(sizeof (float))
Criação das variáveis dinâmicas *pi (inteira) e *pf (flutuante). (type *) força o ponteiro a indicar o tipo a que deve apontar
Exercício
int *p, *q; int x;
p = (int *) malloc(sizeof (int)); *p = 3; q = p; printf (“%d %d\n”, *p, *q); x = 7; *q = x; printf(“%d %d\n”, *p, *q);
p = (int *) malloc(sizeof (int)); *p = 5;
printf(“%d %d\n”, *p, *q);
3 3
7 7
5 7
Alocando e liberando variáveis
dinâmicas
Função free usada em C para liberar o
armazenamento de uma variável alocada
dinamicamente
free(p); (invalida quais quer referencias
futuras a *p. A chamada desta função torna o armazenamento ocupado por *p disponível para reutilização, a função recebe um ponteiro)
Exercício
int *p, *q;
p = (int *) malloc(sizeof (int)); *p = 5;
q = (int *) malloc(sizeof (int)); *q = 8;
free(p); p = q;
q = (int *) malloc(sizeof (int)); *q = 6;
printf(“%d %d\n”, *p, *q);
Observe detidamente
int *p;
p = (int *) malloc(sizeof (int)); *p = 3;
p = (int *) malloc(sizeof (int)); *p = 7;
Nesse caso a primeira cópia de p é perdida e seu conteúdo não pode ser liberado porque o endereço não foi salvo
O valor NULL
NULL indica um ponteiro que não aponta a
nada
O valor 0 (zero) pode ser definido como
NULL
#define NULL 0
O valor NULL (zero) pode ser atribuído a
qualquer variável ponteiro p, após o que a
referencia *p é inválida
Tome cuidado!!
Uma chamada a free(p) invalida uma referencia subseqüente a *p entretanto os verdadeiros efeitos de free não são definidos pela linguagem C, cada implementação de C pode desenvolver sua própria versão dessa função. Na maioria das implementação e o armazenamento *p é liberado mais o valor de p fica inalterado. Isso significa que embora uma referencia para *p se torne inválida talvez não exista um meio de detectar a invalidação. O valor de p é um endereço válido objeto nesse endereço, de tipo correto, pode ser usado como valor de *p . p é chamado de ponteiro perdido. E responsabilidade do programador jamais usar esse ponteiro num programa. Convém explicitamente definir p como NULL depois de executar free(p).
•Se p e q são ponteiros com o mesmo valor, as variáveis *p e
*q são idênticas. *p e *q referem-se ao mesmo objeto. Uma
Listas Ligadas usando variáveis
dinâmicas
Para implementar listas ligadas o primeiro é fazer os nós.
Definiremos os nos como
struct node { int info;
struct node *next;
};
typedef struct node *NODEPTR;
info: Campo de informação
node: ponteiro para o próximo nó na lista *NODEPTR: Ponteiro a estrutura node
Listas Ligadas usando variáveis
dinâmicas
Com essa implementação do nó se declaramos
NODEPTR p;
o comando
p = getnode();
deverá colocar o endereço de um nó disponível em p
NODEPTR getnode(){ NODEPTR p;
p = (NODEPTR) malloc(sizeof(struct node)); return p;}
e o comando
freenode(p);
Deve disponibilizar o nó que está apontado por p void freenode(NODEPTR p) {free(p);}
Note que:
O programador não deverá preocupar se com o
ponteiro avail apontando para o primeiro nó disponível uma vez que os sistema de alocação rastreia esse nó
Não existe nenhum teste em getnode para
determinar a ocorrência de estouro essa condição é detectada no malloc e depende do sistema.
Operações primitivas da lista
insafter: Aceita um ponteiro p para um nó e
um item x como parâmetros. Insere x no
siguinte nó apontado por p
deleafter: Aceita um ponteiro a um nó e um
ponteiro a um item x como parâmetros.
Elimina o nó seguinte a node(p) e armazena
seu conteúdo em x
Operações primitivas das listas ligadas
void insafter (NODEPTR p, int x) { NODEPTR q; if (p == NULL) { printf(“inser. nula”); exit(1); } q = getnode(); q->info = x; q->next = p->next; p->next = q; } void deleafter (NODEPTR p,int *px) { NODEPTR q; if((p==NULL)||(p->next==NULL)) { printf(“remoção nula”); exit(1); } q = p->next; *px = q->info; p->next = q->next; freenode(q); }FILAS como LISTAS em C
Suponhamos que a struct node e NODEPTR
tenham sido declaradas struct fila {
NODEPTR inicio, fim; };
struct fila q;
Inicio e fim são ponteiros ao primeiro e último nó de uma fila representada como uma lista. Como seria empty?
int empty(struct fila *pq) { return ((pq->inicio == NULL)? 1: 0); } void insert
(struct fila *pq, int x) { NODEPTR p; p = getnode(); p->info = x; p->next = NULL; if (pq->fim == NULL) pq->inicio = p; else (pq->fim)->next = p; pq->fim = p; }
int remove(struct fila *pq) { NODEPTR p; int x; if (empty(pq)) { printf(“underflow\n”); exit(1); } p = pq->front; x = p->info; pq->fornt = p->next; if (pq->front == NULL) pq->rear = NULL; freenode(p); return(x); }
Outras operações com Listas em C
Definamos a operação place(list, x) list aponta a uma
lista linear classificada e x um elemento a ser inserido em sua posição correto na lista, operação usada para implementar o pqinsert
void place(NODPTR *plist, int x){ NODEPTR p, q;
q = NULL;
for(p = *plist; p!=NULL && x>p->info; p = p->next) q = p;
if(q == NULL) push(plist, x);//ins. x no ini lista else insafter(q, x);}
plist deve ser declarada como um ponteiro para o ponteiro da
lista uma vez que o valor do ponteiro de lista externo será alterado si x for inserido no inicio da lista usando o push. A chamada a essa rotina será place(&list, x)
Outras operações com Listas em C
Definamos insend: Insire um elemento no
final da lista
void insend(NODPTR *plist, int x){ NODEPTR p, q;
p = getnode(); p->info = x;
p->next = NULL;
if(*plist == NULL) *plist p; else { //procura o ultimo nó
for(q=*plist; q->next!=NULL; q=q->next); q->next =p;}
Outras operações com Listas em C
Função search(list, x) retorna um ponteiro
para a primeira ocorrência de x dentro de
uma lista list e o ponteiro NULL se x não
ocorrer na lista
NODEPTR search(NODPTR *plist, int x){ NODEPTR p;
for(p=plist; p!=NULL; p=p->next) if (p->info == x) return (p); return (NULL);
Listas lineares não homogêneas
Evidentemente um nó numa lista não precisa representar um inteiro, para representar uma pilha de strings de caracteres por uma lista ligada, são necessários nós contendo as strings de carateres em seus campos info. Os nós poderiam ser:
struct node{
char info[100];
struct node *next; };
Listas lineares não homogêneas
É possível que determinadas aplicações exija nós mais de um item na informação. Exemplo um nó de estudante: struct node { char name[30]; char id[9]; char address[100]; float gpindex; char major[20];
struct node *next; };
Listas lineares não homogêneas
Também podemos representar listas não homogêneas: Que contem
nós de diversos tipos. Neste caso é um nó cujos itens podem ser de 3 tipos. Uma união sempre faz os nós o suficiente grande , se usa um malloc para a união pegará o espaço para o maior dos elementos e getnode e freenode não precisarão ser reformadas
#define INTGR 1 #define FLT 2 #define STRING 3 struct node{ int etype union{ int ival; float fval; char *pval; } element;
struct node *next; };
Implementação de nós cabeçalhos
Lembrando... Nós cabeçalhos são usados para conter informações globais sobre a lista. Se o conteúdo desse nó é igual ao resto o cabeçalho pode ser implementado como os outros. Porem podem ser diferentes
struct node{ char info;
struct node *next; };
struct charstr{ int legth;
struct node *firstchar; };
struct charstr s1, s2;
s1 e s2 do tipo charstr são nós cabeçalhos para uma lista de
caracteres. O cabeçalho contem o número de caracteres na lista e um ponteiro para a lista firstchar. s1 e s2 representam