O
scanf possui comportamento semelhante ao printf, só que para leitura. Da mesma forma que %d no printf significa imprimir um decimal, no scanf significa ler um decimal.
Exemplos comuns do uso do scanf:
int val1, val2;
float val3;
printf("Digite dois números em um ponto flutuante:\n");
scanf("%d", &val1); // lê um decimal e coloca na variável val1
scanf("%i", &val2); // lê um inteiro e coloca na variável val2
scanf("%f", &val3); // lê um ponto flutuante e coloca na variável val3
printf("val1 = %d\n", val1);
printf("val2 = %d\n", val2);
printf("val3 = %f\n", val3);
Qual a diferença em ler um decimal (%d) ou ler um inteiro (%i)?
Será que tem diferença?
Sim, tem.
Veja o que acontece quando se usa o código anterior:
make testes
cc testes.c -o testes
./testes
Digite dois números em um ponto flutuante:
020
020
3.4
val1 = 20
val2 = 16
val3 = 3.400000
Para ambos os inteiros foi digitado 020, como você explica que o val2 tem 16?
Acontece que quando lido com %i o scanf irá interpretar a leitura. Se o que for digitado começar com 0 (ZERO) a leitura será interpretada como octal. 020 em octal corresponde ao decimal 16. Da mesma forma se o número começar com 0x a leitura será considerada como hexadecimal. Se digitar 0x10 para val2 o resultado impresso será 16.
Contudo, é na leitura de strings que o scanf tem recursos!
Muitos reclamam do fato de que o scanf não lê espaços em branco ao ler strings:
char frase[200];
printf("Digite frase:\n");
scanf("%s", frase);
printf("Você digitou: %s\n", frase);
Ao compilar este código e executá-lo:
make testes
cc testes.c -o testes
./testes
Digite frase:
Apenas um teste
Você digitou: Apenas
O que houve com o resto da frase? Porque ele não leu a parte "um teste" que foi digitado?
Acontece que o scanf lê caracteres até encontrar algum que não sirva. Quebras de linha e espaços em branco indicam ao scanf que deve parar a leitura. Mas isto pode ser mudado no scanf:
char frase[200];
printf("Digite frase:\n");
scanf("%[A-Z]s", frase);
printf("Você digitou: %s\n", frase);
Agora apenas letras maiúsculas servem e tão logo o scanf encontre algo que não seja letra maiúscula, ele irá parar de ler:
./testes
Digite frase:
APEnas Um teste
Você digitou: APE
Veja que agora ele leu somente as três letras APE, parando assim que encontrou uma não maiúscula.
Posso colocar todos os tipos de caracteres que valem como leitura dentro dos colchetes:
scanf("%[A-Z a-z 0-9]s", frase);
Agora, na digitação da frase "Apenas uns 200 testes, fim" ele irá ler "Apenas uns 200 testes" parando de ler quando encontrar a vírgula, pois a vírgula não faz parte dos caracteres válidos. Posso até mesmo colocar o ENTER como uma entrada válida:
scanf("%[A-Z a-z 0-9\n]s", frase);
Agora nem mesmo o pressionar de um ENTER irá encerrar a leitura, apenas se for digitado algum caractere que não faça parte do especificado (vírgula, por exemplo).
Também é possível colocar uma exceção, ou seja, o que não pode:
scanf("%[^\n]s", frase);
Agora ele aceita tudo, menos quebra de linha. Pronto, o scanf tem agora o mesmo comportamento do gets e passou a ler espaços em branco.
Se, ainda, se desejar evitar overflow na leitura:
scanf("%199[A-Z a-z 0-9\n]s", frase);
Agora ele não deixará colocar mais do que 199 caracteres a variável frase, já que ela só suporta 200 e precisa-se deixar um de reserva para o final de string (infelizmente não para para usar o * do printf aqui).