Entrada Básica
As funções de entrada do C permitem ao programa interagir com o usuário lendo
informações. A função principal que usaremos pra isso é a função scanf de
<stdio.h>.
Função scanf
Assim como printf, a função scanf recebe uma string de formato; a diferença
é que nesse caso a string determina não o que será exibido, mas o que será lido.
Os modificadores de comprimento e especificadores de conversão na string de
formato determinam o tipo do valor que será lido. Vejamos algumas especificações
(todas devem iniciar com %):
| Modificador | Especificador | Significado | 
|---|---|---|
| c | Um caractere, será armazenado em um char | |
| d | Um número inteiro, será armazenado em um int | |
| l | d | Um número inteiro, será armazenado em um long int | 
| ll | d | Um número inteiro, será armazenado em um long long int | 
| f | Um número real, será armazenado em um float | |
| l | f | Um número real, será armazenado em um double | 
| L | f | Um número real, será armazenado em um long double | 
Diferente de printf, perceba que scanf utiliza a especificação %f para
float e %lf para double. Você não deve utilizar %f no lugar de %lf ou
%d lugar de %c e vice-versa.
Para armazenar um valor lido com uma especificação de conversão, precisamos
especificar seu alvo (uma localização na memória). No nosso caso, utilizaremos o
operador & (que será explicado depois) para obter o endereço de uma variável
na memória:
int n;
scanf("%d", &n);
A chamada de função acima lê um número inteiro da entrada e armazena seu valor
em n por meio de seu endereço na memória. A especificação %d descarta
quaisquer caracteres white-space da entrada até encontrar outro tipo de
caractere, portanto esse scanf funciona com qualquer número de caracteres
white-space precedendo o número na entrada.
Antes de continuar é importante definir o que é um caractere white-space. Essa expressão se refere a qualquer caractere da lista abaixo.
- ' '(espaço)
- '\t'(tabulação horizontal)
- '\v'(tabulação vertical)
- '\n'(quebra de linha)
- '\f'(quebra de página)
Retomando nosso foco, já podemos fazer um simples programa com entrada e saída:
#include <stdio.h>
int main(void)
{
    printf("Digite um número inteiro: ");
    int num;
    scanf("%d", &num);
    printf("O número digitado foi %d.\n", num);
    return 0;
}
Execute o código acima e tente fazê-lo produzir um resultado incorreto. A leitura do número pode dar errado de várias formas, incluindo:
- A entrada não é um número. Nesse caso o scanfnão modifica a entrada (exceto por descartar caracteres white-space iniciais) e ela pode ser lida futuramente.
- O número digitado pode ser grande demais para ser armazenado em um int. Nesse caso o comportamento do programa é indefinido.
- O número digitado pode conter casas decimais, e nesse caso o separador decimal e todos dígitos seguintes serão ignorados.
Com a especificação %d a sequência de dígitos será lida até um caractere de
outro tipo (ex. uma letra) ser encontrado. Com a entrada 163p90, scanf
associará 163 ao %d e p90 continuará na entrada para ser lido futuramente.
Podemos decompor a entrada 163p90 em duas variáveis int e uma char da
seguinte forma:
int n1,
    n2;
char ch;
scanf("%d%c%d", &n1, &ch, &n2);
printf("n1: %d\n"
       "n2: %d\n"
       "ch: %c\n", n1, n2, ch);
O scanf acima associa 163 ao primeiro %d, armazena o valor em n1 e a
sequência p90 continua na entrada. O próximo caractere ('p' nesse caso) se
associa ao %c e é armazenado em ch. O último %d recebe o inteiro 90 que
é armazenado em n2. Depois, os valores são exibidos com printf.
Quando um caractere da string de formato não faz parte de uma especificação de
conversão, o scanf verificará se esse caractere é igual ao próximo caractere
da entrada. Se sim, o caractere da entrada é descartado e prosseguimos com a
string de formato, caso contrário a execução do scanf para.
printf("Quantos anos você tem? ");
int idade;
scanf("Eu tenho %d", &idade);
O scanf acima só chega ao %d se todos caracteres anteriores forem
correspondentes na entrada. Se a entrada for Eu tenho 5, o valor de idade
será 5, mas a entrada Eu tinha 5 não armazena nada em idade e seu valor é
indeterminado. Um espaço na string de formato se encaixa em qualquer número
(inclusive zero) de caracteres white-space na entrada, então a entrada
Eutenho5 também funciona corretamente.
A string de formato "Eu tenho%d" também funciona corretamente pois, como dito
no início, a especificação %d automaticamente pula qualquer espaço em branco
até encontrar um caractere non-white-space (caracteres não white-space, como
letras e dígitos).
Não se esqueça que após digitar um número e ele ser lido, o scanf não descarta
o caractere de quebra de linha (\n) do final da linha de entrada. Isso pode
fazer com que esse caractere se associe a uma futura especificação %c e isso
pode ser indesejado. Para descartar esse caractere de uma forma simples, utilize
um espaço antes da especificação %c e isso pulará a quebra de linha.
Aqui está um código em que a quebra de linha na entrada pode ser prejudicial:
int num;
scanf("%d", &num);
char ch;
scanf("%c", &ch); // Isso lerá uma quebra de linha se o usuário tiver digitado
                  //  um número e pressionado ENTER.
printf("O caractere lido é '%c'\n", ch);
E aqui está uma versão que se previne disso:
int num;
scanf("%d", &num);
char ch;
//     ↓
scanf(" %c", &ch); // O usuário pode inserir espaços e pressionar ENTER o quanto
                   // quiser. Apenas um caractere non-white-space se associará.
printf("O caractere lido é '%c'\n", ch);
Referências
- ISO/IEC JTC1/SC22/WG14 N2310
- 5.2.2 Character display semantics
 - 6.4 Lexical elements
 - 7.21.6.2 The fscanf function