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.
- Inicia no limite inferior de temperatura na escala Fahrenheit.
fahr = inferior;
- Enquanto temperatura não fôr 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;
- converte temperatura para escala Celsius;
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:
- podemos fazer comentários no código colocando-os entre
/*
e*/
; - temos de declarar todas as variáveis;
- devemos inicializar todas as variáveis, em particular aquelas cujo valor inicial é relevante no que se segue;
- utilizámos a estrutura de controlo
while
, que permite definir ciclos; - a indentação ajuda a compreender a estrutura do programa, mas não indispensável;
- a função
printf
permite efectuar escrita formatada, notar a utilização de%d
para representar um inteiro decimal e o caracter tabulação\t
; - podemos melhorar a formatação...
printf("%3d\t%6d\n", fahr, celsius);
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:
- Lê um inteiro e guarda-o numa variável
v
.scanf("%d", &v);
- Se v for válido:
while (v >= 0)
- escreve o inteiro lido;
printf("%d\n", v);
- lê próximo inteiro e guarda-o novamente na variável
v
.scanf("%d", &v);
- escreve o inteiro lido;
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:
- operadores de comparação de igualdade:
==
,!=
; - operadores de comparação de desigualdades:
>
,<
,<=
, e>=
.
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:
- Inicializa contador a
0
.contador = 0;
- Lê um inteiro.
scanf("%d", &v);
- Enquanto conseguir ler um valor válido:
while (v >= 0)
- incrementa contador;
++contador;
- lê próximo inteiro;
scanf("%d", &v);
- incrementa contador;
- Escreve valor do contador.
printf("%ld\n", contador);
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:
- Inicializa contador de notas e aprovações a
0
.notas = aprovacoes = 0;
- Lê nota.
scanf("%d", &v);
- Enquanto conseguir ler uma nota válida:
while (v >= 0)
- incrementa o contador de notas;
notas++;
- se a nota lida é positiva,
if (v >= 10)
- incrementa contador de aprovações;
aprovacoes++;
- incrementa contador de aprovações;
- lê nota seguinte.
scanf("%d", &v);
- incrementa o contador de notas;
- Escreve valor dos contadores.
printf(...);
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:
- Modifique o programa anterior por forma a calcular também qual a maior nota presente na lista.
- 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