AULA 20 - Introdução à Linguagem C - FIC

De IFSC
Ir para navegação Ir para pesquisar

Ponteiros para Estruturas

Existem diversos motivos para se usar ponteiros para estruturas.

Assim como ponteiros são mais rápidos e fáceis de manipular que matrizes, também os ponteiros para estruturas são melhores que matrizes de estruturas.

Várias representações de dados que parecem fantásticas são constituídas de estruturas contendo ponteiros para outras estruturas.

A sintaxe da declaração de um ponteiro para estrutura é:

 struct rótulo *nome_ponteiro;

onde rótulo é o identificador para o tipo de estrutura e nome_ponteiro é o nome do ponteiro que aponta para tipos estrutura.

Na verdade, a sintaxe é a mesma de qualquer declaração de ponteiro.

Ex:

 struct lista *ptrl;

declara o ponteiro ptrl que pode apontar para qualquer estrutura do tipo lista.


A sintaxe da definição de um ponteiro para estrutura é:

 nome_ponteiro = &estrutura;

onde estrutura é uma estrutura qualquer do tipo que o ponteiro nome_ponteiro pode apontar.

Ex:

  ptrl = &livro[0];

define o conteúdo de ptrl como sendo o endereço do primeiro elemento da matriz de estruturas livro.


Sabemos que se conhecemos o nome de uma dada variável estrutura, podemos acessar seus campos usando seu nome acompanhado do operador ponto.

Para acessar os campos de uma estrutura através do ponteiro não teria sentido utilizar-se a construção "ptrl.preco", por exemplo, porque ptrl é um ponteiro e não um nome de estrutura.

Temos duas formas de acessar os campos:

1. Utilizando a construção:

  (*ptrl).preco

que indica "o campo preco da estrutura apontada por ptrl ".

Isto é equivalente a "livro[0].preco", se ptrl == & livro[0] !

ou

2. Utilizando o operador "->" (sinal de "menos" seguido pelo sinal "maior que"):

  ptrl -> preco 

Este é o método mais comum e tem o mesmo significado da primeira opção: ptrl é um ponteiro para estrutura, mas "ptrl -> preco" é uma variável double.

LISTAS ENCADEADAS

A lista encadeada ou "lista ligada" é uma estrutura de dados abstrata que pode ser criada em C, utilizando-se o mecanismo de ponteiros para estruturas.

Uma lista encadeada assemelha-se a uma corrente em que as estruturas estão penduradas sequencialmente. Isto é, a corrente é acessada através de um ponteiro para a primeira estrutura, chamado "cabeça", e cada estrutura contém um ponteiro para a sua sucessora.

O ponteiro da última estrutura tem valor NULL ("\0"), indicando o fim da lista.

Normalmente uma lista encadeada é criada dinamicamente na memória.

O diagrama abaixo ilustra a grosso modo como ficaria uma lista encadeada na memória:

ListaC.png


onde cada flecha é um ponteiro apontando para a próxima estrutura da lista.

Esta "flecha" é um campo da estrutura, criado como tipo ponteiro, que conterá o endereço da estrutura seguinte.

Exemplo:

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

void incluistruct(void);

struct Item{
   float preco;
   struct Item * Prox;
};

struct Item * Prim, *Atual, *Novo;
 
 
void main(void)
{
   char c;
   Prim = (struct Item *)NULL;
   while(c!='s'&& c!='S')
   {
       printf("\n Digite N para novo item ou S, para sair");
       c = getche();
       if(c=='n'|| c=='N')
           incluistruct();
       else{
           if(c=='s'||c=='S')
               printf("\n Saindo...");
           else
               printf("\nERRO! ");
       }
   }
   return 0;
}

void incluistruct()
{
   Novo = (struct Item *)malloc(sizeof(struct Item));
   if(Prim==(struct Item *)NULL)
       Prim=Atual=Novo;
   else
   {
       Atual=Prim;
       while(Atual->Prox != (struct Item *)NULL)
           Atual=Atual->Prox;
       Atual->Prox = Novo;
       Atual = Novo;
   }
   printf("\nDigite o preco: ");
   scanf("%f", &Atual->preco);
   Atual->Prox = (struct Item *)NULL;

}


Abaixo temos o exemplo "Livraria", utilizando-se listas ligadas, ao invés de matrizes de estruturas:

/*******************************************************************************/
/*** PROGRAMA LIVRARIA 2 - Demonstra como criar bancos de dados em C, utilizando */
/*** estruturas e listas ligadas */
/*******************************************************************************/
#include <stdlib.h> /* para a função atof(), que transforma uma string em float */
#include <stdio.h>
#include <conio.h>
#define TRUE 1
void novonome(void);
void listatot(void);
struct prs /* estrutura básica para listas ligadas */
{ char titulo[30];
 char autor[30];
 int regnum;
 double preco;
 struct prs *ptrprox;
};
struct prs *ptrprim, *ptratual, *ptrnovo; /* declara estruturas tipo prs para montar a lista encadeada */
void main (void)
{ char ch;

ptrprim = (struct prs *) NULL; /* sem dados ainda */
for(;ch != 's';)
{ printf("\nDigite: 'e' para adicionar um livro\n");
printf("\t'l ' para listar os livros\n");
printf("\t's' para sair:\n");
ch = getche();
printf("\n");
switch(ch)
{ case 'e' : novonome(); break;
  case 'l' : listatot(); break;
  case 's' : printf("Fim do programa"); break;
 default : puts("\nDigite somente opções válidas: ");
}
}
}

/***** FUNÇÃO NOVONOME - adiciona um novo livro ao arquivo ***/
void novonome(void)
 { char numstr[81];
   ptrnovo = (struct prs *) malloc (sizeof(struct prs)); /* novo ponteiro p/ estrut. da lista */
  /* reserva espaço na memória (malloc) para armazenar estrutura do tamanho (sizeof) de prs */
  if (ptrprim == (struct prs *) NULL) /* se o 1o. ponteiro da lista tem conteudo NULL */
     ptrprim = ptratual = ptrnovo; /* lista ainda não tem dados */
  else
 { ptratual = ptrprim;
   while(ptratual -> ptrprox != (struct prs *) NULL) /* procura novo item */
      ptratual = ptratual -> ptrprox;
   ptratual -> ptrprox = ptrnovo;
   ptratual = ptrnovo;
}
printf(" Digite titulo: ");
gets (ptratual -> titulo);
printf(" Digite autor: ");
gets(ptratual -> autor);
printf(" Digite o número do livro (3 digitos): ");
gets(numstr);
ptratual -> regnum = atoi(numstr); /* função atoi() converte uma string para inteiro */
printf(" Digite preço:");
gets(numstr);
ptratual -> preco = atof(numstr);
printf("\n_______________________________\n");
ptratual -> ptrprox = (struct prs *) NULL; /* último */
}
/***** FUNÇÃO LISTATOT - lista os dados de todos os livros *****/
void listatot(void)
{
  if(ptrprim == (struct prs *) NULL)
  { printf("\n\nLista vazia!!\n");
  return;
 }
ptratual = ptrprim;
do
{ printf("Titulo: %s. \n", ptratual -> titulo);
printf("Autor: %s.\n", ptratual -> autor);
printf("Numero do registro: %3d.", ptratual -> regnum);
printf("Preço: %4.2f. \n\n", ptratual -> preco);
ptratual = ptratual -> ptrprox;
} while(ptratual != (struct prs *) NULL);
printf("\n_______________________\n");
 }


A grande vantagem neste último programa "Livraria", que utiliza listas ligadas, é que a memória necessária para armazenar as estruturas é alocada à medida que a lista aumenta.

No primeiro programa "Livraria", foi necessário alocar 50 espaços de memória, cada um do tamanho de uma estrutura, o que representa um enorme desperdício se entramos com apenas 3 registros, por exemplo, ou que pode ser insuficiente (e tomar espaços indevidos na memória), se um desavisado resolve entrar com 100 registros!