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 %):

ModificadorEspecificadorSignificado
cUm caractere, será armazenado em um char
dUm número inteiro, será armazenado em um int
ldUm número inteiro, será armazenado em um long int
lldUm número inteiro, será armazenado em um long long int
fUm número real, será armazenado em um float
lfUm número real, será armazenado em um double
LfUm 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 scanf nã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