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>; };
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:
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.
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 da inscricao de um aluno Entrada: nome do aluno, curso, identificador, nota e condiçao Saída: dados cadastrados e situaçao do aluno */ #include <stdio.h> #include <conio.h> #include <ctype.h> struct inscricao { char nome[30]; char curso[30]; int Id; // numero que indentifica o aluno float nota; char condicao; // A para aprovado e R para reprovado } estudante; // declaração da variável estudantes void alteraStruct (struct inscricao *p_estudante); // declara procedimento int main(void){ // Declarações // Instruções printf("Informe o nome do estudante: "); gets(estudante.nome); fflush(stdin); printf("Digite o nome do curso: "); gets(estudante.curso); printf("Informe a nota do aluno nesta disciplina: "); scanf("%f",&estudante.nota ); printf("Informe o número do Id: "); scanf("%d", &estudante.Id); alteraStruct(&estudante); // aciona o procedimento system("cls"); // Apresentação dos dados lidos printf("Dados Armazenados\n"); printf("\tNome..........:%s\n", estudante.nome); printf("\tCurso........:%s\n", estudante.curso); printf("\tId.............:%d\n", estudante.Id); printf("\tNota.............:%f.1\n", estudante.nota); printf("\tCondicao..............:"); estudante.condicao == 'A' ? puts("aprovado") : puts("reprovado"); getch(); return 0; } // Corpo do procedimento void alteraStruct (struct inscricao *p_estudante) { if ((*p_estudante).nota >= 5){ // acessa conteúdo do dado e verifica p_estudante->condicao = 'A'; // acessa endereço do dado }else{ p_estudante->condicao = 'R'; } }
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_estudante).nota acessa o endereço da struct e o conteúdo do seu componente, enquanto que a p_estudante->condicao chega ao endereço do componente para depois manipulá-lo. Sendo assim, o valor da nota foi lido pelo procedimento, enquanto que o valor da condicao foi inicializado, pois 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!!