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 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!!