C printf e scanf

Esta é a página de consulta rápida do printf ("print formatted", imprimir formatado) e do scanf ("scan formatted", ler formatado). Aqui estão, em tabelas, todos os formatos, ajustes e armadilhas. Para aprender com calma, passo a passo, veja as aulas Saída com printf, Formatos do printf e Entrada com scanf.

Todas as saídas foram produzidas de verdade: no gcc 15.2 da máquina da Ana (Linux de 64 bits, pasta ~/c/ref, comando gcc -std=c23 -Wall -Wextra) e no editor do guia (Clang em WebAssembly). Quando os dois diferem, a tabela diz.

Atalhos: Anatomia de um formato · Especificadores · Modificadores de tamanho · Flags · Largura e precisão · O * · A família printf · scanf · printf × scanf · Erros comuns · Experimente


Anatomia de um formato

Cada lacuna do printf começa com % e termina com uma letra (o especificador). Entre os dois podem vir, nesta ordem, quatro ajustes opcionais:

%  [flags]  [largura]  [.precisão]  [tamanho]  especificador
%     -        10          .2           l            f        →  %-10.2lf
flags
Zero ou mais destes: - (alinhar à esquerda), + (sempre mostrar o sinal), espaço, # (forma alternativa), 0 (completar com zeros). Tabela.
largura
Número mínimo de caracteres que a lacuna ocupa. Se o valor é maior, ela cresce: a largura nunca corta. * = "a largura vem do próximo argumento".
.precisão
Um ponto seguido de um número. O sentido muda com o tipo: casas decimais no %f, algarismos significativos no %g, dígitos mínimos no %d, caracteres máximos no %s. .* = vem do próximo argumento.
tamanho
Diz o tamanho do valor: hh h l ll j z t L (e wN no C23). Tabela.
especificador
A letra que diz o tipo e a forma de mostrar: d, f, s… Tabela.

Especificadores do printf

Medidos com este programa (os colchetes mostram onde cada lacuna começa e termina):

#include <stdio.h>

int main(void) {
    int n = 255;
    double x = 3.14159;
    printf("[%d] [%i] [%u]\n", -42, -42, 42u);
    printf("[%o] [%x] [%X] [%#o] [%#x] [%#X]\n", n, n, n, n, n, n);
    printf("[%f] [%F] [%e] [%E]\n", x, x, x, x);
    printf("[%g] [%G] [%g] [%g]\n", x, 0.00001234, 1e20, 100000.0);
    printf("[%a] [%A]\n", 1.0, 0.5);
    printf("[%c] [%s] [%%]\n", 'A', "Ana");
    printf("[%b] [%#b] [%B]\n", 10u, 10u, 5u);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra esp.c -o esp
$ ./esp
[-42] [-42] [42]
[377] [ff] [FF] [0377] [0xff] [0XFF]
[3.141590] [3.141590] [3.141590e+00] [3.141590E+00]
[3.14159] [1.234E-05] [1e+20] [100000]
[0x1p+0] [0X1P-1]
[A] [Ana] [%]
[1010] [0b1010] [101]
EspecificadorTipo do argumentoMostraExemplo → saídaAula
%d, %iint (e char, short, que viram int)inteiro com sinal, base 10printf("%d", -42) → -42Formatos
%uunsigned intinteiro sem sinal, base 10printf("%u", 42u) → 42Inteiros
%ounsigned intoctal (base 8)printf("%o", 255) → 377Formatos
%x, %Xunsigned inthexadecimal (base 16), letras minúsculas ou maiúsculasprintf("%x %X", 255, 255) → ff FFBits
%b, %B C23unsigned intbinário (base 2)printf("%b", 10u) → 1010 (só no gcc; veja o aviso abaixo)Bits
%f, %Fdouble (e float, que vira double)número com vírgula, 6 casasprintf("%f", 3.14159) → 3.141590float e double
%e, %Edoublenotação científica (e+00 = × 100)printf("%e", 3.14159) → 3.141590e+00float e double
%g, %Gdoubleo mais curto entre %f e %e, sem zeros inúteis no fimprintf("%g", 0.00001234) → 1.234e-05Formatos
%a, %A C99doubleponto flutuante em hexadecimal (o valor exato dos bits)printf("%a", 1.0) → 0x1p+0float e double
%cint (um char)um caractereprintf("%c", 'A') → Achar
%schar * (uma string terminada em '\0')o textoprintf("%s", "Ana") → AnaStrings
%pvoid * (um endereço)o endereço, em hexadecimalgcc: 0x7ffd012f7710 (muda a cada execução), NULL sai (nil); editor: 0x7fffec, NULL sai 0Memória e Endereços
%nint *não mostra nada: guarda no int quantos caracteres já saíramprintf("Ana%n", &k) → k vale 3Segurança
%%(nenhum)o próprio %printf("10%%") → 10%Formatos
  • %d e %i são iguais no printf. No scanf não: %i entende 0x1F como hexadecimal e 017 como octal (veja a tabela do scanf).
  • %g troca para a notação científica quando o expoente é menor que −4 ou maior ou igual à precisão (6, sem precisão): 100000.0 sai 100000, mas 1e20 sai 1e+20.
  • %f serve para float e double: ao passar por printf, todo float é convertido em double. Por isso, no printf, %lf é igual a %f. No scanf é diferente (veja por quê).
  • %p espera um void *: o jeito certo é printf("%p\n", (void *)&idade).
  • %n é perigoso: um texto vindo do usuário usado como formato pode escrever na memória com ele. É por isso que nunca se escreve printf(texto) (veja Erros comuns).
%b não funciona no editor do guia

O %b é novo (C23). A glibc do Linux já o conhece, mas a biblioteca C do editor (wasi-libc), não: ali, a linha printf("[%b] [%#b] [%B]\n", …) não mostra nada (o printf desiste da linha inteira) e o Clang não avisa. Para mostrar binário no editor, use o laço da aula Operadores de Bits.

Modificadores de tamanho

O modificador vem logo antes da letra e diz o tamanho do valor. Sem ele, %d espera um int. Com l, um long; com ll, um long long; e assim por diante.

TamanhoCom d iCom u o x XCom f e g aExemplo → saída
(nenhum)intunsigned intdoubleprintf("%d", 42) → 42
hh C99signed charunsigned char(não existe)printf("%hhu", uc) com uc = 200 → 200
hshortunsigned short(não existe)printf("%hd", s) com s = -300 → -300
l (L minúsculo)longunsigned longdouble (o l não muda nada)printf("%ld", 2000000000L) → 2000000000
ll C99long longunsigned long long(não existe)printf("%lld", 9000000000000000000LL) → 9000000000000000000
j C99intmax_tuintmax_t(não existe)printf("%jd", INTMAX_MAX) → 9223372036854775807
z C99o size_t com sinalsize_t(não existe)printf("%zu", sizeof(double)) → 8
t C99ptrdiff_to ptrdiff_t sem sinal(não existe)printf("%td", &v[7] - &v[2]) → 5
L(não existe)(não existe)long doubleprintf("%Lf", 2.5L) → 2.500000 (no editor, veja abaixo)
wN C23intN_t (%w32d)uintN_t(não existe)printf("%w32d", (int32_t)7) → 7 no gcc; o Clang do editor não conhece

O modificador também vale para o %n: %hhn guarda num signed char, %ln num long, e assim por diante.

%Lf (long double) no editor

No editor, um printf("%Lf", …) encerra o programa com esta mensagem (é o padrão da biblioteca C do WebAssembly, para deixar o programa menor):

Support for formatting long double values is currently disabled.
To enable it, add -lc-printscan-long-double to the link command.
Abortado (imagem do núcleo gravada)

Para usar long double no editor, acrescente a opção que a mensagem pede: data-flags="-std=c23 -Wall -Wextra -lc-printscan-long-double". No gcc, %Lf funciona sem nada.

Tipos de tamanho fixo: inttypes.h

Para int64_t, uint8_t etc. (aula stdint.h), o formato certo muda de sistema para sistema (int64_t é long no Linux de 64 bits e long long no editor). A solução portátil são as macros do <inttypes.h>, que viram o texto certo e se "colam" ao resto do formato:

#include <inttypes.h>
int64_t populacao = 123456789012;
printf("População: %" PRId64 "\n", populacao);   // vira "%ld" no gcc e "%lld" no editor
MacroParaMacroPara
PRId8 PRId16 PRId32 PRId64int8_t … int64_t em decimalPRIu8 … PRIu64uint8_t … uint64_t
PRIx8 … PRIx64, PRIX64em hexadecimalPRIdMAX, PRIuMAXintmax_t, uintmax_t
SCNd32, SCNd64, SCNu8…as mesmas, para o scanfPRIdPTR, PRIuPTRintptr_t, uintptr_t

Flags

#include <stdio.h>

int main(void) {
    printf("[%5d] [%-5d] [%05d] [%+d] [% d] [%+d]\n", 42, 42, 42, 42, 42, -42);
    printf("[%8.3f] [%-8.2f] [%08.2f] [%+.1f] [%.0f] [%#.0f]\n", 3.14159, 3.14159, 3.14159, 3.14159, 2.5, 2.0);
    printf("[%.5d] [%8.5d] [%.0d] [%.3s] [%10.3s] [%-10s]\n", 42, 42, 0, "Banana", "Banana", "Ana");
    printf("[%.3e] [%.3g] [%#g] [%g]\n", 1234.5678, 1234.5678, 1.5, 1.5);
    int larg = 6, prec = 2;
    printf("[%*d] [%-*d] [%.*f] [%*.*f]\n", larg, 7, larg, 7, prec, 3.14159, larg, prec, 3.14159);
    printf("[%*d]\n", -6, 7);
    printf("[%#x] [%#o] [%#x]\n", 0, 0, 255);
    printf("[%-+6d] [%+05d]\n", 7, 7);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra flags.c -o flags
$ ./flags
[   42] [42   ] [00042] [+42] [ 42] [-42]
[   3.142] [3.14    ] [00003.14] [+3.1] [2] [2.]
[00042] [   00042] [] [Ban] [       Ban] [Ana       ]
[1.235e+03] [1.23e+03] [1.50000] [1.5]
[     7] [7     ] [3.14] [  3.14]
[7     ]
[0] [0] [0xff]
[+7    ] [+0007]

A saída do editor é idêntica, caractere por caractere.

FlagO que fazExemplo → saída
-Alinha à esquerda dentro da largura (o padrão é à direita).[%-5d] com 42 → [42 ]
+Mostra o sinal sempre, também o + dos positivos.[%+d] com 42 → [+42]
(espaço)Põe um espaço no lugar do sinal dos positivos (alinha com os negativos). Se houver +, o espaço é ignorado.[% d] com 42 → [ 42]
0Completa a largura com zeros à esquerda (em vez de espaços). Ignorado com -, e nos inteiros quando há precisão.[%05d] com 42 → [00042]
#Forma alternativa: 0 antes do octal, 0x/0X antes do hexadecimal (mas não no zero), 0b no binário; no %f/%e, ponto mesmo sem casas; no %g, mantém os zeros do fim.[%#x] com 255 → [0xff]; [%#.0f] com 2.0 → [2.]; [%#g] com 1.5 → [1.50000]

As flags podem ser combinadas, em qualquer ordem: %-+6d → [+7 ], %+05d → [+0007].

Largura e precisão

ComA largura (%8…)A precisão (%.3…) significaExemplo → saída
d i u o x Xmínimo de caracteresmínimo de dígitos (completa com zeros). Com .0, o valor 0 não mostra nada.[%8.5d] com 42 → [ 00042]; [%.0d] com 0 → []
f F e Emínimo de caracteres (contando o ponto e o sinal)casas decimais (padrão 6). Arredonda.[%8.3f] com 3.14159 → [ 3.142]; [%.3e] com 1234.5678 → [1.235e+03]
g Gmínimo de caracteresalgarismos significativos (padrão 6)[%.3g] com 1234.5678 → [1.23e+03]
smínimo de caracteres (na verdade, bytes)máximo de caracteres: corta o texto[%.3s] com "Banana" → [Ban]; [%10.3s] → [ Ban]
c pmínimo de caracteres(não use)[%3c] com 'A' → [ A]
  • O arredondamento é "para o par" no empate exato: printf("%.0f", 2.5) mostra 2, não 3 (e 3.5 mostra 4). Nos outros casos, arredonda para o mais próximo.
  • A largura conta bytes, não letras: em UTF-8, ã e ç ocupam 2 bytes, e uma coluna com %-10s sai torta. Veja Acentos e UTF-8.

O *: largura e precisão em variáveis

Um * no lugar do número diz "pegue a largura (ou a precisão) do próximo argumento, que é um int". Os argumentos vêm na ordem em que aparecem: largura, precisão, valor.

FormatoArgumentosSaídaEquivale a
[%*d]6, 7[ 7]%6d
[%-*d]6, 7[7 ]%-6d
[%*d]-6, 7[7 ]largura negativa = flag -
[%.*f]2, 3.14159[3.14]%.2f
[%*.*f]6, 2, 3.14159[ 3.14]%6.2f
%.*s3, "Banana"Ban%.3s (útil para imprimir só um pedaço de uma string)

Atenção: o argumento do * tem de ser int. Um sizeof ou strlen (que são size_t) precisa de conversão: printf("%.*s", (int)strlen(t), t).

A família printf e o valor de retorno

FunçãoEscreve emObservaçãoAula
printf(fmt, …)a tela (stdout)Devolve quantos bytes escreveu, ou um número negativo se deu erro.printf
fprintf(arq, fmt, …)um arquivo (FILE *), ou stderrfprintf(stderr, "Erro: …\n") é o jeito de mostrar erros.Escrever Arquivos
snprintf(buf, tam, fmt, …) C99uma stringNunca passa de tam bytes (contando o '\0'). Devolve quantos bytes precisaria: se for >= tam, o texto foi cortado.Texto ↔ Número
sprintf(buf, fmt, …)uma stringEvite: não sabe o tamanho do buf e pode estourá-lo.Segurança
vprintf, vfprintf, vsnprintfcomo as de cimaRecebem um va_list em vez dos …: para escrever a sua própria função do tipo printf.Funções Variádicas
puts(s)a telaMostra o texto e pula linha. Não interpreta %.printf
putchar(c)a telaUm caractere.char
#include <stdio.h>

int main(void) {
    int n = printf("Olá, Ana!\n");
    printf("printf devolveu %d\n", n);
    char curto[8];
    int precisa = snprintf(curto, sizeof curto, "Total: R$ %.2f", 1234.5);
    printf("[%s] precisava de %d caracteres\n", curto, precisa);
    int x;
    int r = scanf("%d", &x);
    printf("scanf devolveu %d (EOF = %d)\n", r, EOF);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra retorno.c -o retorno
retorno.c: In function ‘main’:
retorno.c:7:58: warning: ‘Total: R$ ’ directive output truncated writing 10 bytes into a region of size 8 [-Wformat-truncation=]
    7 |     int precisa = snprintf(curto, sizeof curto, "Total: R$ %.2f", 1234.5);
      |                                                  ~~~~~~~~^~
retorno.c:7:19: note: ‘snprintf’ output 18 bytes into a destination of size 8
    7 |     int precisa = snprintf(curto, sizeof curto, "Total: R$ %.2f", 1234.5);
      |                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
$ ./retorno < /dev/null                       « entrada vazia: o scanf encontra o fim do arquivo
Olá, Ana!
printf devolveu 11
[Total: ] precisava de 17 caracteres
scanf devolveu -1 (EOF = -1)
  • "Olá, Ana!\n" tem 10 letras, mas o printf devolveu 11: ele conta bytes, e o á ocupa 2.
  • O snprintf cortou o texto em 7 caracteres + '\0' e avisou, pelo retorno, que precisava de 17. O gcc percebeu já na compilação (-Wformat-truncation, que vem com o -Wall); o Clang do editor não avisa.

scanf: a tabela

O scanf usa quase as mesmas letras, mas ao contrário: em vez de receber valores, recebe endereços de variáveis (por isso o &) e as preenche com o que o usuário digitou. Aula: Entrada com scanf.

FormatoArgumentoLêPula espaços antes?
%dint *inteiro em base 10, com sinal opcionalsim
%iint *inteiro em qualquer base: 0x1F = 31 (hexadecimal), 017 = 15 (octal), 17 = 17sim
%uunsigned *inteiro sem sinalsim
%o, %xunsigned *octal; hexadecimal (com ou sem 0x)sim
%f, %e, %g, %afloat *número com ponto (os quatro são iguais no scanf)sim
%lfdouble *número com ponto, para doublesim
%Lflong double *número com ponto, para long doublesim
%hhd, %hd, %ld, %lld, %zusigned char *, short *, long *, long long *, size_t *os mesmos modificadores de tamanho do printfsim
%cchar *um caractere qualquer, inclusive espaço e Enternão (use " %c")
%schar * (um array)uma palavra: para no primeiro espaço ou Enter. Sempre com largura: %19s num char nome[20]sim
%[abc]char *só os caracteres da lista (um "conjunto"): %[0-9] lê só dígitosnão
%[^\n]char *tudo até o Enter (o ^ = "menos estes"): uma linha com espaços. Use com largura: %39[^\n]não (use " %39[^\n]")
%nint *não lê nada: guarda quantos caracteres foram consumidos até ali. Não conta no retorno.não
%*d, %*s…(nenhum)lê e descarta (o * no scanf = "pule este campo"). Não conta no retorno.sim
%%(nenhum)espera um % na entradasim

O que mais pode ir no formato

No formatoEfeitoExemplo
um espaço (ou \n, \t)pula todos os espaços, Enters e tabs que houver na entrada (também nenhum)scanf(" %c", &letra) pula o Enter que sobrou da leitura anterior
outro caracteretem de aparecer igualzinho na entrada; senão o scanf para aliscanf("%d/%d/%d", &d, &m, &a) lê 17/10/2026
um número (largura)máximo de caracteres lidos naquele campo%19s, %39[^\n], %2d

O valor de retorno

scanf devolveQuando
o número de campos preenchidos (1, 2…)leu tudo, ou parte (se devolveu menos que o esperado, parou num campo que não combinou)
0o primeiro campo não combinou (por exemplo, abc para um %d). O texto que não combinou continua na entrada.
EOF (−1)a entrada acabou antes do primeiro campo (Ctrl+D no terminal, arquivo vazio)

Todos esses casos num programa só:

#include <stdio.h>

int main(void) {
    int idade = 0;
    double altura = 0.0;
    char nome[20] = "";
    char cidade[40] = "";
    char letra = '?';
    int lidos = scanf("%d %lf", &idade, &altura);
    printf("lidos=%d idade=%d altura=%.2f\n", lidos, idade, altura);
    lidos = scanf(" %c", &letra);
    printf("lidos=%d letra=%c\n", lidos, letra);
    lidos = scanf("%19s", nome);
    printf("lidos=%d nome=%s\n", lidos, nome);
    lidos = scanf(" %39[^\n]", cidade);
    printf("lidos=%d cidade=%s\n", lidos, cidade);
    int d, m, a;
    lidos = scanf("%d/%d/%d", &d, &m, &a);
    printf("lidos=%d data=%02d/%02d/%d\n", lidos, d, m, a);
    int h = 0, n = 0;
    lidos = scanf("%i %*d %x%n", &h, &d, &n);
    printf("lidos=%d h=%d d=%d n=%d\n", lidos, h, d, n);
    lidos = scanf("%d", &idade);
    printf("lidos=%d\n", lidos);
    lidos = scanf("%d", &idade);
    printf("lidos=%d\n", lidos);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra leitura.c -o leitura
$ printf '29 1.65\nA\nAna\nSão Paulo - SP\n17/10/2026\n0x1F 99 ff\nabc\n' | ./leitura
lidos=2 idade=29 altura=1.65
lidos=1 letra=A
lidos=1 nome=Ana
lidos=1 cidade=São Paulo - SP
lidos=3 data=17/10/2026
lidos=2 h=31 d=255 n=11
lidos=0
lidos=0
  • %i leu 0x1F como 31; o %*d leu o 99 e jogou fora (por isso lidos=2, e não 3); o %x leu ff = 255.
  • n=11: o Enter que sobrou da linha anterior (1) mais 0x1F 99 ff (10).
  • As duas últimas leituras devolvem 0: o abc não é um número e fica na entrada, travando todas as leituras de %d seguintes. É por isso que um laço com scanf que não confere o retorno pode rodar para sempre. Solução: Validar a Entrada.

printf × scanf: as diferenças que confundem

Assuntoprintfscanf
Argumentosos valores: printf("%d", idade)os endereços: scanf("%d", &idade). Num array de char, sem &: scanf("%19s", nome)
double%f (ou %lf, que é igual)%lf. %f é para float: com um double, guarda lixo
Por quê?todo float vira double ao ser passado: o printf só recebe doublerecebe um endereço e precisa saber se ali cabem 4 bytes (float) ou 8 (double)
%iigual a %dentende 0x (hexadecimal) e 0 na frente (octal): 010 vira 8!
*largura ou precisão vem de um argumentolê o campo e descarta
Larguramínima, para alinharmáxima, para não estourar o array (%19s)
Precisão (.2)casas, dígitos ou caracteresnão existe: scanf("%.2f", …) é inválido
Espaço no formatosai um espaçopula qualquer quantidade de espaços e Enters
%smostra até o '\0'lê só uma palavra (para no espaço); para a linha toda, %[^\n] ou, melhor, fgets
%cmostra um caracterelê qualquer caractere, até o Enter que sobrou (use " %c")
Retornobytes escritoscampos preenchidos, 0, ou EOF

Erros comuns

Este programa reúne os enganos mais frequentes. O -Wall pega quase todos:

#include <stdio.h>

int main(void) {
    int idade = 29;
    double preco = 4.5;
    char texto[20] = "100% certo";
    printf("%d\n", preco);
    printf("%f\n", idade);
    printf("%d %d\n", idade);
    printf("%lu\n", sizeof(int));
    printf("%s\n", 'A');
    printf(texto);
    printf("\n50%\n");
    scanf("%d", idade);
    scanf("%f", &preco);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra errado.c -o errado
errado.c: In function ‘main’:
errado.c:7:14: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘double’ [-Wformat=]
    7 |     printf("%d\n", preco);
      |             ~^     ~~~~~
      |              |     |
      |              int   double
      |             %f
errado.c:8:14: warning: format ‘%f’ expects argument of type ‘double’, but argument 2 has type ‘int’ [-Wformat=]
    8 |     printf("%f\n", idade);
      |             ~^     ~~~~~
      |              |     |
      |              |     int
      |              double
      |             %d
errado.c:9:17: warning: format ‘%d’ expects a matching ‘int’ argument [-Wformat=]
    9 |     printf("%d %d\n", idade);
      |                ~^
      |                 |
      |                 int
errado.c:11:14: warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘int’ [-Wformat=]
   11 |     printf("%s\n", 'A');
      |             ~^     ~~~
      |              |     |
      |              |     int
      |              char *
      |             %d
errado.c:12:12: warning: format not a string literal and no format arguments [-Wformat-security]
   12 |     printf(texto);
      |            ^~~~~
errado.c:13:18: warning: unknown conversion type character ‘\x0a’ in format [-Wformat=]
   13 |     printf("\n50%\n");
      |                  ^~
errado.c:14:13: warning: format ‘%d’ expects argument of type ‘int *’, but argument 2 has type ‘int’ [-Wformat=]
   14 |     scanf("%d", idade);
      |            ~^   ~~~~~
      |             |   |
      |             |   int
      |             int *
errado.c:15:13: warning: format ‘%f’ expects argument of type ‘float *’, but argument 2 has type ‘double *’ [-Wformat=]
   15 |     scanf("%f", &preco);
      |            ~^   ~~~~~~
      |             |   |
      |             |   double *
      |             float *
      |            %lf

A mesma coisa, aviso por aviso, com a versão do Clang (o compilador do editor):

Linhagcc 15Clang (editor)O que acontece e como consertar
printf("%d\n", preco);format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘double’format specifies type 'int' but the argument has type 'double'Mostra lixo (no gcc, um número diferente a cada execução: -1387403448, -710790344…; no editor, 0). Use %f, ou converta: (int)preco.
printf("%f\n", idade);format ‘%f’ expects argument of type ‘double’, but argument 2 has type ‘int’format specifies type 'double' but the argument has type 'int'No gcc mostrou 4.500000 (o valor de preco, que estava "à mão"!); no editor, 0.000000. O printf não converte: use %d ou (double)idade.
printf("%d %d\n", idade);format ‘%d’ expects a matching ‘int’ argumentmore '%' conversions than data argumentsA segunda lacuna mostra lixo (29 -67866608). Uma lacuna para cada valor.
printf("%lu\n", sizeof(int));(nenhum aviso)(nenhum aviso)Funciona nos dois, mas por sorte: size_t é unsigned long no Linux e no editor; no Windows de 64 bits, não. O certo é %zu.
printf("%s\n", 'A');format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘int’format specifies type 'char *' but the argument has type 'char'No gcc, Falha de segmentação (o 65 vira um endereço inválido); no editor, uma linha vazia. Para um caractere, %c.
printf(texto);format not a string literal and no format arguments [-Wformat-security]format string is not a string literal (potentially insecure) [-Wformat-security]O % de "100% certo" vira formato: o editor mostrou 100 erto (o % c engoliu o c). Com texto do usuário, é uma falha de segurança. Use printf("%s", texto) ou fputs(texto, stdout).
printf("\n50%\n");unknown conversion type character ‘\x0a’ in formatinvalid conversion specifier '\x0a'Um % sozinho abre uma lacuna (aqui, com o Enter, \x0a, como "letra"). Para mostrar o %, escreva %%.
scanf("%d", idade);format ‘%d’ expects argument of type ‘int *’, but argument 2 has type ‘int’format specifies type 'int *' but the argument has type 'int'Faltou o &: o scanf escreve no "endereço" 29 e o programa cai com Falha de segmentação. Escreva &idade.
scanf("%f", &preco);format ‘%f’ expects argument of type ‘float *’, but argument 2 has type ‘double *’format specifies type 'float *' but the argument has type 'double *'O scanf guarda um float (4 bytes) num double (8): o valor sai errado. Para double, %lf.

Erros que o compilador não pega

O %c "pulou" a pergunta
#include <stdio.h>

int main(void) {
    int qtd = 0;
    char tam = '?';
    char nome[20] = "";
    printf("Quantidade: ");
    scanf("%d", &qtd);
    printf("Tamanho (P/M/G): ");
    scanf("%c", &tam);
    printf("\nqtd=%d tam=%d\n", qtd, tam);
    printf("Nome completo: ");
    scanf("%19s", nome);
    printf("nome=[%s]\n", nome);
    return 0;
}
$ ./enter
Quantidade: 3                                 « a Ana digita 3 e Enter
Tamanho (P/M/G):
qtd=3 tam=10                                  « nem esperou: o %c leu o Enter (código 10)
Nome completo: M                              « a Ana digita M, achando que é o tamanho
nome=[M]

O que significa: o %d leu o 3 e deixou o Enter na entrada. O %c lê qualquer caractere, inclusive esse Enter.

Como consertar: um espaço antes: scanf(" %c", &tam). O espaço pula os Enters e espaços que sobraram.

O %s só leu o primeiro nome

Com a entrada Ana Souza, scanf("%19s", nome) guarda só Ana; o Souza fica para a próxima leitura.

Como consertar: para uma linha inteira, scanf(" %19[^\n]", nome) ou, melhor, fgets(nome, sizeof nome, stdin) (aula fgets).

%s sem largura: estouro do array

scanf("%s", nome) num char nome[20] aceita uma palavra de 100 letras e escreve além do fim do array, sem nenhum aviso do compilador. No gcc, o programa costuma morrer no fim com *** stack smashing detected ***: terminated (veja Erros Comuns).

Como consertar: sempre %19s (o tamanho do array menos 1, para o '\0'). Aula: Segurança.

O laço com scanf não para nunca

Se o usuário digita abc num %d, o scanf devolve 0 e deixa o abc na entrada. A próxima volta do laço tenta ler o mesmo abc… para sempre.

Como consertar: confira o retorno (if (scanf("%d", &n) != 1)) e descarte a linha ruim (while ((c = getchar()) != '\n' && c != EOF) {}). Aula: Validar a Entrada.

Vírgula ou ponto?

O scanf("%lf") espera ponto: 1.65. Com 1,65, ele lê só o 1 e para na vírgula. O mesmo vale para a saída: %.2f mostra 1.65. Só se o programa chamar setlocale(LC_ALL, "") num sistema em português é que o gcc passa a usar vírgula (3,14); no editor, o setlocale não muda nada.


Experimente

Um laboratório de formatos: troque os formatos e os valores e veja o resultado. Os colchetes mostram onde cada lacuna começa e termina, assim dá para ver os espaços.

Experimente: mude as larguras, as precisões e as flags (-, +, 0, #) e clique em Rodar. Tente prever a saída antes.

#include <stdio.h>

int main(void) {
    int qtd = 7;
    double preco = 12.9;
    const char *item = "Caderno";

    printf("[%d] [%5d] [%-5d] [%05d] [%+d]\n", qtd, qtd, qtd, qtd, qtd);
    printf("[%f] [%.2f] [%8.2f] [%-8.1f] [%e] [%g]\n", preco, preco, preco, preco, preco, preco);
    printf("[%s] [%10s] [%-10s] [%.3s]\n", item, item, item, item);
    printf("[%x] [%X] [%#x] [%o] [%c]\n", 255, 255, 255, 8, 'A');

    int largura = 12;
    printf("[%*s] [%-*s]\n", largura, item, largura, item);
    printf("%zu bytes num double; 100%% pronto\n", sizeof preco);
    return 0;
}

Missão: o programa lê a altura da Ana (a entrada já vem preenchida com 1.65), mas mostra um valor errado, e o compilador avisa. Conserte o formato do scanf para que a saída seja Altura: 1.65 m, sem nenhum aviso.

#include <stdio.h>

int main(void) {
    double altura = 0.0;
    printf("Sua altura: ");
    if (scanf("%f", &altura) != 1) {
        printf("Isso não é um número.\n");
        return 1;
    }
    printf("Altura: %.2f m\n", altura);
    return 0;
}
#include <stdio.h>

int main(void) {
    double altura = 0.0;
    printf("Sua altura: ");
    if (scanf("%lf", &altura) != 1) {
        printf("Isso não é um número.\n");
        return 1;
    }
    printf("Altura: %.2f m\n", altura);
    return 0;
}

Resumo

  • Formato: %[flags][largura][.precisão][tamanho]letra, nesta ordem. Exemplo: %-10.2lf.
  • Letras: d i u o x X (inteiros), f e g a (ponto flutuante), c, s, p, %%. %b (binário, C23) só funciona no gcc.
  • Tamanhos: hh (char), h (short), l (long), ll (long long), z (size_t), t (ptrdiff_t), j (intmax_t), L (long double). Para int64_t, PRId64.
  • Flags: - esquerda, + sinal, espaço, 0 zeros, # forma alternativa. * tira largura ou precisão de um argumento int.
  • No scanf: endereços (&), %lf para double, " %c" para pular o Enter, %19s com largura, %[^\n] para a linha; confira o retorno (campos lidos, 0 ou EOF).
  • Compile com -Wall: ele pega formato errado, & esquecido e printf(texto).