Gabarito da Lista 7


Módulo 7:  Aula 8: Diretivas de compilação
                   Aula 9: Entradas e saídas padronizadas
                  

Exercício 1: página c830.html

Enunciado:
Escreva uma macro que retorne 1 se o seu argumento for um número ímpar e 0 se for um número par.

Solução:
#define EPAR(x) ((x)d%2) ? 1 : 0

Comentários: repare o uso dos parênteses, que permite que expressões do tipo EPAR(a+b) sejam possíveis.

 

Exercício 2: página c930.html


Enunciado:
Escreva um programa que leia nomes pelo teclado e os imprima na tela. Use as funções puts e gets para a leitura e impressão na tela.

Solução:
#include <stdio.h>
void main()
{
    char nome[30];
    printf("\n\nEscreva um Nome: ");
    gets(nome);
    puts("\nO nome escrito foi:");
    puts(nome);
}

Exercício 3: Página c940.html

Enunciado:
Escreva um programa que leia (via teclado) e apresente na tela uma matriz 3X3 de inteiros. Utilize os novos códigos de formato aprendidos para que a matriz se apresente corretamente identada. Altere os tipos de dados da matriz (int, float, double) e verifique a formatação correta para a identação. Verifique também a leitura e impressão de números hexadecimais.

Solução 1: Números inteiros
/* Versao 1.0: Inteiros
   Leitura e Impressao de Hexadecimais
   Identacao a esquerda.              */
#include <stdio.h>
void main()
{
    int mat[3][3];
    int i,j;
    /* Leitura da Matriz: */
    printf("\n\nEntre com os valores para a matriz (em hexadecimal):");
    for (i=0; i<3; i++)
    {
      printf("\nLinha %d: ",i+1);
      for (j=0; j<3; j++)
        scanf("%X", &mat[i][j]);
    }
    /* Impressao: */
    printf("\n");
    for (i=0; i<3; i++)
    {
      printf("\n|");
      for (j=0; j<3; j++)
        printf("%-6X|", mat[i][j]);
    }
}
 

Solução 2: Para floats
/* -------------------
   Versao 1.1: Floats
   Leitura e Impressao
   Identacao a esquerda.  */
#include <stdio.h>
void main()
{
    float mat[3][3];
    int i,j;
    /* Leitura da Matriz: */
    printf("\n\nEntre com os valores para a matriz:");
    for (i=0; i<3; i++)
    {
      printf("\nLinha %d: ",i+1);
      for (j=0; j<3; j++)
        scanf("%f", &mat[i][j]);
    }
    /* Impressao: */
    printf("\n");
    for (i=0; i<3; i++)
    {
      printf("\n|");
      for (j=0; j<3; j++)
        printf("%10.2e|", mat[i][j]);
    }
}

Comentários:
Preste atenção na versão para os float. Verifique como o programa reage para números de ordem de grandeza diferentes (i.e. 124345123.1 e 0.12433; 12.3 e 0.001).

Exercício 4: Página c960.html

Enunciado:
Escreva um programa que abra um arquivo texto e conte o número de caracteres presentes nele. Imprima o número de caracteres na tela.

#include <stdio.h>
#include <stdlib.h>

void main()
{
    FILE *p;
    char c, str[30];
    int i = 0;
    /* Le um nome para o arquivo a ser aberto: */
    printf("\n\n Entre com um nome para o arquivo:\n");
    gets(str);

    /* Abre para leitura */

    if (!(p = fopen(str,"r"))) /* Caso ocorra algum erro na abertura do arquivo..*/
    {   /* o programa aborta automaticamente */
        printf("Erro! Impossivel abrir o arquivo!\n");
        exit(1);
    }

    while (!feof(p)) /* Enquanto não se chegar no final do arquivo */
    {
        c = getc(p); /* Le um caracter no arquivo */
        i++;                 /* E incrementa i */
    }
    fclose(p); /* Fecha o arquivo */
    printf("\nO numero de caracteres do arquivo %s e' igual a %d ", str, i);
}

Exercício 5: Página c980.html

Enunciado:
Escreva um programa que leia uma lista de nomes e idades de um arquivo texto.Prepare um arquivo para ser lido com nomes e idades. Apresente os dados lidos em forma de tabela na tela.Use as funções de sua preferência, mas faça pelo menos duas versões do programa usando funções de leitura diferentes. 

Solução:  apresentaremos uma versão. Tente criar outra ...

#include <stdio.h>
void main()
{
    char nome[30], narq[30];
    FILE *arq;
    int idade;
    printf("\nArquivo fonte: ");
    gets(narq);
    /* Tenta abrir o arquivo: */
    if ((arq = fopen(narq, "r")) == NULL)
    {
          printf("\n Erro: Nao e possivel abrir %s!!", narq);
          exit(1);
    }
    printf("\nConteudo de %s:\n", narq);
    while (!feof(arq))
    {
      fgets(nome, 29, arq);
      fscanf(arq,"%d\n",&idade);
      printf("Nome: %s   | Idade: %d\n", nome, idade);
    }
    fclose(arq);
}

Comentários:
O programa lê o nome e a idade e só então testa o final de arquivo. Ocorre então que, caso exista um nome sem idade no final do arquivo, ocorrerá um erro na leitura da idade. Tente corrigir este problema. O arquivo com o qual o programa foi testado é do tipo:
Nome1 sobrenome1
idade1
nome2 sobrenome2
idade 2
...
nomeN sobrenomeN
idadeN
 

Exercícios adicionais:

Exercício 1:
Verifique o programa abaixo e tente identificar problemas que irão ocorrer em tempo de execução. (Você pode usar um debugger, ou colocar printf's para tentar 'ver' os valores das variáveis).

#define max(A,B) ((A>B) ? (A):(B))
#define min(A,B) ((A<B) ? (A):(B))
#define sqr(x) x*x
void main()
{
int a = 10 ,b = 50, minimo, maximo, quad;
int z = 5;
minimo = min(a,b);
maximo = max(a++,b++);
quad = sqr(z+1);
}

Verifique o que acontece também, se mudarmos a linha de declarações do programa
int a = 10 ,b = 50, minimo, maximo, quad;

por
float a = 10 ,b = 50, minimo, maximo, quad;

Solução:
- a primeira linha, onde se calcula o mínimo de a e b (min(a,b)) está OK, tudo funciona normalmente. A linha
minimo = min(a,b);

é substituída por
minimo = ((a<b) ? (a):(b));

que irá retornar o mínimo entre a e b, corretamente.

- a segunda linha,
maximo = max(a++,b++);

será substituída por
maximo = ((a++>b++) ? (a++):(b++));

Em virtude disto, em tempo de execução, o programa vai incrementar as duas variáveis na hora do teste, o que pode ser indesejado. Mas o pior de tudo é que o maior valor será incrementado duas vezes. Veja bem: se a = 10 e b = 50, quando o programa executa o teste (a++>b++)? ele incrementa a e b. Quando ele retorna o valor de b, ele atribui 51 a máximo, mas incrementa em seguida b mais uma vez (lembre da expressão: ((a++>b++) ? (a++):(b++)). No final da linha, b=52. Isto é indesejado.

- a terceira linha:
quad = sqr(z+1);

será substituída por
quad = z+1*z+1;

em tempo de compilação, segundo a definição da macro (vide o define). Para z=5, teremos
quad = 5+1*5+1;    /* = 11  &  != 36 !!! */

Para resolver este problema é preciso redefinir a macro, usando parênteses, como segue:
#define quad(x)  (x)*(x)

- Ao se trocar os tipos da variável, o resultado é muito interessante: As três macros continuam funcionando perfeitamente! Se tivéssemos uma função para realizar cada uma das operações (min, max e sqr), seriam definidos tipos de retorno para cada função. Assim, se quiséssemos fazer as mesmas operações para diferentes tipos de dados (int, float, double, char), deveríamos escrever uma função para cada tipo, ou uma função com um tipo mais geral (tipo float), e pagar o preço de conversão de tipo a cada chamada. Com o uso de macros, isto se torna desnecessário.

Comentários:
É bem verdade que não se pode fazer tudo com macros. Em muitas vezes, inclusive, seu uso pode até ser meio perigoso, e deve-se tomar cuidado redobrado. Mas em casos simples como os apresentados aqui, as macros podem ser uma boa alternativa para resolver problemas menos complexos, sem excessiva geração de códigos.
 
Exercício 2:
Enunciado:
Escreva um programa que leia um arquivo do tipo base de dados em .txt, retire e apresente informações importantes na tela. Você pode criar o arquivo no EDIT do DOS, ou no NOTEPAD do Windows, ou no PICO, no UNIX. O arquivo pode ser do tipo:

Nome:     Joao da Silva             Telefone: +55 00 000-0000
Endereco: Rua do Joao, 168
$
Nome:     Maria da Silva            Telefone: +55 99 999-9999
Endereco: Rua da Vila, 093
$
...
O símbolo $ (dólar) entre os dados é só para informar que acabou uma sequência de dados, e pode ser usado para restabelecer o sincronismo, caso o programa se perca em algum ponto. Você pode usar um flag para encerrar, mas e melhor que use feof() (é mais elegante...).

Solução:
#include <stdio.h>
#define NOME_TAM 26
#define ENDER_TAM 30
#define TELE_TAM 20

void main()
{
    FILE *arq;
    char nome[NOME_TAM+1], ender[ENDER_TAM+1], tele[TELE_TAM+1], arq_nome[30];
    char c;

    printf("\n\nArquivo de entrada: ");
    gets(arq_nome);
    if ((arq = fopen(arq_nome, "r")) == NULL)
    {
      printf("\nErro ao abrir o arquivo %s!",arq_nome);
      exit(1);
    }

    printf("\n\nImprimindo dados do arquivo: %s\n", arq_nome);
    while (!feof(arq))  /* Enquanto nao encontra o fim do arquivo */
    {
      c =0;
      /* Leitura sincronizada.. */
      fscanf(arq, "%s", nome);     /* Salta a palavra: 'Nome:' */
      fgets(nome, NOME_TAM, arq);
      fscanf(arq, "%s", tele);     /* Salta a palavra: 'Telefone:' */
      fgets(tele, TELE_TAM, arq);
      fscanf(arq, "\n%s", ender);  /* Salta a palavra: 'Endereco:' */
      fgets(ender, ENDER_TAM, arq);
      printf("\nNome: %s\nEndereco: %sTelefone: %s", nome, ender, tele);

      /* Caso alguma parte se perca, e o ponteiro no arquivo fique parado
         em alguma parte do registro, esta rotina fara a sincronizacao. */
      while(c != '$')
      {
        c = getc(arq);
        if (feof(arq)) break;
      }
    }
    fclose(arq);
}

Comentários:
O programa lê sempre primeiro o título do registro a ser lido primeiro, com o único intuito de posicionar o ponteiro de posição do arquivo na posição correta para a leitura do dado interessante. No final da leitura de um registro, ele sempre procura o caracter de sincronização '$'. Caso o ponteiro se perca no meio do arquivo, este procedimento é utilizado para sincronização, com o preço de se perder um registro. O maior desafio neste programa é pegar a informação correta na hora correta. Para isto é fundamental manter o cursor de leitura do arquivo (o ponteiro que aponta para a posição a ser lida no momento) no lugar certo, e ler a quantidade certa de dados.

OBS: Segue a listagem do arquivo usado para o teste:
Nome:     Joao da Silva             Telefone: +55 00 000-0000
Endereco: Rua do Joao, 168
$
Nome:     Maria da Silva            Telefone: +55 99 999-9999
Endereco: Rua da Vila, 093
$
Nome:     Joaquim Oliveira          Telefone: +55 00 000-0000
Endereco: Rua do Ouro, 148
$
Nome:     Rute  da Silva            Telefone: +55 99 999-9999
Endereco: Rua da Comadre, 1086
$
Nome:     Mauricio Alencar          Telefone: +55 00 000-0000
Endereco: Rua das Promessas, 23
$
Nome:     Ana Ribeiro               Telefone: +55 99 999-9999
Endereco: Rua das Perolas, 120/101
 


Curso de C do CPDEE/UFMG - 1996-1999