C Entrada com scanf

Até agora, todos os valores dos seus programas estavam escritos no próprio código: int idade = 29;. Para mudar a idade, só editando e compilando de novo. Um programa de verdade pergunta: qual é a sua idade? Quanto custa o produto? Nesta aula você vai aprender o scanf, a função do C que lê o que a pessoa digita no teclado e guarda numa variável, e as armadilhas que fazem dele a função mais perguntada nos fóruns de C: o & esquecido, o %lf do double e o Enter que sobra.

Você vai aprender
  • Como ler um número digitado com scanf("%d", &idade), e para que serve o &.
  • Como ler vários valores de uma vez, e o que o scanf faz com espaços, Enters e outros caracteres.
  • As letras de cada tipo: %d, %lf (double), %f (float), %c, %s e %i, e onde elas são diferentes das do printf.
  • O valor de retorno: como saber se a pessoa digitou mesmo um número.
  • O espaço mágico do " %c" e a largura do "%19s".
  • Os erros reais: Falha de segmentação sem o &, o R$ 0.00 do %f com double e o stack smashing detected.

Todos os programas desta aula foram compilados com o gcc 15.2 na máquina fictícia da Ana (usuária ana, computador notebook), na pasta ~/c, com o comando padrão do guia: gcc -std=c23 -Wall -Wextra arquivo.c -o programa. Nos terminais, o que vem logo depois de uma pergunta do programa (como o 29 em Quantos anos você tem? 29) foi digitado pela Ana, seguido de Enter.


A ideia: o formulário de papel

Lembra do printf? Ele é um formulário já preenchido: o texto tem lacunas (%d) e o programa coloca os valores nelas para mostrar na tela.

O scanf é o caminho contrário. Ele é um formulário em branco que você entrega para a pessoa. O %d diz "aqui vai um número inteiro". A pessoa escreve, e o scanf lê o que foi escrito e guarda na gaveta que você indicou, a variável. Para isso ele precisa saber onde fica a gaveta, e é por isso que aparece o &.

O nome vem de scan formatted, "ler formatado" (o printf é print formatted, "imprimir formatado"). Ele mora no mesmo <stdio.h> do printf.


1. A primeira leitura

#include <stdio.h>

int main(void) {
    int idade = 0;
    printf("Quantos anos você tem? ");
    scanf("%d", &idade);
    printf("Daqui a 10 anos você terá %d anos.\n", idade + 10);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra idade.c -o idade
$ ./idade
Quantos anos você tem? 29                  « o programa parou e esperou; a Ana digitou 29 e apertou Enter
Daqui a 10 anos você terá 39 anos.
int idade = 0;
A gaveta que vai receber o número. Começa com 0 para nunca ter lixo (aula Variáveis): logo abaixo você vai ver por que isso importa.
printf("Quantos anos você tem? ");
A pergunta, o prompt. Sem \n no fim, de propósito: assim o que a pessoa digita aparece na mesma linha da pergunta. O scanf não mostra nada sozinho; sem esse printf, o programa ficaria parado numa tela vazia, e a pessoa nem saberia que ele está esperando.
scanf(
Chama a função. O programa para aqui e espera a pessoa digitar e apertar Enter.
"%d"
O formato: "leia um número inteiro". É a mesma letra do printf.
&idade
O endereço da variável idade: o lugar da memória onde ela mora. O & se lê "endereço de". O printf só precisa do valor para mostrar (idade); o scanf precisa saber onde guardar (&idade).
idade + 10
Depois do scanf, idade vale o que foi digitado e pode ser usada como qualquer variável.
Por que o &? Uma prévia

Quando você chama uma função, o C entrega a ela uma cópia do valor. Se o scanf recebesse idade, receberia uma cópia do 0, e não teria como mudar a variável de verdade. Com &idade, ele recebe o endereço e escreve direto na gaveta. Isso tem nome, ponteiro, e aulas inteiras: Memória e Endereços, Ponteiros e Passar por Ponteiro. Por enquanto, guarde a regra: no scanf, número e caractere levam &.

E se a pessoa digitar outra coisa?

$ ./idade
Quantos anos você tem? abc
Daqui a 10 anos você terá 10 anos.     « "abc" não é número: o scanf não guardou nada, e idade continuou 0
$ ./idade
Quantos anos você tem? 29 anos
Daqui a 10 anos você terá 39 anos.     « leu o 29 e parou no espaço; o " anos" ficou sem ler

O %d lê algarismos (com um sinal - ou + na frente, se tiver) e para no primeiro caractere que não serve para um número. Com abc, ele para logo no a, sem ter lido nada, e a variável fica como estava. Foi por isso que ela começou com 0: sem o = 0, o programa mostraria um valor qualquer que estivesse na memória. A seção 4 mostra como o programa descobre que a leitura falhou.

Experimente: clique em Rodar. O programa vai parar na pergunta: clique no terminal da caixa, digite a sua idade e aperte Enter. Rode de novo e digite abc, depois -5 e depois 42 (com espaços antes). Depois troque a pergunta e a conta: pergunte quantas horas a pessoa dorme por noite e mostre quantas horas ela dorme por semana (horas * 7).

#include <stdio.h>

int main(void) {
    int idade = 0;
    printf("Quantos anos você tem? ");
    scanf("%d", &idade);
    printf("Daqui a 10 anos você terá %d anos.\n", idade + 10);
    return 0;
}

2. Esqueci o &

É o erro número 1 de quem começa. Sem o &, o scanf recebe o valor da variável (0) e acha que ele é um endereço:

#include <stdio.h>

int main(void) {
    int idade = 0;
    printf("Quantos anos você tem? ");
    scanf("%d", idade);
    printf("Você tem %d anos.\n", idade);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra semE.c -o semE
semE.c: In function ‘main’:
semE.c:6:13: warning: format ‘%d’ expects argument of type ‘int *’, but argument 2 has type ‘int’ [-Wformat=]
    6 |     scanf("%d", idade);
      |            ~^   ~~~~~
      |             |   |
      |             |   int
      |             int *
$ ./semE
Quantos anos você tem? 29
Falha de segmentação     (imagem do núcleo gravada) ./semE
  1. O aviso diz: "o formato %d espera um argumento do tipo int *, mas o argumento 2 é do tipo int". O * em int * quer dizer "endereço de um int". O desenho embaixo do código aponta as duas partes que não combinam: o %d e o idade.
  2. Ele compilou mesmo assim: é só um aviso. Mas o programa tentou escrever o 29 no "endereço 0", que não pertence a ele, e o sistema o derrubou: Falha de segmentação (segmentation fault), código de saída 139.
  3. Conserto: scanf("%d", &idade);.
Leia os avisos do scanf

O -Wall confere cada % do formato contra a variável que você passou. Um aviso -Wformat num scanf quase sempre é um programa que vai ler errado ou cair. No editor do guia, o Clang avisa com outras palavras (format specifies type 'int *' but the argument has type 'int'), e o programa não cai: mostra Você tem 0 anos., porque a memória do WebAssembly é organizada de outro jeito (aula O Editor do Guia). Não confie nisso: no Linux, cai.


3. Vários valores de uma vez

Um formato pode ter várias lacunas. Cada uma precisa da sua variável, na mesma ordem, cada uma com o seu &:

#include <stdio.h>

int main(void) {
    int a = 0;
    int b = 0;
    printf("Digite dois números: ");
    scanf("%d %d", &a, &b);
    printf("%d + %d = %d\n", a, b, a + b);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra soma.c -o soma
$ ./soma
Digite dois números: 3 4
3 + 4 = 7
$ ./soma
Digite dois números: 3          « a Ana apertou Enter depois do 3…
4                               « …e o scanf continuou esperando o segundo número
3 + 4 = 7
$ ./soma
Digite dois números: 3,4
3 + 0 = 3                       « a vírgula não é espaço: o segundo %d não leu nada

Três regras explicam essas saídas:

  1. %d pula os brancos antes do número. "Brancos" são espaços, tabulações e Enters. Por isso tanto faz digitar 3 4, 3 Enter 4 ou 3 4: o resultado é o mesmo.
  2. Um espaço no formato (o de "%d %d") quer dizer "pule quantos brancos houver, inclusive nenhum". Como o %d já pula brancos sozinho, aqui ele não muda nada; na seção 5 ele vai fazer toda a diferença.
  3. O scanf para no primeiro problema. Em 3,4, o primeiro %d leu o 3 e parou na vírgula. O segundo %d encontrou a vírgula, que não é número nem branco, e desistiu. O b continuou 0.

Outros caracteres no formato

Qualquer outro caractere no formato precisa aparecer igualzinho na entrada. Isso serve para ler uma data com barras:

#include <stdio.h>

int main(void) {
    int dia = 0;
    int mes = 0;
    int ano = 0;
    printf("Data (dia/mês/ano): ");
    int lidos = scanf("%d/%d/%d", &dia, &mes, &ano);
    printf("Li %d valores: dia %d, mês %d, ano %d\n", lidos, dia, mes, ano);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra data.c -o data
$ ./data
Data (dia/mês/ano): 17/10/2026
Li 3 valores: dia 17, mês 10, ano 2026
$ ./data
Data (dia/mês/ano): 17-10-2026
Li 1 valores: dia 17, mês 0, ano 0     « esperava "/" e veio "-": parou depois do dia

O int lidos = scanf(…) é novidade: o scanf devolve um número, e esse número é o assunto da próxima seção. Um detalhe: com "%d/%d/%d", a entrada 17 / 10 / 2026 (com espaços) também lê só o dia, porque a barra do formato tem de vir logo depois do número. Para aceitar espaços, ponha espaços no formato: "%d /%d /%d".


4. O valor de retorno: deu certo?

O scanf devolve quantas lacunas conseguiu preencher. Este programa mostra esse número:

#include <stdio.h>

int main(void) {
    int a = 0;
    int b = 0;
    int lidos = scanf("%d %d", &a, &b);
    printf("scanf devolveu %d (a = %d, b = %d)\n", lidos, a, b);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra retorno.c -o retorno
$ ./retorno
3 4
scanf devolveu 2 (a = 3, b = 4)      « as duas lacunas preenchidas
$ ./retorno
3 x
scanf devolveu 1 (a = 3, b = 0)      « só a primeira
$ ./retorno
x 4
scanf devolveu 0 (a = 0, b = 0)      « nenhuma: parou logo no x
$ ./retorno
scanf devolveu -1 (a = 0, b = 0)     « a Ana apertou Ctrl+D sem digitar nada: fim da entrada
O scanf devolveuSignifica
o número de lacunas (aqui, 2)Tudo lido.
um número menor (1, 0)Parou no meio: veio algo que não combina com o formato. As variáveis que faltaram ficaram como estavam.
EOF (vale -1)End of file, "fim do arquivo": a entrada acabou antes de ler qualquer coisa. No teclado do Linux, é o Ctrl+D; quando a entrada vem de um arquivo, é o fim dele.

A receita de segurança

Um programa de verdade confere esse número antes de usar a variável. A forma mais comum usa um if ("se"), que tem aula própria (if e else). Por enquanto, leia como uma frase: "se o scanf não leu 1 valor, avise e termine o programa".

#include <stdio.h>

int main(void) {
    int idade = 0;
    printf("Quantos anos você tem? ");
    if (scanf("%d", &idade) != 1) {
        printf("Isso não é um número.\n");
        return 1;
    }
    printf("Daqui a 10 anos você terá %d anos.\n", idade + 10);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra checada.c -o checada
$ ./checada
Quantos anos você tem? abc
Isso não é um número.
$ echo $?
1                                    « o código de saída: 1 avisa que deu errado
$ ./checada
Quantos anos você tem? 17
Daqui a 10 anos você terá 27 anos.
if (scanf("%d", &idade) != 1)
Primeiro o scanf roda e lê. Depois o número que ele devolveu é comparado com 1 (!= é "diferente de"; aula Operadores de Comparação). Se for diferente, o bloco entre { } roda.
return 1;
Termina o main ali mesmo, com código de saída 1. Por costume, 0 é "deu certo" e qualquer outro número é "deu errado". O resto do programa não roda.
!= 1
Compare com o número de lacunas: com "%d %d", o certo é != 2. Não use == 0: ele deixaria passar o EOF (-1) e as leituras pela metade.
O gcc pode cobrar isso de você

Com otimização (-O2), o gcc do Ubuntu liga proteções extras da biblioteca C e reclama quando o retorno do scanf é jogado fora:

$ gcc -std=c23 -Wall -Wextra -O2 idade.c -o idade
idade.c: In function ‘main’:
idade.c:6:5: warning: ignoring return value of ‘scanf’ declared with attribute ‘warn_unused_result’ [-Wunused-result]
    6 |     scanf("%d", &idade);
      |     ^~~~~~~~~~~~~~~~~~~

"Ignorando o valor de retorno do scanf, declarado com o atributo warn_unused_result" (avise se o resultado não for usado). Sem -O2, e no editor do guia, esse aviso não aparece: o hábito de conferir tem de ser seu.


5. Letras e números com vírgula: %c, %lf, %f

double é %lf

Aqui o scanf é diferente do printf. No printf, %f serve para double e para float (aula Formatos do printf). No scanf, não: %f é só para float, e double exige %lf (de long float). A Ana errou isso na lojinha:

#include <stdio.h>

int main(void) {
    double preco = 0.0;
    printf("Preço do produto: ");
    scanf("%f", &preco);
    printf("Com desconto: R$ %.2f\n", preco * 0.9);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra preco.c -o preco
preco.c: In function ‘main’:
preco.c:6:13: warning: format ‘%f’ expects argument of type ‘float *’, but argument 2 has type ‘double *’ [-Wformat=]
    6 |     scanf("%f", &preco);
      |            ~^   ~~~~~~
      |             |   |
      |             |   double *
      |             float *
      |            %lf
$ ./preco
Preço do produto: 25.50
Com desconto: R$ 0.00                « 25.50 com 10% de desconto deveria dar 22.95

O %f mandou o scanf gravar um float (4 bytes) numa gaveta de double (8 bytes), no formato do float. Lido como double, aquele padrão de bits vira um número minúsculo, praticamente 0. O gcc avisou e até mostrou o conserto embaixo do código: %lf. O editor do guia dá o mesmo R$ 0.00.

Repare também que a entrada usa ponto: 25.50. O scanf segue o formato americano, como o printf (aula float e double). Com 25,50, ele lê o 25 e para na vírgula.

Missão: conserte o programa da Ana para ele mostrar o desconto certo, sem nenhum aviso. A entrada preparada é 25.50, e a saída esperada é:

Preço do produto: 25.50
Com desconto: R$ 22.95
#include <stdio.h>

int main(void) {
    double preco = 0.0;
    printf("Preço do produto: ");
    scanf("%f", &preco);
    printf("Com desconto: R$ %.2f\n", preco * 0.9);
    return 0;
}
#include <stdio.h>

int main(void) {
    double preco = 0.0;
    printf("Preço do produto: ");
    scanf("%lf", &preco);
    printf("Com desconto: R$ %.2f\n", preco * 0.9);
    return 0;
}

Com float, o %f é o certo. Os dois juntos:

#include <stdio.h>

int main(void) {
    double preco = 0.0;
    float peso = 0.0f;
    printf("Preço do quilo e peso: ");
    scanf("%lf %f", &preco, &peso);
    printf("Total: R$ %.2f\n", preco * peso);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra preco2.c -o preco2
$ ./preco2
Preço do quilo e peso: 25.50 1.5
Total: R$ 38.25

%c e o Enter que sobra

O %c lê um caractere (aula Caracteres). Mas, diferente do %d, ele não pula brancos: um espaço ou um Enter também é um caractere. Veja o que acontece com o pedido de camisetas:

#include <stdio.h>

int main(void) {
    int quantidade = 0;
    char tamanho = '?';
    printf("Quantas camisetas? ");
    scanf("%d", &quantidade);
    printf("Tamanho (P, M ou G)? ");
    scanf("%c", &tamanho);
    printf("Pedido: %d camiseta(s) tamanho [%c]\n", quantidade, tamanho);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra pedido.c -o pedido
$ ./pedido
Quantas camisetas? 3
Tamanho (P, M ou G)? Pedido: 3 camiseta(s) tamanho [      « nem deu tempo de digitar o tamanho!
]
  1. A Ana digitou 3 e apertou Enter. Na entrada ficou: 3 \n (o Enter é o caractere \n; aula Escapes).
  2. O %d leu o 3 e parou antes do \n, que não faz parte do número. O \n sobrou na entrada.
  3. O %c pegou o primeiro caractere que encontrou: o \n que sobrou. Por isso o programa nem esperou, e os colchetes mostram uma quebra de linha.

O conserto é um espaço antes do %c: " %c". Lembra da regra da seção 3? O espaço no formato pula todos os brancos, inclusive o Enter que sobrou:

$ grep '%c"' pedido.c
    scanf(" %c", &tamanho);              « o espaço entre a aspa e o %
$ gcc -std=c23 -Wall -Wextra pedido.c -o pedido
$ ./pedido
Quantas camisetas? 3
Tamanho (P, M ou G)? M
Pedido: 3 camiseta(s) tamanho [M]

Regra prática: quase sempre que for ler um caractere com scanf, escreva " %c". O mesmo Enter que sobra atrapalha o fgets, que lê uma linha inteira; a aula Ler uma Linha mostra o problema e dois consertos.

Missão: a entrada preparada é 3, Enter, M. Conserte o formato do segundo scanf para o pedido sair com o tamanho certo:

Pedido: 3 camiseta(s) tamanho [M]
#include <stdio.h>

int main(void) {
    int quantidade = 0;
    char tamanho = '?';
    printf("Quantas camisetas? ");
    scanf("%d", &quantidade);
    printf("Tamanho (P, M ou G)? ");
    scanf("%c", &tamanho);
    printf("Pedido: %d camiseta(s) tamanho [%c]\n", quantidade, tamanho);
    return 0;
}
#include <stdio.h>

int main(void) {
    int quantidade = 0;
    char tamanho = '?';
    printf("Quantas camisetas? ");
    scanf("%d", &quantidade);
    printf("Tamanho (P, M ou G)? ");
    scanf(" %c", &tamanho);
    printf("Pedido: %d camiseta(s) tamanho [%c]\n", quantidade, tamanho);
    return 0;
}

6. Textos: %s lê uma palavra

Para ler um texto, a variável é um array de char, uma fileira de gavetas de letras (aula Strings). O %s lê uma palavra: pula os brancos do começo e para no primeiro espaço ou Enter.

#include <stdio.h>

int main(void) {
    char nome[20];
    printf("Seu nome: ");
    scanf("%19s", nome);
    printf("Olá, %s!\n", nome);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra nome.c -o nome
$ ./nome
Seu nome: Ana Maria
Olá, Ana!                                  « parou no espaço: o "Maria" ficou sem ler
$ ./nome
Seu nome: Bartolomeu_de_Gusmao_Albuquerque
Olá, Bartolomeu_de_Gusma!                  « só os 19 primeiros caracteres
char nome[20];
Espaço para 20 caracteres. Um deles fica reservado para o '\0', a marca de fim do texto que o C coloca sozinho. Sobram 19 para as letras.
%19s
A largura: "leia no máximo 19 caracteres". Ela protege o array: o que passar disso fica na entrada, sem ler.
nome, sem &
A exceção da regra do &. O nome de um array já é o endereço do começo dele (aula Ponteiros e Arrays). Número e char levam &; array de char, não.

Sem a largura: o estouro

Sem o 19, o %s escreve quantas letras vierem, mesmo que não caibam. Com char nome[8]; e scanf("%s", nome);:

$ gcc -std=c23 -Wall -Wextra estouro.c -o estouro
$ ./estouro
Seu nome: Maximiliano_Bartolomeu_de_Gusmao
Olá, Maximiliano_Bartolomeu_de_Gusmao!
*** stack smashing detected ***: terminated
Abortado                   (imagem do núcleo gravada) ./estouro

Nenhum aviso na compilação. O nome de 32 letras não cabia nas 8 gavetas e o scanf escreveu por cima da memória vizinha. O Ubuntu compila os programas com uma proteção (o stack protector) que percebeu o estrago no fim do main e derrubou o programa: "esmagamento da pilha detectado". Nem sempre há essa sorte: um estouro desse tipo é a origem de muitas falhas de segurança, porque quem digita escolhe o que vai parar na memória. No editor do guia não há essa proteção, e o programa termina normalmente, com a memória estragada sem ninguém ver.

Regra do guia: %s sempre com largura

A largura é o tamanho do array menos 1: char nome[20] → "%19s"; char cidade[50] → "%49s". Para ler um nome com espaços (Ana Maria), use o fgets.


7. %d ou %i?

No printf, %d e %i são iguais. No scanf, o %i entende a base pelo jeito como o número começa, como no código C (aula Inteiros): 0x é hexadecimal e um 0 na frente é octal (base 8).

#include <stdio.h>

int main(void) {
    int d = 0;
    int i = 0;
    scanf("%d %i", &d, &i);
    printf("%%d leu %d e %%i leu %d\n", d, i);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra inteiro.c -o inteiro
$ ./inteiro
010 010
%d leu 10 e %i leu 8           « para o %i, 010 é octal: 1×8 + 0 = 8
$ ./inteiro
25 0x19
%d leu 25 e %i leu 25          « 0x19 em hexadecimal: 1×16 + 9 = 25

Para a idade, o mês ou a quantidade, ninguém quer que 08 dê problema. Use %d; o %i só quando você quer mesmo aceitar 0x….


Letras do scanf × do printf

Tipo da variávelprintfscanfLeva &?
int%d%dsim
unsigned int%u%usim
long / long long%ld / %lld%ld / %lldsim
float%f%fsim
double%f (ou %lf)%lf (%f lê errado)sim
char%c" %c" (com espaço antes)sim
char nome[20]%s%19s (largura = tamanho − 1)não

Formas diferentes de ler: qual usar

FormaExemploQuando usar
scanf sem conferirscanf("%d", &n);Só em testes rápidos. Se a pessoa digitar letras, o programa segue com um valor que ninguém digitou.
scanf conferindo o retornoif (scanf("%d", &n) != 1) { … }O padrão deste guia para ler números nos exercícios.
Uma pergunta por vezdois scanf("%d", …)Mais claro para quem usa o programa: cada pergunta tem o seu prompt.
Vários valores numa linhascanf("%d %lf", &q, &p)Quando os valores andam juntos (quantidade e preço, dia/mês/ano). Confira com != 2, != 3…
Linha inteirafgets(linha, sizeof linha, stdin)Textos com espaços e programas que precisam ser à prova de qualquer entrada: aulas Ler uma Linha e Texto ↔ Número.
getsgets(nome)Nunca. Não tem limite de tamanho; foi removido da linguagem no C11 (aula Ler uma Linha).

Quando dá errado

warning: format ‘%d’ expects argument of type ‘int *’, but argument 2 has type ‘int’

O que significa: faltou o &: o scanf recebeu o valor da variável em vez do endereço. Rodando, o programa cai com Falha de segmentação. No editor (Clang): format specifies type 'int *' but the argument has type 'int'.

Como consertar: scanf("%d", &idade);.

warning: format ‘%f’ expects argument of type ‘float *’, but argument 2 has type ‘double *’

O que significa: a variável é double e o formato é o do float. O valor lido sai errado (no exemplo, 0.00).

Como consertar: %lf para double. O gcc mostra o conserto na última linha do aviso.

O programa não esperou eu digitar a letra

O que significa: um %c depois de outra leitura pegou o Enter que sobrou da anterior.

Como consertar: espaço antes do %c: scanf(" %c", &letra);.

O número saiu 0 (ou um valor que ninguém digitou)

O que significa: a entrada não combinou com o formato (letras no lugar de número, vírgula no lugar de ponto, um - no lugar da /) e o scanf desistiu sem guardar nada. A variável ficou com o valor de antes.

Como consertar: confira o retorno (if (scanf(…) != 1)) e avise a pessoa. Inicialize a variável, para o "valor de antes" nunca ser lixo.

*** stack smashing detected ***: terminated + Abortado

O que significa: um %s sem largura escreveu mais letras do que o array tinha de espaço. O gcc não avisa na compilação.

Como consertar: largura sempre: char nome[20] → scanf("%19s", nome).

Só a primeira palavra do nome foi lida

O que significa: o %s para no primeiro espaço. Não é defeito: é a regra dele.

Como consertar: para ler a linha inteira, use fgets.


Referência rápida

EscritaO que faz
scanf("%d", &n)Lê um int, pulando os brancos antes dele.
scanf("%d %d", &a, &b)Lê dois inteiros, separados por espaços ou Enters.
scanf("%d/%d/%d", …)A / precisa aparecer igualzinha na entrada.
scanf("%lf", &x)Lê um double (com ponto: 25.50).
scanf(" %c", &c)Lê um caractere, pulando os brancos (e o Enter que sobrou).
scanf("%19s", nome)Lê uma palavra de até 19 letras, sem &.
retornoQuantas lacunas foram preenchidas; EOF (-1) se a entrada acabou.
Ctrl+DNo terminal do Linux, encerra a entrada (o scanf devolve EOF).

A tabela completa de formatos está em Referência: printf e scanf.


Exercícios

1. O endereço

Complete para ler um número inteiro na variável pontos:

scanf("%d", );

2. O formato do double

Complete o formato para ler a variável double altura:

scanf("", &altura);

3. A letra depois do número

Complete com o formato inteiro, com as aspas, para ler a resposta s/n depois de um scanf("%d", …), sem pegar o Enter que sobrou:

scanf(, &resposta);

4. Com int a = 0, b = 0; e a entrada 8 x, quanto scanf("%d %d", &a, &b) devolve?

5. Qual leitura está certa para char cidade[30];?

6. A Ana digitou Ana Maria para um scanf("%19s", nome). O que fica em nome?

Missão final: o caixa da lojinha

Missão: o caixa da lojinha da Ana lê, numa linha só, a quantidade (um int) e o preço de cada item (um double), e mostra o total. A entrada preparada é 3 12.50. Escreva o scanf que lê os dois valores e confira o retorno: se ele não for 2, mostre Entrada inválida. e termine com return 1;. Sem avisos. A saída deve terminar com:

Total: R$ 37.50
#include <stdio.h>

int main(void) {
    int quantidade = 0;
    double preco = 0.0;
    printf("Quantidade e preço: ");

    // leia a quantidade e o preço aqui, conferindo o retorno

    printf("Total: R$ %.2f\n", quantidade * preco);
    return 0;
}
#include <stdio.h>

int main(void) {
    int quantidade = 0;
    double preco = 0.0;
    printf("Quantidade e preço: ");

    if (scanf("%d %lf", &quantidade, &preco) != 2) {
        printf("Entrada inválida.\n");
        return 1;
    }

    printf("Total: R$ %.2f\n", quantidade * preco);
    return 0;
}

Depois, troque o que está no campo Entrada da caixa e rode de novo. Com abc, o seu != 2 mostra Entrada inválida.. Com 3 12,50, porém, o total sai R$ 36.00, sem reclamação: o %lf leu o 12, parou na vírgula, e as duas lacunas foram preenchidas. O retorno do scanf pega a entrada errada, mas não a entrada pela metade. Para conferir a linha inteira, existe o caminho do fgets com strtol e strtod.


Resumo

  • scanf("%d", &n) para o programa, espera a pessoa digitar e apertar Enter, e guarda o número em n. Mostre antes uma pergunta com printf.
  • O & entrega o endereço da variável. Sem ele: aviso -Wformat e Falha de segmentação. Arrays de char (%s) não levam &.
  • %d, %lf e %s pulam os brancos antes do valor; o %c não pula, por isso use " %c".
  • No scanf, double é %lf; %f é só para float. Números com ponto: 25.50.
  • O scanf devolve quantas lacunas preencheu (ou EOF, -1). Confira sempre: if (scanf(…) != 1).
  • %s lê uma palavra; sempre com largura ("%19s" para char nome[20]). Para linhas com espaços, fgets.
  • Próximo passo: Operadores Aritméticos, para fazer contas com os valores que você leu.