Mudanças entre as edições de "SOP-stdio"
(New page: = Entrada e saída padrão = == Explorando ''printf'' para mostrar dados na tela == A função [http://manpages.ubuntu.com/manpages/intrepid/man3/printf.3.html printf] mostra dados na t...) |
|||
Linha 121: | Linha 121: | ||
== Explorando ''scanf'' para ler dados do teclado == | == Explorando ''scanf'' para ler dados do teclado == | ||
− | + | A função [http://manpages.ubuntu.com/manpages/lucid/man3/scanf.3.html scanf] possibilita que se leiam valores pelo teclado, de forma a armazená-los em variáveis. Assim como no caso de [http://manpages.ubuntu.com/manpages/intrepid/man3/printf.3.html printf], esses valores podem ser de diferentes tipos e devem ser especificados segundo um ''formato''. No exemplo abaixo a função ''scanf'' é usada para ler um número inteiro, o que está especificado no formato ''"%d"'': | |
+ | |||
+ | <syntaxhighlight lang=c> | ||
+ | #include <stdio.h> | ||
+ | |||
+ | int main() { | ||
+ | int x; | ||
+ | |||
+ | printf("Digite um número inteiro: "); | ||
+ | scanf("%d", &x); | ||
+ | |||
+ | printf("Número digitado=%d\n", x); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | Note que ''scanf'' serve somente para ler valores do teclado. Portanto, se for necessário mostrar alguma mensagem antes de ler o valor desejado, deve-se usar ''printf''. | ||
+ | |||
+ | === O problema do ''scanf'', quando se digita um valor inválido === | ||
+ | |||
+ | Algumas equipes identificaram um problema na leitura da opção pelo teclado. Se o usuário digitar algo que não seja um número inteiro, a interface entra em ''loop'', sempre mostrando o menu de opções. Para exemplificar, veja abaixo a leitura de um número inteiro feita com ''scanf'': | ||
+ | |||
+ | <syntaxhighlight lang=c> | ||
+ | int main() { | ||
+ | int opcao, lidos; | ||
+ | |||
+ | do { | ||
+ | // aqui mostra as opções do menu | ||
+ | |||
+ | printf("Digite uma opção: "); | ||
+ | lidos = scanf("%d", &opcao); | ||
+ | printf("Opção escolhida=%d\n", opcao); | ||
+ | |||
+ | // executa a ação correspondente à opção escolhida | ||
+ | |||
+ | } while (opcao != 7); // neste exemplo, a opção 7 faz a saída do programa | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | {{#ev:youtube|m6hB9CvHl24}} | ||
+ | |||
+ | [http://www.sj.ifsc.edu.br/~msobral/SOP/videos/com-erro.avi Este video] mostra o problema do ''loop'' quando se digita um valor inválido. | ||
+ | |||
+ | Lembre que o ''scanf'' tenta ler o valor digitado de acordo com a especificação contida no formato (no exemplo, "%d" que corresponde a um número inteiro - maiores detalhes no [http://www.sj.ifsc.edu.br/~msobral/SOP/docs/C_ufmg/aulas/c940.html curso de C da UGMF]). Se ele conseguir, retorna a quantidade de valores lidos (no exemplo isto é guardado na variável ''lidos''). Assim, se ''scanf'' no exemplo acima não conseguir ler o valor digitado, irá retornar 0. O problema é que neste caso o valor que foi digitado fica pendente em memória, quer dizer, o ''scanf'' não descarta esse valor, apesar de não ter conseguido lê-lo. Na próxima vez que o ''scanf'' for chamado ele vai tentar novamente ler esse mesmo valor, e se der erro o valor vai continuar pendente. Isto só se reverterá quando o ''scanf'' especificar um tipo de dado compatível com o valor pendente, ou então se o formato especificar que se deve descartá-lo. Uma possível correção seria portanto testar se o ''scanf'' conseguiu ler o valor, e em caso negativo descartar tudo o que foi digitado. Veja assim o exemplo abaixo com a correção: | ||
+ | |||
+ | <syntaxhighlight lang=c> | ||
+ | int main() { | ||
+ | int x, n; | ||
+ | |||
+ | do { | ||
+ | // aqui mostra as opções do menu | ||
+ | |||
+ | // o laço abaixo faz com que se repita a leitura pelo teclado até que se digite um | ||
+ | // número inteiro. | ||
+ | do { | ||
+ | printf("Digite uma opção: "); | ||
+ | n = scanf("%d", &x); | ||
+ | |||
+ | // se n == 0, então scanf não conseguiu ler o valor, porque ele não é do tipo inteiro. | ||
+ | // Então o valor digitado deve ser descartado. Para descartar tudo o que foi digitado, deve-se | ||
+ | // chamar de novo o scanf, mas com o formato "%*[^\n]". Isto joga fora toda a linha digitada. | ||
+ | // Uma explicação sobre formatos do scanf está na sua página de manual (execute "man scanf"). | ||
+ | if (n == 0) { | ||
+ | n = scanf("%*[^\n]"); | ||
+ | } | ||
+ | } while (n == 0); | ||
+ | |||
+ | printf("Opção escolhida=%d\n", x); | ||
+ | |||
+ | // executa a ação correspondente à opção escolhida | ||
+ | |||
+ | } while (x != 7); // neste exemplo, a opção 7 faz a saída do programa | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | Com a correção acima, o problema do ''loop'' desaparece, o que se pode ver [http://www.sj.ifsc.edu.br/~msobral/SOP/videos/sem-erro.avi neste outro video]. | ||
+ | |||
+ | Esse problema da leitura de valores com ''scanf'' pode aparecer em outras partes da agenda. Então seria mais prático se o algoritmo contido no programa acima, que repete a leitura do teclado até que se consiga ler uma valor válido, pudesse ser reaproveitado. Isto é um bom exemplo do uso de funções da linguagem C. Uma função é um algoritmo a ser reutilizado. Como todo algoritmo, deve ter dados de entrada, uma sequência de instruções, e fornecer dados de saída como resultado. Veja o exemplo abaixo, que se baseia na leitura do teclado com ''scanf'': | ||
+ | |||
+ | <syntaxhighlight lang=c> | ||
+ | |||
+ | // Função meu_scanf: | ||
+ | // Dados de entrada: parâmetro "char * mensagem" é o texto a ser mostrado antes de ler do teclado. | ||
+ | // Dado de saída: meu_scanf devolve (retorna) um número inteiro que corresponde ao valor lido. | ||
+ | |||
+ | int meu_scanf(char * mensagem) { | ||
+ | int x, n; | ||
+ | |||
+ | do { | ||
+ | printf(mensagem); | ||
+ | n = scanf("%d", &x); | ||
+ | |||
+ | // se n == 0, então scanf não conseguiu ler o valor, porque ele não é do tipo inteiro. | ||
+ | // Então o valor digitado deve ser descartado. Para descartar tudo o que foi digitado, deve-se | ||
+ | // chamar de novo o scanf, mas com o formato "%*[^\n]". Isto joga fora toda a linha digitada. | ||
+ | // Uma explicação sobre formatos do scanf está na sua página de manual (execute "man scanf"). | ||
+ | if (n == 0) { | ||
+ | n = scanf("%*[^\n]"); | ||
+ | } | ||
+ | } while (n == 0); | ||
+ | |||
+ | // aqui meu_scanf retorna o valor lido, que está contido na variável "x" | ||
+ | return x; | ||
+ | } | ||
+ | |||
+ | int main() { | ||
+ | int opcao; | ||
+ | |||
+ | do { | ||
+ | // aqui mostra as opções do menu | ||
+ | |||
+ | // chama a função "meu_scanf" para ler um número inteiro do teclado. | ||
+ | opcao = meu_scanf("Digite uma opção: "); | ||
+ | |||
+ | printf("Opção escolhida=%d\n", opcao); | ||
+ | |||
+ | // executa a ação correspondente à opção escolhida | ||
+ | |||
+ | } while (opcao != 7); // neste exemplo, a opção 7 faz a saída do programa | ||
+ | } | ||
+ | |||
+ | </syntaxhighlight> | ||
+ | |||
+ | No final destas [http://www.sj.ifsc.edu.br/~msobral/SOP/slides/aula16.pdf transparências] há um resumo sobre funções. Mais informações estão no [http://www.sj.ifsc.edu.br/~msobral/SOP/docs/C_ufmg/aulas/c700.html curso de linguagem C da UFMG]. |
Edição das 07h33min de 30 de novembro de 2010
Entrada e saída padrão
Explorando printf para mostrar dados na tela
A função printf mostra dados na tela de acordo com um formato (daí seu nome: printf = print formatted, ou imprima formatado). O exemplo mais simples de uso do printf faz com que seja mostrada somente uma constante de texto, como a seguir:
printf("Apenas uma constante de texto ...\n");
O resultado na tela é:
Apenas uma constante de texto ...
Porém printf faz muito mais do que isso. Um segundo uso envolve mostrar dados que misturam constantes de texto e valores numéricos:
int voltas = 15;
float angulo = 3.1416;
printf("Contador de voltas = %d\n", voltas);
printf("Angulo da volta atual = %f radianos\n", angulo);
Na tela será mostrado o seguinte:
Contador de voltas = 15 Angulo da volta atual = 3.1416 radianos
Esse segundo exemplo mostra como especificar o formato do que deve aparecer na tela. O formato é o primeiro argumento da função printf, e ele contém uma especificação do texto que deve aparecer na tela. Nesse texto podem-se indicar valores a serem substituídos (eles aparecem prefixados com %), os quais devem ser fornecidos nos demais argumentos da função. No primeiro printf do exemplo acima, o formato é:
"Contador de voltas = %d\n"
Nesse texto, toda sequência de caracteres que iniciar com % indica que ali deve-se substituir por um valor. Dependendo da letra que vier depois de %, o tipo de valor será int (inteiro), float (real), double (real com mais casas), char (caractere), string (texto). A tabela abaixo exemplifica os diferentes tipos de valores que podem ser especificados no formato da função printf:
Tipo de dados | Formato | Exemplo | O que aparece na tela |
---|---|---|---|
int | %d | int horas=2;
printf("Já se passaram %d horas ...\n", horas);
|
Já se passaram 2 horas ... |
char | %c | char letra = 'S';
printf("Você digitou a letra %c !\n", letra);
|
Você digitou a letra S ! |
float | %f | float media = 4.5;
printf("Média calculada=%f\n", media);
|
Média calculada=4.5 |
double | %g | double angulo = 0.1122333;
printf("Angulo=%g\n", angulo);
|
Angulo=0.111222333 |
char * (string) | %s | char * nome = "Filomena";
printf("Cara sra. %s ...", nome);
|
Cara sra. Filomena ... |
Para substituir mais de um valor, basta especificar no formato e fornecer os valores nos demais argumentos:
int hora = 10, minuto = 5, segundo = 0;
printf("Horario: %d:%d:%d\n", hora, minuto, segundo);
... o que faz aparecer na tela:
Horario: 10:5:0
Esse último exemplo poderia ser melhorado, pois o horário ficou esquisito de ler ... normalmente costumamos ver horas, minutos e segundos com dois algarismos, como a seguir:
Horario: 10:05:00
É possível fazer com que printf faça isso automaticamente, bastando especificar o seguinte no formato:
int hora = 10, minuto = 5, segundo = 0;
printf("Horario: %02d:%02d:%02d\n", hora, minuto, segundo);
Note que ao invés de %d se especificou %02d. Esse formato faz com que se mostre um número inteiro com no mínimo dois algarismos. Caso o número tenha somente um algarismo, printf automaticamente adiciona um 0 à esquerda. Isso funciona somente para formatos numéricos.
Além de acrescentar zeros à esquerda de valores numéricos, é possível tabular os valores mostrados de forma que ocupem uma certa quantidade de colunas. Por exemplo, caso se deseje mostrar o seguinte:
Nome Idade Sexo Marcelo 39 M Marina 9 F Luis 11 M
... pode-se fazer assim:
printf("%-12s %-12s %s\n", "Nome", "Idade", "Sexo");
printf("%-12s %-12d %c\n", "Marcelo", 39, 'M');
printf("%-12s %-12d %c\n", "Marina", 9, 'F');
printf("%-12s %-12d %c\n", "Luis", 11, 'M');
Note que desta vez adicionou-se após % a sequência -12. Isso indica que o valor a ser mostrado deve ocupar no mínimo 12 posições, preenchendo-se com espaços caso necessário. Veja que os valores foram alinhados à esquerda. Caso se deseje que sejam alinhados à direita, basta fazer assim:
printf("%12s %12s %s\n", "Nome", "Idade", "Sexo");
printf("%12s %12d %c\n", "Marcelo", 39, 'M');
printf("%12s %12d %c\n", "Marina", 9, 'F');
printf("%12s %12d %c\n", "Luis", 11, 'M');
... e assim aparecerá o seguinte na tela:
Nome Idade Sexo Marcelo 39 M Marina 9 F Luis 11 M
Explorando scanf para ler dados do teclado
A função scanf possibilita que se leiam valores pelo teclado, de forma a armazená-los em variáveis. Assim como no caso de printf, esses valores podem ser de diferentes tipos e devem ser especificados segundo um formato. No exemplo abaixo a função scanf é usada para ler um número inteiro, o que está especificado no formato "%d":
#include <stdio.h>
int main() {
int x;
printf("Digite um número inteiro: ");
scanf("%d", &x);
printf("Número digitado=%d\n", x);
}
Note que scanf serve somente para ler valores do teclado. Portanto, se for necessário mostrar alguma mensagem antes de ler o valor desejado, deve-se usar printf.
O problema do scanf, quando se digita um valor inválido
Algumas equipes identificaram um problema na leitura da opção pelo teclado. Se o usuário digitar algo que não seja um número inteiro, a interface entra em loop, sempre mostrando o menu de opções. Para exemplificar, veja abaixo a leitura de um número inteiro feita com scanf:
int main() {
int opcao, lidos;
do {
// aqui mostra as opções do menu
printf("Digite uma opção: ");
lidos = scanf("%d", &opcao);
printf("Opção escolhida=%d\n", opcao);
// executa a ação correspondente à opção escolhida
} while (opcao != 7); // neste exemplo, a opção 7 faz a saída do programa
}
Este video mostra o problema do loop quando se digita um valor inválido.
Lembre que o scanf tenta ler o valor digitado de acordo com a especificação contida no formato (no exemplo, "%d" que corresponde a um número inteiro - maiores detalhes no curso de C da UGMF). Se ele conseguir, retorna a quantidade de valores lidos (no exemplo isto é guardado na variável lidos). Assim, se scanf no exemplo acima não conseguir ler o valor digitado, irá retornar 0. O problema é que neste caso o valor que foi digitado fica pendente em memória, quer dizer, o scanf não descarta esse valor, apesar de não ter conseguido lê-lo. Na próxima vez que o scanf for chamado ele vai tentar novamente ler esse mesmo valor, e se der erro o valor vai continuar pendente. Isto só se reverterá quando o scanf especificar um tipo de dado compatível com o valor pendente, ou então se o formato especificar que se deve descartá-lo. Uma possível correção seria portanto testar se o scanf conseguiu ler o valor, e em caso negativo descartar tudo o que foi digitado. Veja assim o exemplo abaixo com a correção:
int main() {
int x, n;
do {
// aqui mostra as opções do menu
// o laço abaixo faz com que se repita a leitura pelo teclado até que se digite um
// número inteiro.
do {
printf("Digite uma opção: ");
n = scanf("%d", &x);
// se n == 0, então scanf não conseguiu ler o valor, porque ele não é do tipo inteiro.
// Então o valor digitado deve ser descartado. Para descartar tudo o que foi digitado, deve-se
// chamar de novo o scanf, mas com o formato "%*[^\n]". Isto joga fora toda a linha digitada.
// Uma explicação sobre formatos do scanf está na sua página de manual (execute "man scanf").
if (n == 0) {
n = scanf("%*[^\n]");
}
} while (n == 0);
printf("Opção escolhida=%d\n", x);
// executa a ação correspondente à opção escolhida
} while (x != 7); // neste exemplo, a opção 7 faz a saída do programa
}
Com a correção acima, o problema do loop desaparece, o que se pode ver neste outro video.
Esse problema da leitura de valores com scanf pode aparecer em outras partes da agenda. Então seria mais prático se o algoritmo contido no programa acima, que repete a leitura do teclado até que se consiga ler uma valor válido, pudesse ser reaproveitado. Isto é um bom exemplo do uso de funções da linguagem C. Uma função é um algoritmo a ser reutilizado. Como todo algoritmo, deve ter dados de entrada, uma sequência de instruções, e fornecer dados de saída como resultado. Veja o exemplo abaixo, que se baseia na leitura do teclado com scanf:
// Função meu_scanf:
// Dados de entrada: parâmetro "char * mensagem" é o texto a ser mostrado antes de ler do teclado.
// Dado de saída: meu_scanf devolve (retorna) um número inteiro que corresponde ao valor lido.
int meu_scanf(char * mensagem) {
int x, n;
do {
printf(mensagem);
n = scanf("%d", &x);
// se n == 0, então scanf não conseguiu ler o valor, porque ele não é do tipo inteiro.
// Então o valor digitado deve ser descartado. Para descartar tudo o que foi digitado, deve-se
// chamar de novo o scanf, mas com o formato "%*[^\n]". Isto joga fora toda a linha digitada.
// Uma explicação sobre formatos do scanf está na sua página de manual (execute "man scanf").
if (n == 0) {
n = scanf("%*[^\n]");
}
} while (n == 0);
// aqui meu_scanf retorna o valor lido, que está contido na variável "x"
return x;
}
int main() {
int opcao;
do {
// aqui mostra as opções do menu
// chama a função "meu_scanf" para ler um número inteiro do teclado.
opcao = meu_scanf("Digite uma opção: ");
printf("Opção escolhida=%d\n", opcao);
// executa a ação correspondente à opção escolhida
} while (opcao != 7); // neste exemplo, a opção 7 faz a saída do programa
}
No final destas transparências há um resumo sobre funções. Mais informações estão no curso de linguagem C da UFMG.