ESTRUTURA DE DADOS COMPOSTA HETEROGÊNEA

 

Um uso bastante frequente na Linguagem C é a elaboração de estruturas de dados heterogêneas, mas vulgarmente conhecidas em outras linguagens de programação como registros. Para um melhor entendimento inicial da funcionalidade de uma estrutura observe a figura abaixo.

 

 

 

Essa ficha de inscrição apenas coleta alguns dados de um indivíduo qualquer e serve de exemplo do que vem a ser um registro ou estrutura de dados heterogênea.

 

Uma estrutura nada mais é do que uma coleção de algumas variáveis, possivelmente de tipos diferentes, que agrupam certas informações logicamente relacionadas em um único nome, ou seja, o identificador da estrutura de dados heterogêneos. Essas variáveis são os membros da estrutura e são conhecidas como elementos ou componentes da mesma. Acompanhe o exemplo descrito na linguagem de programação C para melhor compreender esta estrutura.

 

struct conta_bancaria
{
  char cliente[30];    // componente para o nome do cliente
  int agencia;         // componente para indicar a agê
ncia do cliente
  int conta;           // número da conta do cliente
  float valor;         // componente que identifica o valor do lançamento
  char tipo;     // tipo de operação bancaria, sendo D=débito e C=crédito
};

 

Observe no exemplo acima que os membros são relacionados, pois o nome do cliente está ligado às suas operações bancárias em sua conta e agência.

 

As estruturas heterogêneas constituem um recurso importante para a organização dos dados utilizados devido à possibilidade de tratar um grupo de valores como uma única variável, similar às estruturas homogêneas (vetor e matriz). Observe também que a declaração de conta_bancaria define uma estrutura que pode tornar-se um tipo de dado a ser utilizado em declarações de variáveis. É importante atentar que a declaração da estrutura acima não cria nenhuma variável de acesso a esta estrutura, ela apenas cria a estrutura de dado usando a palavra reservada struct.

 

Esta palavra informa ao compilador que um modelo de estrutura está sendo definido na memória do computador. Depois desta criação será possível declarar variáveis que a utilizem como um tipo da estrutura onde variáveis poderão armazenar valores respeitando suas características.

 

Forma Geral de Definição da struct

 

struct <identificador>
{
     <tipo dado>  <identificador>;
};
 
 
  • <identificador> define o nome da struct e de seus membros ou componentes
  • <tipo dado> define o tipo de dado de cada componente da struct
 

A declaração da variável que armazenará dados neste tipo de estrutura definida pela palavra reservada pode ser feita juntamente com a especificação da struct ou separadamente em qualquer lugar onde o escopo desta struct seja válido.

 

struct <identificador>
{
     <tipo dado>  ;
} <variável>;

 

ou após o ponto e vírgula da declaração da struct a inserção da instrução:

 

struct <identificador da struct já definida anteriormente> <variável>;

 

<variável> consiste na variável que acessará os membros da struct

 

Exemplos:

 

struct conta_bancaria
{
   char cliente[30];
   int agencia;    
   int conta;         
   float valor;       
   char tipo;        
}  lancamento; // lancamento consiste na variável da struct conta_bancaria
 

 

Para a segunda possibilidade de criação da variável de acesso a struct conta_bancaria a declaração consistiria somente na linha abaixo, porém antes já deveria ter sido especificada a struct sem nenhuma variável de acesso.

 

struct conta_bancaria lancamento; // só declara a variável lacamento

 

A declaração de uma struct pode ser feita com a especificação do nome da struct e da variável de acesso a mesma, ou somente com o nome da struct, ou somente com o nome da variável de acesso a struct, mas nunca com a ausência dos dois nomes.

 

Somente será alocado espaço na memória do computador, para armazenamento dos dados de uma struct, após a declaração de uma variável de acesso a essa struct. A quantidade de componentes em uma struct corresponde a quantidade de memória disponível no computador onde o programa será executado, sendo, normalmente, uma quantidade enorme, porém finita.

 

Os elementos que estão presentes em uma struct são referenciados através do operador ponto (.), respeitando a seguinte forma geral:

 
  • <nome da struct> . <nome do componente>
  • <nome da struct> nome da struct anteriormente definifa (declarada)
  • <nome do componente> nome do componente a ser acessado na struct
 

Observe o exemplo de manipulação de uma struct:

 

/* 

    SÍNTESE
   
           Objetivo:   cadastra os dados de um lançamento bancário
   
           Entrada:   nome do cliente, agê
ncia, conta, valor e operação
   
           Saída:      dados cadastrados para o lançamento desejado

*/

#include <stdio.h>
#include <conio.h>
#include <ctype.h>

struct banco
{
   char cliente[30];
   int agencia;
   int conta;
   float valor;
   char tipo;              // D para débito e C para crédito
};
 
int main(void)
{
   struct banco lancamento;     // declaração da variável lancamento
   printf("Informe o nome do cliente: ");
   gets(lancamento.cliente);   // lê nome do cliente em lancamento
   fflush(stdin);         // limpa o buffer entre uso de gets com scanf
   printf("Informe o número de sua agência: ");
   scanf("%d", &lancamento.agencia);  // lê a agência de lancamento
   printf("Digite o número de sua conta: ");
   scanf("%d", &lancamento.conta);
   printf("Informe o valor da operação: R$ ");
   scanf("%f", &lancamento.valor);
   printf("Digite D para operação de débito ou C para crédito. ");
   do{           // aceita somente os 2 valores válidos
      lancamento.tipo = toupper(getche());   // lê sempre maiúsculo
   } while (lancamento.tipo != 'D' && lancamento.tipo != 'C');
   clrscr();                // limpa a janela de tamanho padrão

// Apresentação dos dados lidos

   printf("Dados Armazenados\n);
   printf("\tCliente..........:%s\n", lancamento.cliente);
   printf("\tAgencia........:%d\n", lancamento.agencia);
   printf("\tConta............:%d\n", lancamento.conta);
   printf("Saldo.............:%4.2f\n", lancamento.saldo);
   printf("Tipo..............:");
   lancamento.tipo == 'D' ? puts("Débito") : puts("Crédito");
   getch();     // aguardar visualização dos resultados pelo usuário
   return 0;

}    // fim programa

 

A utilização de matrizes de estruturas é um recurso muito comum no registro de informações, pois a junção destes dois tipos de estrutura de dados (heterogênea e homogênea) permite ao computador armazenar e manipular, mais eficientemente, um conjunto de dados.

 

Para combinação destas estruturas de dados, torna-se, primeiramente necessária, a definição da struct (estrutura heterogênea) e posteriormente a declaração de uma variável que seja uma matriz (estrutura homogênea) dessa struct que corresponderá ao tipo de dados da matriz. Veja o exemplo a seguir:

 

/*
  
   SÍNTESE
   
          Objetivo:   registra os candidatos no vestibular

          Entrada:   nome, curso, ano atual e avaliação alcançada

          Saída:      confirma o registro do candidato

*/

#include <stdio.h>
#include <conio.h>

struct inscricao            // declara estrutura heterogênea
{
   char nome[30];
   char curso[20];
   int ano;
   float nota;
};

int main(void)
{
   int contador;
   struct inscricao vestibular[50];     // matriz de 50 inscricao
   for(contador = 0; contador < 50; contador++)
   {
      printf("Informe o nome do candidato: ");
      gets(vestibular[contador].nome);
      printf("Informe o curso desejado: ");
      gets(vestibular[contador].curso);
      fflush(stdin);
      printf("Digite o ano atual (exemplo 2003: ");
      scanf("%d", &vestibular[contador].ano);
      printf("Avaliação obtida: ");
      scanf("%f",&vestibular[contador].nota);
      fflush(stdin);     // limpa buffer de memória para repetição
      printf("\n\n\nCandidato %2d registrado.\n\n", contador);
   }
   getch();     // aguardar visualização dos resultados pelo usuário
   return 0;

}    // fim programa

 

Na manipulação de struct ainda é possível copiar todos os dados de uma variável de struct para outra variável desta mesma struct. Para isso é feita uma atribuição normal entre as variáveis, porém isso só é possível para variáveis que sejam de um mesmo tipo de struct. Observe o exemplo abaixo supondo a declaração das variáveis vestibular e estudante.

 
 
struct inscricao
{
   char nome[30];
   char curso[20];
   int ano;
   float nota;
} vestibular, estudante;
 
 

Note que esta declaração não corresponde a uma matriz de struct, mas declara duas variáveis diferentes com a mesma struct como seus tipos. Após a leitura de todos os componentes desta struct será possível copiá-los, todos com a simples atribuição abaixo, onde todos os dados de vestibular serão idênticos aos dados de estudantes.

 
  • // após a leitura de todos os dados de vestibular...
  • vestibular = estudante; // atribuição entre variáveis da mesma struct
 

A Linguagem C permite também a criação de ponteiros para struct, onde a declaração do ponteiro obedece o mesmo padrão de definição de ponteiros, ou seja, a inserção do operador de derreferenciamento (usando o *) a frente do nome da variável que acessará a struct.

 

struct inscricao *p_vestibular; // cria ponteiro p_vestibular para inscricao

 

O uso de ponteiro para struct funciona como um ponteiro convencional, pois acessa o endereço de memória onde a struct está armazenada. Mais precisamente, um ponteiro para struct, aponta para o primeiro endereço de armazenamento dos dados que compõem esta struct referenciada.

 

No entanto, a manipulação dos componentes de uma struct acessada por meio de ponteiros deverá acontecer através do operador seta (->), ao invés do operador ponto (.). Veja o exemplo mais elucidativo a seguir:

 

/* 

   SÍNTESE
   
           Objetivo:   cadastra os dados de um lançamento bancário
   
           Entrada:   nome cliente, agência, conta, valor e operação
   
           Saída:      dados cadastrados para o lançamento desejado

*/

#include <stdio.h>
#include <conio.h>
#include <ctype.h>

struct banco
{
   char cliente[30];
   int agencia;
   int conta;
   float valor;
   char tipo;              // D para débito e C para crédito
};
 
int main(void)
{
   // Declarações
   
   struct banco lancamento;     // declaração da variável lancamento
   void alteraStruct (struct banco *p_banco);   // declara procedimento
   
   // Instruções
   
   printf("Informe o nome do cliente: ");
   gets(lancamento.cliente);
   fflush(stdin);
   printf("Digite o número da agência: ");
   scanf("%d", &lancamento.agencia);
   printf("Informe o número da conta: ");
   scanf("%d", &lancamento.conta);
   alteraStruct(&lancamento);        // aciona o procedimento
   clrscr();
   
   // Apresentação dos dados lidos 
   
   printf("Dados Armazenados\n);
   printf("\tCliente..........:%s\n", lancamento.cliente);
   printf("\tAgencia........:%d\n", lancamento.agencia);
   printf("\tConta............:%d\n", lancamento.conta);
   printf("Saldo.............:%4.2f\n", lancamento.saldo);
   printf("Tipo..............:");
   lancamento.tipo == 'D' ? puts("Débito") : puts("Crédito");
   getch();
   return 0;

}

// Corpo do procedimento

void alteraStruct(struct banco *p_banco)
{
   (*p_banco).agencia = 0352;   // acessa conteúdo do dado
   p_banco->conta = 52709;      // acessa endereço do dado
   (*p_banco).valor = 1000;
   p_banco->tipo = 'C';
}

 

Observando este exemplo com cuidado é possível verificar o acesso ao endereço de memória que possui os dados armazenados na struct. Porém este acesso é feito de forma diferente, de acordo com operador usado em sua manipulação.

 

A manipulação (*p_banco).agencia acessa o endereço da struct e o conteúdo do seu componente, enquanto que a p_banco->conta = 52709 chega ao endereço do componente para depois manipulá-lo. Sendo assim, o valor da conta e agência foram alterados pelo procedimento, enquanto que o valor da operação e seu tipo foram inicializados, pois ambos ainda não possuíam conteúdo algum.

 

Atividade de Fixação

 

No intuito de fixar os novos conteúdos abordados por este módulo e verificar sua compreensão sobre os mesmos, são sugerido alguns exercícios de fixação para serem resolvidos. Clique no link de exercícios ao lado para iniciar a lista de exercícios referente a este conteúdo abordado. Bons estudos sobre os mesmos!!