VOLPITI - Tecnologia da Informação
 
Busca
Tópicos
  Cadastre-se :: Hospedagem LinuxDicas :: Acessórios LinuxDicas :: Fórum LinuxDicas   

Usuários LinuxDicas
· AvantGO
· Backend XML (RSS)
· Contato/Publicidade
· Enviar Notícias/Artigos
· Fórum LinuxDicas
· Lista de Discussão
· Mensagens Privadas

Casa do Linux


Lista de Discussão
·[linuxdicas] quetão de linux
·Ferramenta de Backup
·redes
·Search Engine

Leia mais...

Artigos LinuxDicas
· Resistência ao Linux
· Guia para o Novato
· Licenciamento do SuSE
· Foca GNU/Linux - Iniciante
· Foca GNU/Linux - Intermediário
· Foca GNU/Linux - Avançadoe
· Curso C
· Outras Seções de Artigos

FAQ LinuxDicas
· Licença
· Servidores
· Ambiente X
· Hardware
· Instalação
· Internet
· Aplicativos
· Sistema
· StarOffice

Notícias Velhas
Segunda, abril 21
· MON - Monitorando a disponibilidade de serviços
Quinta, abril 17
· Últimos dias para Inscrições de Palestras no Flisol Campinas
· Acessando seus arquivos de sua máquina de qualquer computador do mundo
Terça, abril 15
· Artigo sobre o Moregroupware
Segunda, abril 14
· Análise do Mandriva Xtreme2 Pack
Terça, março 18
· Como migrar dados do msaccess para mysql
· A verdadeira razão para usarmos Linux
· usando o rsync 3.0.0 para fazer backup entre linux e xp
· Firefox3 : Como Instalar manualmente.
Sábado, fevereiro 09
· Configurando um DNS CHROOTED Primário e Reverso para o seu site.
Quinta, dezembro 27
· Censurando a internet com o OpenDNS
Domingo, outubro 14
· Bandwidth Monitoring Tools For Linux
Quinta, outubro 11
· Variáveis de ambiente
Quinta, agosto 02
· Rodando processos em múltiplos servidores, Cluster!!!
· Instalando XEN no seu Ubuntu/Debian
Domingo, junho 17
· Vovó, faz backup pra mim?
Quarta, junho 13
· Canetas: Idealismo Antártico
Quinta, junho 07
· CA confiável com certificados digitais
Domingo, junho 03
· Equações Matemáticas via Shell
Sábado, junho 02
· Permanent Link to Garimpar é preciso [2]: Confira seu e-mail

Notícias antigas

Versões Estáveis
· Kernel Linux
· *nix e *BSD ISOs da Unicamp
· Linux ISO
· FreeBSD Releases

Curso de C - 10. Estruturas, Uniões, Enumerações e Definição de Tipois

(2547 total de palavras neste texto)
(3791 vizualização(ões))   Imprimir




Curso de C - 10. Estruturas, Uniões, Enumerações e Definição de Tipos

Como já vimos, as matrizes nos trazem um meio de organizar coleções de dados do mesmo tipo, que poupa o trabalho do programador de ter que declarar muitas variáveis. Por outro lado, o que podemos fazer quando queremos organizar uma coleção de dados de tipos diferentes?? Neste caso, as matrizes não podem nos ajudar. Para esse fim existem as estruturas e as uniões, que agrupam dados de tipos diferentes e nos fornecem um meio bastante prático de acessar estes dados.

As enumerações são uma forma de nomear coleções de constantes de modo a facilitar e tornar mais intuitivo o seu uso.

Aprenderemos também nesta aula como criar tipos de dados. Have fun!!

10.1. Estruturas

Uma estrutura em C é similar aos registros em Pascal: é um agrupamento de dados de tipos diferentes, referenciados por um nome, o que facilita bastante o acesso e a organização. Sua forma geral é a seguinte:

  struct nome
  {
  	tipo1 nome1;
  	tipo2 nome2;
  	   ...
  	tipoN nomeN;
  };

Geralmente, os dados agrupados em uma estrutura estão relacionados. Como exemplo podemos citar os dados do endereço de uma pessoa, onde temos o nome da rua (uma string), o número da casa (um inteiro), o complemento (uma string), o CEP (um inteiro longo), o nome do bairro (uma string), o nome da cidade (uma string) e a sigla do estado (uma string de 2 caracteres). Vamos declarar esta estrutura então, para ver como fica:

  struct endereco
  {
  	char rua[30];
  	int numero;
  	char complem[30];
  	long int cep;
  	char bairro[15];
  	char cidade[15];
  	char estado[3];
  };

Quando declaramos uma estrutura como acima, ainda não declaramos nenhuma variável. Estamos apenas especificando o tipo, ou a estrutura da estrutura... Para declarar uma variável da estrutura struct endereco, fazemos assim:

  struct endereco dados;

Neste exemplo, declaramos a variável dados como sendo do tipo struct endereco. Outra forma de declarar uma variável de uma estrutura é colocar o nome da variável após a segunda chave da estrutura, antes do ponto-e-vírgula:

  struct endereco
  {
  	char rua[30];
  	int numero;
  	char complem[30];
  	long int cep;
  	char bairro[15];
  	char cidade[15];
  	char estado[3];
  } dados;

Aqui também declaramos a variável dados do tipo struct endereco. Se você precisar de apenas uma variável da estrutura em seu programa, não é necessário dar nome a estrutura:

  struct		/* sem nome... */
  {
  	char rua[30];
  	int numero;
  	char complem[30];
  	long int cep;
  	char bairro[15];
  	char cidade[15];
  	char estado[3];
  } dados;

Para acessar as variáveis internas à estrutura, usamos o ponto entre o nome da variável e o nome do elemento. Exemplo:

  #include <stdio.h>
  
  /* definimos a forma da estrutura */
  
  struct endereco
  {
  	char rua[30];
  	int numero;
  	char complem[30];
  	long int cep;
  	char bairro[15];
  	char cidade[15];
  	char estado[3];
  };
  
  main()
  {
  	struct endereco dados;		/* declaração */
  	
  	/* Lendo ... */
  	
  	printf("Rua: ");
  	fgets(dados.rua, 29, stdin);
  	
  	printf("Número: ");
  	scanf("%d", &dados.numero); getchar();
  	
  	printf("Complemento (se não houver, digite '-'): ");
  	fgets(dados.complem, 29, stdin);
  	
  	printf("CEP: ");
  	scanf("%ld", &dados.cep); getchar();
  	
  	printf("Bairro: ");
  	fgets(dados.bairro, 14, stdin);
  	
  	printf("Cidade: ");
  	fgets(dados.cidade, 14, stdin);
  	
  	printf("Estado (sigla): ");
  	fgets(dados.estado, 3, stdin);
  	
  	/* Agora, imprimindo... */
  	
  	printf("-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
  	printf("RUA: %s", dados.rua);
  	printf("NÚMERO: %d\n", dados.numero);
  	printf("COMPLEMENTO: %s", dados.complem);
  	printf("BAIRRO: %s", dados.bairro);
  	printf("CEP: %ld\n", dados.cep);
  	printf("CIDADE: %s", dados.cidade);
  	printf("ESTADO: %s\n", dados.estado);
  	printf("-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
  	
  	return 0;
  }

Como você pode ver, para acessar a variável (ou campo) numero, interna à estrutura dados, basta usar a notação dados.numero. Para ler com scanf, basta colocar o & antes de dados.numero. As strings também são tratadas como se estivessem fora da estrutura. Para ler o nome da rua, basta fazer fgets(dados.rua, 29, stdin).

Inicialização de Estruturas

Uma estrutura só pode ser inicializada se for de classe extern ou static, e isto depende de onde a variável estrutura foi declarada. Da mesma forma que inicializamos matrizes, podemos inicializar uma estrutura:

  struct produto
  {
  	char nome[20];
  	float preco;
  };
  
  struct produto item= { "Drive CD-ROM 52X", 175.00 };

Os elementos devem aparecer na mesma ordem em que foram definidos na estrutura.

Atribuição de Estruturas

O padrão ANSI C permite que você atribua uma estrutura à outra, sem a necessidade de fazer isso campo por campo. Vamos criar a estrutura produto:

  struct produto
  {
  	char nome[20];
  	float preco;
  };

Agora, declaramos duas variáveis: p1 e p2.

  struct produto p1, p2;

O trecho de código a seguir é válido:

  strcpy(p1.nome, "mouse");	/* atribui "mouse" a string p1.nome */
  p1.preco= 30.55;
  
  p2= p1;		/* aqui os campos de p2 recebem os valores dos campos de p1 */
  
  printf("%s = %.2f", p2.nome, p2.preco);

Será impresso: Mouse = 30.55.

Matrizes de Estruturas

Como uma estrutura não deixa de ser um tipo, você pode criar uma matriz de estruturas facilmente, utilizando a mesma notação para declaração de matrizes de outros tipos. Vamos usar como exemplo a estrutura struct produto. Criaremos uma matriz de 10 elementos dessa estrutura:

  #include <stdio.h>
  #include <string.h>
  
  /* definição da "forma" da estrutura */
  struct produto
  {
  	char nome[20];
  	float preco;
  };	
   
  main()
  {
  	struct produto itens[10];	/* uma matriz de estruturas */
  	int i;
  	
  	for (i=0; i<10; i++)
  	{
  		printf("Nome do produto: ");
  		fgets(itens[i].nome, 19, stdin);
  		printf("Preço do produto: ");
  		/* esse %*c limpa o buffer (o mesmo que getchar()) */
  		scanf("%f%*c", &itens[i].preco);
  	}
  	for (i=0; i<10; i++)
  	{
  		printf("Nome: %s", itens[i].nome);
  		printf("Preço: %.2f\n\n", itens[i].preco);
  	}
  	return 0;
  }

Para acessar os campos de uma matriz de estruturas, basta colocar o nome da matriz, seguido do índice, ponto e nome do campo. Assim:

  itens[0].preco	

Você estará acessando o campo preco do primeiro elemento da matriz de estruturas.

Ponteiros para Estruturas

Os ponteiros e as estruturas fazem o casamento perfeito para a criação de estruturas de dados mais avançadas, como listas ligadas, pilhas e filas. Os dois quase sempre aparecem juntos em muitos problemas de programação. Vamos ver nas próximas aulas que passar um ponteiro de uma estrutura para uma função pode aumentar drasticamente a performance do programa, pois apenas o endereço é passado. Esta é apenas uma vantagem dos ponteiros para estruturas.

Declarar um ponteiro para estrutura é como declarar um ponteiro para qualquer tipo de dado. Considerando a estrutura struct produto:

  struct produto *ptr_dados;

Declaramos o ponteiro ptr_dados, que aponta para estruturas do tipo struct produto.

Veja o programa a seguir, que ilustra a utilização de ponteiros para estruturas:

  #include <stdio.h>
  
  struct produto
  {
  	char nome[20];
  	float preco;
  };	
  
  main()
  {
  	struct produto dados;	/* declara variável */
  	struct produto *ptr;	/* declara ponteiro */
  	
  	strcpy(dados.nome, "Mouse");
  	dados.preco= 35.00;
  	
  	ptr= &dados;	/* o ponteiro ptr recebe o endereço da estrutura dados */
  	
  	printf("%s = %.2f", ptr->nome, ptr->preco);
  	return 0;
  }

Notou algo diferente?? Porque não usamos o ponto ao imprimir os campos nome e preco, apontados por ptr?

Quando vamos referenciar os campos de uma estrutura através de um ponteiro, não podemos usar o ponto entre o nome da variável e o campo, e sim o operador ->. Usar essa notação é semelhante a usar o asterisco antes de um ponteiro para inteiro, por exemplo, onde é referenciado o conteúdo da variável apontada, não o endereço. Aqui, ptr->preco é 35.00 e ptr->nome é "Mouse".

Estruturas dentro de estruturas

Os campos de uma estrutura podem perfeitamente ser uma outra estrutura, assim como também matrizes, strings e ponteiros. Por exemplo:

  struct address
  {
  	char rua[20];
  	int num;
  	char bairro[15];
  	long int cep;
  	char cidade[20];
  	char estado[4];
  };
  
  struct aluno
  {
  	long int ra;
  	char curso[15];
  	struct address ender;	/* estrutura aninhada */
  };

A estrutura struct aluno possui o campo ender, que também é uma estrutura, a struct address. Aí você me pergunta: como fazer para acessar um campo da estrutura struct address que está dentro de struct aluno? Basta fazer assim:

  struct aluno a1; 	/* declarar variável */
  
  a1.ender.num= 1200;
  a1.ender.cep= 13098756;

É só ir usando os pontos para acessar os campos. As estruturas podem ser aninhadas até 15 vezes, segundo o padrão ANSI C. A maioria dos compiladores, porém, permite mais que isso.

10.2. Campos de Bits

Além dos operadores bit-a-bit, C oferece uma outra forma de acessar e manipular os bits de um byte: os Campos de Bits. Esse tipo especial de estrutura é bastante usado em programas que manipulam algum tipo de hardware.

Os campos de bits devem ser declarados como int, unsigned ou signed, porém alguns compiladores só admitem campos de bits unsigned. Campos de bits de comprimento 1 devem ser obrigatoriamente unsigned (não podem ter sinal).

A forma geral da definição de um campo de bits é a seguinte:

  struct nome
  {
  	tipo nome1: comprimento;
  	tipo nome2: comprimento;
  	tipo nome3: comprimento;
  	 ...
  	tipo nomeN: comprimento;
  };

Para você entender, vamos mostrar um exemplo bem bobo, onde criamos um campo de bits para um byte:

  #include <stdio.h>
  
  struct campo	/* o campo de bits */
  {
  	unsigned int a: 1;	/* comprimento 1, só pode ser 0 ou 1 (1 bit) */
  	unsigned int b: 1;	
  	unsigned int c: 1;	
  	unsigned int d: 1;	
  	unsigned int e: 1;	
  	unsigned int f: 2;	/* comprimento 2, pode ter valor 0, 1, 2 ou 3 (2 bits) */
  	unsigned int g: 1;
  } byte;		
  
  main()
  {	
  	
  	/* vá alterando os valores dos campos e 
  	   veja o resultado da impressão. */ 
  	byte.a= 1;
  	byte.b= 0;
  	byte.c= 0;
  	byte.d= 0;
  	byte.e= 0;
  	byte.f= 3;	/* 3 em binário é 11 (2 bits) */
  	byte.g= 1;
  	
  	/* como um caractere tem 1 byte, imprimimos como caractere */
  	printf("%c\n", byte);	
  	
  	return 0;
  }	  

As variáveis de campo de bits possuem algumas restrições:

  • você não pode obter o endereço delas;
  • dependência da máquina, por causa da ordem dos campos (da esquerda para direita ou da direita para esquerda);
  • não podem ser organizadas em matrizes;

10.3. Uniões

As uniões são bastante parecidas com as estruturas, na declaração, na forma de acessar os campos, etc. A única e crucial coisa que difere uma união de uma estrutura é que o espaço de memória alocado por uma variável união é compartilhado por seus campos. Isto quer dizer que uma variável união (ou union) ocupa o espaço do tamanho do seu maior campo.

A forma geral de uma union é a seguinte:

  union nome
  {
  	tipo nome1;
  	tipo nome2;
  	...
  	tipo nomeN;
  };

Suponhamos a seguinte union:

  union teste
  {
  	int num;
  	float fp;
  	double dfp;
  	char ch;
  } info;

Pergunto: qual o espaço ocupado pela variável info na memória? Acertou quem respondeu 8 bytes, ou 64 bits, ou o tamanho de um double (o seu maior elemento).

Uma estrutura com os mesmos campos ocuparia 17 bytes (considerando int de 4 bytes).

Um exemplo do uso de uniões é gravar um inteiro byte por byte em um arquivo. Considerando um inteiro de 4 bytes:

  union teste
  {
  	int num;
  	char ch[4];
  } info;

Agora para gravar o inteiro, byte por byte:

  FILE *fp;
  
  /* suponhamos que o arquivo já esteja aberto */
  putc(info.ch[0], fp);
  putc(info.ch[1], fp);
  putc(info.ch[2], fp);
  putc(info.ch[3], fp);

A função putc grava um caractere no arquivo cujo descritor é fp. Como um caractere tem 1 byte, gravamos o inteiro todo, byte por byte. Este é um exemplo do uso de uniões.

10.4. Enumerações

Uma enumeração é uma coleção de constantes inteiras, referenciadas por um nome, que define todos os valores possíveis que a variável enumeração pode ter. A forma geral de uma enumeração é:

  enum nome { constante1, constante2, ..., constanteN };

Para declarar uma variável enum:

  enum nome variavel;

Por exemplo:

  enum patente { soldado, cabo, sargento, tenente };	/* definimos a forma */
  
  enum patente militar;		/* só aqui houve a declaração da variável */

Em uma enumeração, o valor do primeiro símbolo (soldado) é 0, o segundo símbolo (cabo) é 1, o terceiro símbolo (sargento) é 2 e assim por diante. Cada símbolo nada mais é do que um inteiro com um apelido (não confundir com strings). A variável militar é uma variável inteira e pode ter somente os valores 0, 1, 2 ou 3.

Exemplo:

  enum patente { soldado, cabo, sargento, tenente };
  
  enum patente militar;
  
  printf("0 - Soldado\n");
  printf("1 - Cabo\n");
  printf("2 - Sargento\n");
  printf("3 - Tenente\n");
  printf("Digite a escolha: ");
  scanf("%d", &militar);
  
  switch(militar)
  {
  	case soldado:
  		printf("Soldado!");
  		break;
  	case cabo:
  		printf("Cabo!");
  		break;
  	case sargento:
  		printf("Sargento!");
  		break;
  	case tenente:
  		printf("Tenente!");
  		break;
  	default:
  		printf("Digitou errado!");
  }

Como você pode ver, enumerações podem tornar o código mais legível e organizado. Sempre que possível é bom utilizá-las.

Você também pode especificar um valor para as constantes de uma enumeração:

  enum patente { soldado, cabo, sargento=50, tenente };

Os elementos terão os seguintes valores:

soldado 0
cabo 1
sargento 50
tenente 51

10.5. Definição de Tipos

Em C nós podemos criar novos tipos ou dar nomes diferentes aos tipos existentes. Basta usar a palavra-chave typedef. A forma geral é:

  typedef tipo nome_novo;

Exemplos:

  typedef char byte;	/* criamos o tipo byte (simplesmente um char) */

Para declarar uma variável:

  byte ch;

Com as estruturas, uniões e enumerações não é diferente. Veja:

  typedef struct produto Prod;	/* Criamos o tipo Prod */
  Prod item;		/* a declaração fica assim, bem mais enxuta */
  
  
  typedef enum {verdade, falso} bool;
  bool valor;

Com as matrizes:

  typedef int mat3x3[3][3];
  
  mat3x3 teste;	/* declara a variável teste (matriz 3 por 3) */
  
  
  typedef float M[10];
  
  M mat;		/* declara a variável mat (matriz unidimensional de 10 elementos) */

Bem... De nada adianta fazer todos esses malabarismos com dados e depois perdermos tudo após a execução do programa. Por isso que na próxima aula vamos aprender a manipular arquivos em C.

Até lá!!


Exercício

1) Faça uma agenda que guarde os seguintes dados: nome, telefone e e-mail. Esta agenda deve ter capacidade para 50 entradas, e você deverá fornecer as seguintes opções:

  • adicionar uma nova entrada;
  • buscar e modificar uma existente;
  • excluir uma entrada.
O programa deverá avisar o usuário quando a agenda estiver cheia, não permitindo novas entradas neste caso. Se o usuário tentar adicionar um telefone ou e-mail já existente, o programa deverá avisar que o registro já consta na agenda.


Powered by txt2tags

  

[ Voltar Curso C | Índice de Seções ]


Copyright © 2002 - 2007 LinuxDicas - Todos Os Direitos Reservados.
LinuxDicas: lucas.martinez @linuxdicas.com.br
Web site engine's code is Copyright © 2003 by PHP-Nuke. All Rights Reserved. PHP-Nuke is Free Software released under the GNU/GPL license.
Tempo para gerar esta página: 0.046 segundos.