AULA 13 - Programação 1 - Engenharia

De MediaWiki do Campus São José
Ir para navegação Ir para pesquisar

Objetivos

  • Definição de estruturas;
  • Estruturas como parâmetros;
  • Inicialização de estruturas;
  • Cópia de estruturas;
  • Exercícios com estruturas.

Estruturas

No C é possível criar tipos de dados que representam uma estrutura. Veja o exemplo seguinte

#include <stdio.h>

struct TUsuario /* struct TUsuario é o nome do tipo que está sendo criado */
{ 
  char userID[20];
  char senha[20];
  int  num_acessos;
} Usuario; /* aqui é definida uma variável do  tipo struct TUsuario */

/* aqui uma tabela/vetor de estruturas */
struct TUsuario TabelaUsuario[20];

main()
{
  /* acessando os campos da estrutura Usuario */
  scanf("%s", Usuario.userId);
  scanf("%s", Usuario.senha);
  Usuario.num_acessos=0;

  /* acessando o campo 10 da tabela de estruturas */
  scanf("%s", TabelaUsuario[10].userID);
  scanf("%s", TabelaUsuario[10].senha);
  
}

Neste exemplo, foi definido um tipo (modelo) para o registro (struct TUsuario) e foi criada uma variável chamada Usuario a partir deste tipo. Na sequência foi criada mais uma variável (um vetor de estruturas) chamada TabelaUsuario. Note que basta usar as palavras chave struct Usuario para criar novas variáveis. O tipo completo é definido uma única vez no início.

Exercícios

  1. Criar um programa que define uma struct para armazenamento do nome e das notas bimestrais de um aluno. Atualizar a estrutura usando o scanf.
#include <stdio.h>

#define NUM_MAX 3

struct TAluno {
  char nome[30];
  char matricula[11];
  float b1,b2,b3,b4;
} Turma[NUM_MAX];

void print_aluno(struct TAluno aux)
{
  printf("Nome -> %s\n", aux.nome);
  printf("Matrícula -> %s\n", aux.matricula);
  printf("Bimestre 1 -> %f\n", aux.b1);
  printf("Bimestre 2 -> %f\n", aux.b2);
  printf("Bimestre 3 -> %f\n", aux.b3);
  printf("Bimestre 4 -> %f\n", aux.b4);          
}

main()
{
  int i;
  
  for(i=0;i<NUM_MAX;i++) {
  	printf("Entre com o nome do aluno\n");
  	scanf("%s", Turma[i].nome);
  	printf("Entre com a matrícula do aluno\n");
  	scanf("%s", Turma[i].matricula);
  	printf("Entre com a nota do bimestre 1\n");
  	scanf("%f", &Turma[i].b1);
  	printf("Entre com a nota do bimestre 2\n");
  	scanf("%f", &Turma[i].b2);
  	printf("Entre com a nota do bimestre 3\n");
  	scanf("%f", &Turma[i].b3);
  	printf("Entre com a nota do bimestre 4\n");
  	scanf("%f", &Turma[i].b4);
  }
  for(i=0;i<NUM_MAX;i++) {
    printf("=========== Aluno %d ============\n", i);  
  	print_aluno(Turma[i]); 
  }      
}

Copiando structs

O exemplo a seguir demonstra como se pode copiar uma variável struct para outra do mesmo tipo.

#include <stdio.h>
  
struct THoras{
   int hora;
   int minuto;
   int segundo;
};

struct THoras Ontem = {2,10,57};

void main()
{
     struct THoras Hoje;
     Hoje = Ontem;

     printf("Hora hoje = %d, Minuto hoje = %d e Segundo hoje %d\n", Hoje.hora, Hoje.minuto, Hoje.segundo);
}

Estruturas dentro de estruturas

Vamos ver um exemplo com estruturas definidas dentro de estruturas:

#include <stdio.h>
  
struct TEndereco{
     char rua[50];
     char numero[10];
};

struct TCidadao{
  char nome[50];
  char cpf[20];
  struct TEndereco endereco;
  int num_filhos;
};

void main()
{
  struct TCidadao Cidadao;

  printf("Entre com o nome\n");
  scanf ("%s",Cidadao.nome);

  printf("Entre com o cpf\n");
  scanf ("%s",Cidadao.cpf);

  printf("Entre a rua\n");
  scanf ("%s",Cidadao.endereco.rua);

  printf("Entre a numero\n");   
  scanf ("%s",Cidadao.endereco.numero);

  printf("Entre com o número de filhos\n");
  scanf ("%d",&Cidadao.num_filhos);
  
}

Exercício: Faça um código adicional para imprimir o conteúdo lido na estrutura.

#include <stdio.h>
 
struct TEndereco{
     char rua[50];
     char numero[10];
};
 
struct TCidadao{
  char nome[50];
  char cpf[20];
  struct TEndereco endereco;
  int num_filhos;
};

void print_endereco(struct TEndereco aux)
{
  printf ("Rua %s Número %s\n", aux.rua, aux.numero); 
}

void print_cidadao(struct TCidadao aux)
{
  printf("Nome: %s CPF: %s ", aux.nome, aux.cpf);
  print_endereco(aux.endereco);
  printf("Numero de filhos %d\n", aux.num_filhos);
}

void main()
{
  struct TCidadao Cidadao;
 
  printf("Entre com o nome\n");
  scanf ("%s",Cidadao.nome);
 
  printf("Entre com o cpf\n");
  scanf ("%s",Cidadao.cpf);
 
  printf("Entre a rua\n");
  scanf ("%s",Cidadao.endereco.rua);
 
  printf("Entre a numero\n");   
  scanf ("%s",Cidadao.endereco.numero);
 
  printf("Entre com o número de filhos\n");
  scanf ("%d",&Cidadao.num_filhos);
 
}

Iniciando structs na definição

Como toda variável, é possível dar valores para uma variável do tipo struct definida no programa:

#include <stdio.h>
 
struct TEndereco{
     char rua[50];
     char numero[10];
};
 
struct TCidadao{
  char nome[50];
  char cpf[20];
  struct TEndereco endereco;
  int num_filhos;
};
 
void main()
{
  struct TCidadao Cidadao = {"Maria",
                             "42342342234",
                             {"Rua AlfaBeta","145"},
                             5;
                            };
 
  printf("Rua do cidadao = %s\n", Cidadao.endereco.rua);
 
}

Passando estruturas como parâmetro e retornando estruturas

Se não for usado o operador "&" , um parâmetro que é estrutura será passado por cópia. Não apresentaremos agora a passagem por endereço pois necessita do conceita de ponteiro. Observe o exercício abaixo.

#include <stdio.h>
  
struct TEndereco{
     char rua[50];
     char numero[10];
};

struct TCidadao{
  char nome[50];
  char cpf[20];
  struct TEndereco endereco;
  int num_filhos;
};

void print_struct (struct TCidadao aux)
{
  printf("nome=%s cpf=%s\n", aux.nome, aux.cpf);
  printf("endereço inicial do aux %p\n", &aux);
}

void main()
{
  struct TCidadao Cidadao;

  printf("Entre com o nome\n");
  scanf ("%s",Cidadao.nome);

  printf("Entre com o cpf\n");
  scanf ("%s",Cidadao.cpf);

  printf("Entre a rua\n");
  scanf ("%s",Cidadao.endereco.rua);

  printf("Entre a numero\n");   
  scanf ("%s",Cidadao.endereco.numero);

  printf("Entre com o número de filhos\n");
  scanf ("%d",&Cidadao.num_filhos);
  
  print_struct(Cidadao);

  printf("endereço inicial do Cidadao %p\n", &Cidadao);
}
O que podemos concluir com os endereços que foram mostrados??? Vamos a mais um exemplo.
#include <stdio.h>
  
struct TEndereco{
     char rua[50];
     char numero[10];
};

struct TCidadao{
  char nome[50];
  char cpf[20];
  struct TEndereco endereco;
  int num_filhos;
};

struct TCidadao ler_struct()
{
  struct TCidadao aux;

  printf("Entre com o nome\n");
  scanf ("%s",aux.nome);

  printf("Entre com o cpf\n");
  scanf ("%s",aux.cpf);

  printf("Entre a rua\n");
  scanf ("%s",aux.endereco.rua);

  printf("Entre a numero\n");   
  scanf ("%s",aux.endereco.numero);

  printf("Entre com o número de filhos\n");
  scanf ("%d",&aux.num_filhos);

  return aux;
}

void print_struct (struct TCidadao aux)
{
  printf("nome=%s cpf=%s\n", aux.nome, aux.cpf);
  printf("endereço inicial do aux %p\n", &aux);
}

void main()
{
  struct TCidadao Cidadao;

  Cidadao = ler_struct();

  print_struct(Cidadao);

  printf("endereço inicial do Cidadao %p\n", &Cidadao);
}

Aplicação de estruturas - Representação de números complexos

Um número complexo possui dois componentes reais que podem ser representados por duas variáveis float ou double. Observe que a declaração separada destes componentes prejudica a legibilidade do código e a construção de algoritmos eficientes para o tratamento destes números. Na sequência apresentamos exercícios que representam números complexos em estruturas.

  1. Implementar uma estrutura que permita representar um número complexo no formato retangular. Em adição, implemente uma função que permita somar dois números complexos retornando um número complexo com o valor da soma.
    #include <stdio.h>
    
    struct tcomplexo {
      float x;  /* parte real */
      float y;  /* parte imaginária */
    };
    
    struct tcomplexo add_complex(struct tcomplexo num_A, struct tcomplexo num_B)
    {
      struct tcomplexo temp;
    
      temp.x = num_A.x + num_B.x;
      temp.y = num_A.y + num_B.y;
      return temp;
    }
    
    main()
    {
      struct tcomplexo num_1, num_2, num_3;
    
      num_1.x = 1.5;
      num_1.y = 2.5;
      num_2.x = 3.5;
      num_2.y = 1.5;  
      num_3 = add_complex(num_1, num_2);
      /* implementar aqui m printf para mostrar o valor de num_3...*/
    }
    
  2. Implementar uma função que retorne o módulo de um número complexo.
    solução
    #include <stdio.h>
    #include <math.h>
     
    struct tcomplexo {
      float x;
      float y;
    };
    
    float calc_mod_complex(struct tcomplexo num_A)
    {
      float temp;
    
      temp = sqrtf(num_A.x*num_A.x + num_A.y * num_A.y);
      return temp;
    }
    
    main()
    {
      struct tcomplexo num_1;
      float result_mod;
    
      num_1.x = 1.5;
      num_1.y = 2.5; 
     
      result_mod = calc_mod_complex(num_1);  
    }
    {{collapse bottom}}
    
  3. Implementar uma estrutura que permita representar uma matriz 2X2 de números complexos no formato retangular. Adicionalmente, implementar uma função para somar estas matrizes. Reusar a função de soma já implementada.
  4. Implementar uma função converte_para_polar que recebe como parâmetro um número complexo na forma retangular (uma struct). A função deve retornar uma struct contendo o número complexo na forma polar.Usar as funções sqrt[1] e atan [2] da biblioteca matemática. Como converter:
    ou
    #include <math.h>
    
    struct TComplexoRet {
      float x,y;
    };
    
    struct TComplexoPolar {
      float mod,ang;
    };
    
    struct TComplexoPolar convert_polar(struct TComplexoRet a)
    {
      struct TComplexoPolar aux;
      
      aux.mod = sqrtf(powf(a.x,2)+powf(a.y,2));
      aux.ang = atan(a.y/a.x);  
      return aux;
    }
    
    main()
    {
     struct TComplexoRet w;
     struct TComplexoPolar y;
     
     w.x = 11.5;
     w.y = 4.6;
     y = convert_polar(w); 
    }
    
  5. Implementar uma função que retorne a multiplicação de dois números complexos fornecidos como parâmetro.
  6. Aplicação no Controle de Acesso

    O exemplo a seguir implementa uma parte do programa de controle de acesso usando estruturas. Neste exemplo a tabela de usuários já vem inicializada nos campos UserID e Senha. Neste exemplo usamos uma função strcmp da BIBLIOTECA DO C para comparação de strings. Note que deve ser incluído string.h

    #include <stdio.h>
    #include <string.h>
     
    /**********************************************/
    /*** PROGRAMA DE CONTROLE DE ACESSO  **/
    /** Autor: TurmaENG.TELECOM - 2012.2 */
    /**********************************************/
     
    /** VARIÁVEIS GLOBAIS DESTE MÓDULO ****/
    
    struct TRegistroUsuario {
    	char UserId[10];
    	char Senha[10];
    };
    
    /* Tabela de Usuários */
    struct TRegistroUsuario TabelaUsuarios[4] = {
             {"joao","abcd"},
             {"maria","xxxx"},
             {"jose","yyyy"},
             {"lara","zzzz"},
    };
    
     
    char userID[20];
     
     
    /** FUNÇÔES DESTE MÓDULO ****/
     
    void mostrar_menu_entrada_usuario()
    {
      printf("*******************************\n");
      printf("Entre com o seu USERID para ter acesso\n"); 
      printf("*******************************\n");
    }
     
    /** Função que implementa as tarefas do administrador **/
     
    void  administrar()
    {
    }
     
    /** Função que valida um usuário e abre a porta **/
     
    void tratar_usuario()
    {
      char senha[10];
      int userEncontrado=1;
      int i;
     
     
     /* 
         Loop para encontrar o usuário na tabela. 
         Ao final do loop a variavel i conterá o índice do usuário (se ele estiver
         na tabela
      */
      for (i=0;i<4 && userEncontrado; i++) {
           if( strcmp(userID, TabelaUsuarios[i].UserId)==0)
                 userEncontrado=0;
      }
     
      /* se usuário encontrado abre a porta */
      if (userEncontrado==0) {
    	printf("Bom dia %s! Entre com a senha\n", userID);
      	scanf("%s",senha);
    	i--; /* o indice do sujeito é i-1 */
    	if(strcmp(senha,TabelaUsuarios[i].Senha)==0) 
    	       printf("Abrir porta!!!\n");
    	else
    		printf("Senha Inválida\n");
      }
    }
     
    void main()
    {
     for(;;) {
         mostrar_menu_entrada_usuario();
         scanf("%s",userID);
         if (strcmp(userID, "admin")==0) {
              administrar();
         } else {
             tratar_usuario();
         }
     }   
    }
    

    Exercício:

    1. Implementar um contador de acesso que permita bloquear o usuário após 3 tentativas seguidas. Note que caso o usuário acerte a senha, este contador deverá ser zerado.
    2. Implementar uma funcionalidade do administrador para desbloquear o usuário bloqueado.
    solução
    #include <stdio.h>
    #include <string.h>
     
    /**********************************************/
    /*** PROGRAMA DE CONTROLE DE ACESSO  **/
    /** Autor: Turma ENG.TELECOM - 2013.1 */
    /**********************************************/
     
    /** VARIÁVEIS GLOBAIS DESTE MÓDULO ****/
     
    struct TRegistroUsuario {
    	char UserId[10];
    	char Senha[10];
    	int contador;
    };
     
    /* Tabela de Usuários */
    struct TRegistroUsuario TabelaUsuarios[4] = {
             {"joao","abcd",0},
             {"maria","xxxx",0},
             {"jose","yyyy",0},
             {"lara","zzzz",0},
    };
     
     
    char userID[20];
     
     
    /** FUNÇÔES DESTE MÓDULO ****/
     
    void mostrar_menu_entrada_usuario()
    {
      printf("*******************************\n");
      printf("Entre com o seu USERID para ter acesso\n"); 
      printf("*******************************\n");
    }
    
    /** Função que implementa as tarefas do administrador **/
     
    void  administrar()
    {
      char aux_senha[10];
      int userEncontrado=1;
      int i;
        
      printf("Entre com a senha do admin \n");
      scanf ("%s", aux_senha);
      
      if(strcmp(aux_senha,"123456")==0) {
          /* senha valida do admin - agora entre com userid a ser desbloqueado */
          printf("Entre com userdID a ser desbloqueado\n");
          scanf("%s",userID);
          for (i=0;i<4 && userEncontrado; i++) {
             if( strcmp(userID, TabelaUsuarios[i].UserId)==0)
                 userEncontrado=0;
          } 
          if (userEncontrado==0) {
             i--;
             TabelaUsuarios[i].contador=0;
          }           
      }
    }
     
    /** Função que valida um usuário e abre a porta **/
     
    void tratar_usuario()
    {
      char senha[10];
      int userEncontrado=1;
      int i;
     
     
     /* 
         Loop para encontrar o usuário na tabela. 
         Ao final do loop a variavel i conterá o índice do usuário (se ele estiver
         na tabela
      */
      for (i=0;i<4 && userEncontrado; i++) {
           if( strcmp(userID, TabelaUsuarios[i].UserId)==0)
                 userEncontrado=0;
      }
     
      /* se usuário encontrado abre a porta */
      if (userEncontrado==0) {
      	 i--; /* o indice do sujeito é i-1 */
        if (TabelaUsuarios[i].contador<3){
        
    	   printf("Bom dia %s! Entre com a senha\n", userID);
      	   scanf("%s",senha);
    
    	   if(strcmp(senha,TabelaUsuarios[i].Senha)==0) {
    	       printf("Abrir porta!!!\n");
    	       TabelaUsuarios[i].contador=0;
    	   }
    	   else {
    		   TabelaUsuarios[i].contador++;
    		   printf("Senha Inválida\n");
    		   printf("Tentativas restantes %d\n", 3-TabelaUsuarios[i].contador);
    		}
        } else {
          printf("Usuário bloqueado\n");
        }
      } else {
        printf("Usuário não encontrado\n");
      }
    }
     
    void main()
    {
     for(;;) {
         mostrar_menu_entrada_usuario();
         scanf("%s",userID);
         if (strcmp(userID, "admin")==0) {
              administrar();
         } else {
             tratar_usuario();
         }
     }   
    }
    
    1. No programa de controle de senha inserir um campo na estrutura do usuário de forma a acomodar uma mensagem de boas vindas particularizada para cada usuário. A mensagem "DEFAULT" é Bom dia!
    2. Implementar na função administrar a inserção da mensagem no exercício anterior.
    3. Na solução acima criar uma função que procura usuário na tabela (já que este código é utilizado em mais do que um luga). A função deve receber o UserID a ser procurado e deve retornar um inteiro correspondente ao índice do usuário encontrado ou -1 se não for encontrado.

    Referências

    https://fresh2refresh.com/c-programming/c-structure-padding/

    https://www.geeksforgeeks.org/memory-layout-of-c-program/

    https://docs.cs50.net/2017/fall/notes/5/lecture5.html#data-structures

    http://www.mathwarehouse.com/programming/passing-by-value-vs-by-reference-visual-explanation.php