Home RecentChanges

Lesson02

Problema: conversão de temperaturas

Podemos converter temperaturas na escala Fahrenheit para a escala Celsius da seguinte forma:

$$T_{Celsius} = \frac{5}{9} \left( T_{Fahrenheit} - 32\right)$$

Algoritmo

Input: limite inferior e limite superior.
Output: impressão da temperatura na escala Celsius para cada temperatura (número inteiro) no intervalo definido pelos limites dados como entrada.

  1. Inicia no limite inferior de temperatura na escala Fahrenheit.
    fahr = inferior;
  2. Enquanto temperatura não fôr superior ao limite superior:
    while (fahr <= superior)
    1. converte temperatura para escala Celsius;
      celsius = 5 * (fahr-32) / 9;
    2. escreve linha da tabela com temperaturas;
      printf("%d\t%d\n", fahr, celsius);
    3. aumenta valor de temperatura na escala Fahrenheit.
      fahr = fahr + passo;

Implementação em C

#include <stdio.h>

/* Conversao Fahrenheit-Celsius */
int main ()
{
  /* Declaracao de variaveis. */
  int fahr, celsius;
  int inferior, superior, passo;

  /* Inicializacao de variaveis. */
  inferior = 0;
  superior = 300;
  passo = 20;

  /* Inicia no limite inferior de temperatura na escala Fahrenheit. */
  fahr = inferior;

  /* Enquanto temperatura nao for superior ao limite superior. */
  while (fahr <= superior)
  {
    /* Converte temperatura para escala Celsius. */
    celsius = 5 * (fahr-32) / 9;

    /* Escreve linha da tabela com temperaturas. */
    printf("%d\t%d\n", fahr, celsius);

    /* Aumenta valor de temperatura na escala Fahrenheit. */
    fahr = fahr + passo;
  }

  return 0;
}

Notas sobre o código:

NOTA: Evitar erros comuns na definição de ciclos:

while (i >= 0);
  i = i -1;

Cuidado com a colocação dos ;'s!

Problema: $0^o F$ são $-17,8^o C$ e não $-17^o C$!

Este problema ocorre devido à divisão inteira na instrução celsius = 5 * (fahr-32) / 9;. A divisão entre inteiros é a divisão inteira, o resultado é o quociente da divisão (número inteiro), e.g., o resultado de 5/9 é 0 e o resultado de 5/2 é 2.

Para resolver este problema podemos de utilizar o tipo float:

#include <stdio.h>

int main ()
{
  float fahr, celsius;
  int inferior, superior, passo;

  inferior = 0;
  superior = 300;
  passo = 20;

  fahr = inferior;
  while (fahr <= superior) {
    celsius = (5.0/9.0) * (fahr-32);
    printf("%3.0f\t%6.1f\n", fahr, celsius);
    fahr = fahr + passo;
  }

  return 0;
}

Definição de constantes

No exemplo anterior não precisamos de facto de tantas variáveis, uma vez que inferior, superior, e passo tomam sempre o mesmo valor. Logo, podemos reescrever da seguinte forma:

#include <stdio.h>

int main ()
{
  float fahr, celsius;
  int inferior, superior, passo;

  fahr = 0;
  while (fahr <= 300) {
    celsius = (5.0/9.0) * (fahr-32);
    printf("%3.0f\t%6.1f\n", fahr, celsius);
    fahr = fahr + 20;
  }

  return 0;
}

Porém não é boa prática utilizarmos valores explícitos no código. Devemos antes utilizar constantes que podemos definir com a directiva #define. Desta forma podemos reescrever o exemplo da seguinte forma:

#include <stdio.h>

#define INFERIOR 0
#define SUPERIOR 300
#define PASSO 20

int main ()
{
  float fahr, celsius;
  int inferior, superior, passo;

  fahr = INFERIOR;
  while (fahr <= SUPERIOR) {
    celsius = (5.0/9.0) * (fahr-32);
    printf("%3.0f\t%6.1f\n", fahr, celsius);
    fahr = fahr + PASSO;
  }

  return 0;
}

Nota: As directivas de preprocessamento como #include ou #define não têm ; no final.

Ciclos for

No exemplo anterior podemos utilizar o for em vez do while da seguinte forma:

#include <stdio.h>

#define INFERIOR 0
#define SUPERIOR 300
#define PASSO 20

int main ()
{
  float fahr, celsius;
  int inferior, superior, passo;

  for (fahr = INFERIOR; fahr <= SUPERIOR; fahr = fahr + PASSO) {
    celsius = (5.0/9.0) * (fahr-32);
    printf("%3.0f\t%6.1f\n", fahr, celsius);
  }

  return 0;
}

Notar a colocação das instruções de inicialização, de teste, e de incremento no for.

Leitura de valores do standard input

Nos exemplos anteriores escrevemos para o standard output com a função printf. Vamos agora ver como ler valores do standard input com a função scanf também definida em no ficheiro stdio.h:

#include <stdio.h>

/* Objectivo: vamos pedir um inteiro ao utilizador e devolver o
   quadrado desse numero. */

int main ()
{
   int x;
   printf("Introduza um valor inteiro:\n");
   scanf("%d", &x);
   printf("O quadrado de %d é %d\n", x, x*x);
   return 0;
}

Neste exemplos indicamos com %d que o input dado pelo utilizador deve interpretado como um inteiro e guardado na variável x. A razão de colocarmos &x em vez apenas x tem a haver com facto de termos de passar a variável por referência e não por valor, como acontece normalmente. Mais tarde no decurso da disciplina vamos ver mais em detalhe a diferença de passar por valor e de passar por referência.

A função scanf é bastante flexível. Por exemplo, se quisermos ler dois inteiros basta utilizar o scanf da seguinte forma:

scanf("%d%d", &x, &y);

E para ler um float:

scanf("%f", &r);

Não esquecer, podemos sempre ver todos os detalhes a respeito desta função na sua man page, man 3 scanf.

Exemplo com leitura e escrita

Neste exemplo queremos ler uma lista de inteiros do standard input introduzidos pelo utilizador e mostrar esses valores no ecrã, ou seja, no standard output. O programa deve terminar quando ler um valor negativo.

Podemos pensar no seguinte algoritmo:

Na implementação completa temos:

#include <stdio.h>

/* Copia inteiros do input para output */
int main ()
{
  int v;

  scanf("%d", &v);
  while (v >= 0) {
    printf("%d\n", v);
    scanf("%d", &v);
  }

  return 0;
}

Notar que neste exemplo utilizámos o operador >=, um dos operadores de comparação:

Comprimento de uma lista

Neste exemplo queremos ler uma lista de inteiros do standard input introduzidos pelo utilizador e, no final, mostrar o número de elementos na lista. O programa deve terminar quando ler um valor negativo.

Consideremos o seguinte algoritmo:

E a sua implementação em C:

#include <stdio.h>

/* Conta numero de elementos de lista de numeros positivos. */
int main ()
{
  int v;
  long contador;

  contador = 0;
  scanf("%d", &v);
  while (v >= 0) {
     ++contador;
     scanf("%d", &v);
  }

  printf("%ld\n", contador);

  return 0;
}

Neste caso a variável contador tem tipo long int, o que significa que temos garantidamente pelo menos 32 bits para guardar o inteiro. E, uma vez que esta variável é do tipo long int, na instrução printf("%ld\n", contador) temos de indicar esse facto com %ld.

A instrução ++contador introduz um novo operador, que é equivalente à instrução contador = contador+1. Para além deste operador, podemos utilizar outros operadores: contador++, --contador, e contador--. Ainda que todos tenham o comportamento esperado, existem alguns detalhes a ter em conta:

int a = 0, b = 0, c = 0;
b = a++;   /* Passamos a ter b=0 e a=1 */
c = ++a;   /* Passamos a ter c=2 e a=2 */

E, no caso de um contador num ciclo:

int a = 0;
while (a++ <= 3) {
  ... /* O ciclo e' executado 4 vezes */
}

int a = 0;
while (++a <= 3) {
  ... /* O ciclo e' executado 3 vezes */
}

Desta forma, a utilização dos operadores ++ e -- de forma prefixa ou sufixa pode influenciar a execução do programa.

No caso da contagem do número de elementos numa lista podemos também substituir o while por um for da seguinte forma:

#include <stdio.h>

/* Conta numero de elementos de lista de numeros positivos. */
int main ()
{
  int v;
  long contador;

  scanf("%d", &v);
  for (contador = 0; v >= 0; ++contador)
    scanf("%d", &v);

  printf("%ld\n", contador);

  return 0;
}

Notar novamente a colocação das instruções de inicialização, de teste, e de incremento no for.

E se quisermos utilizar um double em vez de um long int? Temos de fazer o seguinte:

#include <stdio.h>

/* Conta numero de elementos de lista de numeros positivos. */
int main ()
{
  int v;
  double contador;

  scanf("%d", &v);
  for (contador = 0; v >= 0; ++contador)
    scanf("%d", &v);

  printf("%.0f\n", contador);

  return 0;
}

Em que para além da alteração do tipo da variável foi necessário mudar a formatação no printf de forma a ter em conta o novo tipo da variável contador, em que utilizamos agora %.0f. Em geral podemos utilizar %f para representar variáveis do tipo float e double. Neste caso colocámos também .0 para que o número fosse escrito sem casa decimais.

Exemplo: contagem de aprovações e reprovações

Considere-se uma lista de inteiros que denota as notas dos alunos numa disciplina. Queremos ler uma lista de inteiros positivos introduzidos pelo utilizador e terminar quando for lido um número negativo. No final queremos mostrar o número de notas lidas, o número de aprovações, e o número de reprovações.

Algoritmo:

Implementação:

#include <stdio.h>

/* Contagem de aprovacoes e reprovacoes de lista de notas. */
int main ()
{
  int v, notas, aprovacoes;

  notas = aprovacoes = 0;
  scanf("%d", &v);
  while (v >= 0)
  {
    notas++;

    if (v >= 10)
      aprovacoes++;

    scanf("%d", &v);
  }

  printf("Total: %d, Aprovacoes: %d, Reprovacoes: %d\n",
         notas, aprovacoes, notas-aprovacoes);

  return 0;
}

Neste exemplo a instrução notas = aprovacoes = 0 é equivalente a (notas = (aprovacoes = 0)) e ainda a notas = 0; aprovacoes = 0.

Exercícios:

  1. Modifique o programa anterior por forma a calcular também qual a maior nota presente na lista.
  2. Modifique o programa anterior por forma a calcular a média das notas positivas.

Linha de comandos

Nem sempre queremos estar a escrever o input na consola, em geral queremos passar como entrada o conteúdo de um ficheiro. Como é que podemos então passar o conteúdo de um ficheiro para um programa através do standard input?

Podemos redireccionar para o standard input:

$ ./myprogram < input.txt

E também podemos guardar o resultado do programa enviado para o standard output num ficheiro:

$ ./myprogram > output.txt

E podemos realizar ambas as operações em simultâneo:

$ ./myprogram < input.txt > output.txt