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(ewNno 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]
| Especificador | Tipo do argumento | Mostra | Exemplo → saída | Aula |
|---|---|---|---|---|
%d, %i | int (e char, short, que viram int) | inteiro com sinal, base 10 | printf("%d", -42) → -42 | Formatos |
%u | unsigned int | inteiro sem sinal, base 10 | printf("%u", 42u) → 42 | Inteiros |
%o | unsigned int | octal (base 8) | printf("%o", 255) → 377 | Formatos |
%x, %X | unsigned int | hexadecimal (base 16), letras minúsculas ou maiúsculas | printf("%x %X", 255, 255) → ff FF | Bits |
%b, %B C23 | unsigned int | binário (base 2) | printf("%b", 10u) → 1010 (só no gcc; veja o aviso abaixo) | Bits |
%f, %F | double (e float, que vira double) | número com vírgula, 6 casas | printf("%f", 3.14159) → 3.141590 | float e double |
%e, %E | double | notação científica (e+00 = × 100) | printf("%e", 3.14159) → 3.141590e+00 | float e double |
%g, %G | double | o mais curto entre %f e %e, sem zeros inúteis no fim | printf("%g", 0.00001234) → 1.234e-05 | Formatos |
%a, %A C99 | double | ponto flutuante em hexadecimal (o valor exato dos bits) | printf("%a", 1.0) → 0x1p+0 | float e double |
%c | int (um char) | um caractere | printf("%c", 'A') → A | char |
%s | char * (uma string terminada em '\0') | o texto | printf("%s", "Ana") → Ana | Strings |
%p | void * (um endereço) | o endereço, em hexadecimal | gcc: 0x7ffd012f7710 (muda a cada execução), NULL sai (nil); editor: 0x7fffec, NULL sai 0 | Memória e Endereços |
%n | int * | não mostra nada: guarda no int quantos caracteres já saíram | printf("Ana%n", &k) → k vale 3 | Segurança |
%% | (nenhum) | o próprio % | printf("10%%") → 10% | Formatos |
%de%isão iguais noprintf. Noscanfnão:%ientende0x1Fcomo hexadecimal e017como octal (veja a tabela do scanf).%gtroca para a notação científica quando o expoente é menor que −4 ou maior ou igual à precisão (6, sem precisão):100000.0sai100000, mas1e20sai1e+20.%fserve parafloatedouble: ao passar porprintf, todofloaté convertido emdouble. Por isso, noprintf,%lfé igual a%f. Noscanfé diferente (veja por quê).%pespera umvoid *: 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 escreveprintf(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.
| Tamanho | Com d i | Com u o x X | Com f e g a | Exemplo → saída |
|---|---|---|---|---|
| (nenhum) | int | unsigned int | double | printf("%d", 42) → 42 |
hh C99 | signed char | unsigned char | (não existe) | printf("%hhu", uc) com uc = 200 → 200 |
h | short | unsigned short | (não existe) | printf("%hd", s) com s = -300 → -300 |
l (L minúsculo) | long | unsigned long | double (o l não muda nada) | printf("%ld", 2000000000L) → 2000000000 |
ll C99 | long long | unsigned long long | (não existe) | printf("%lld", 9000000000000000000LL) → 9000000000000000000 |
j C99 | intmax_t | uintmax_t | (não existe) | printf("%jd", INTMAX_MAX) → 9223372036854775807 |
z C99 | o size_t com sinal | size_t | (não existe) | printf("%zu", sizeof(double)) → 8 |
t C99 | ptrdiff_t | o ptrdiff_t sem sinal | (não existe) | printf("%td", &v[7] - &v[2]) → 5 |
L | (não existe) | (não existe) | long double | printf("%Lf", 2.5L) → 2.500000 (no editor, veja abaixo) |
wN C23 | intN_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
| Macro | Para | Macro | Para |
|---|---|---|---|
PRId8 PRId16 PRId32 PRId64 | int8_t … int64_t em decimal | PRIu8 … PRIu64 | uint8_t … uint64_t |
PRIx8 … PRIx64, PRIX64 | em hexadecimal | PRIdMAX, PRIuMAX | intmax_t, uintmax_t |
SCNd32, SCNd64, SCNu8… | as mesmas, para o scanf | PRIdPTR, PRIuPTR | intptr_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.
| Flag | O que faz | Exemplo → 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] |
0 | Completa 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
| Com | A largura (%8…) | A precisão (%.3…) significa | Exemplo → saída |
|---|---|---|---|
d i u o x X | mínimo de caracteres | mí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 E | mí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 G | mínimo de caracteres | algarismos significativos (padrão 6) | [%.3g] com 1234.5678 → [1.23e+03] |
s | mínimo de caracteres (na verdade, bytes) | máximo de caracteres: corta o texto | [%.3s] com "Banana" → [Ban]; [%10.3s] → [ Ban] |
c p | mínimo de caracteres | (não use) | [%3c] com 'A' → [ A] |
- O arredondamento é "para o par" no empate exato:
printf("%.0f", 2.5)mostra2, não3(e3.5mostra4). 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%-10ssai 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.
| Formato | Argumentos | Saída | Equivale 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 |
%.*s | 3, "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ção | Escreve em | Observação | Aula |
|---|---|---|---|
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 stderr | fprintf(stderr, "Erro: …\n") é o jeito de mostrar erros. | Escrever Arquivos |
snprintf(buf, tam, fmt, …) C99 | uma string | Nunca 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 string | Evite: não sabe o tamanho do buf e pode estourá-lo. | Segurança |
vprintf, vfprintf, vsnprintf | como as de cima | Recebem um va_list em vez dos …: para escrever a sua própria função do tipo printf. | Funções Variádicas |
puts(s) | a tela | Mostra o texto e pula linha. Não interpreta %. | printf |
putchar(c) | a tela | Um 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 oprintfdevolveu 11: ele conta bytes, e oáocupa 2.- O
snprintfcortou 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.
| Formato | Argumento | Lê | Pula espaços antes? |
|---|---|---|---|
%d | int * | inteiro em base 10, com sinal opcional | sim |
%i | int * | inteiro em qualquer base: 0x1F = 31 (hexadecimal), 017 = 15 (octal), 17 = 17 | sim |
%u | unsigned * | inteiro sem sinal | sim |
%o, %x | unsigned * | octal; hexadecimal (com ou sem 0x) | sim |
%f, %e, %g, %a | float * | número com ponto (os quatro são iguais no scanf) | sim |
%lf | double * | número com ponto, para double | sim |
%Lf | long double * | número com ponto, para long double | sim |
%hhd, %hd, %ld, %lld, %zu | signed char *, short *, long *, long long *, size_t * | os mesmos modificadores de tamanho do printf | sim |
%c | char * | um caractere qualquer, inclusive espaço e Enter | não (use " %c") |
%s | char * (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ígitos | nã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]") |
%n | int * | 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 entrada | sim |
O que mais pode ir no formato
| No formato | Efeito | Exemplo |
|---|---|---|
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 caractere | tem de aparecer igualzinho na entrada; senão o scanf para ali | scanf("%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 devolve | Quando |
|---|---|
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) |
0 | o 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
%ileu0x1Fcomo 31; o%*dleu o 99 e jogou fora (por issolidos=2, e não 3); o%xleuff= 255.n=11: o Enter que sobrou da linha anterior (1) mais0x1F 99 ff(10).- As duas últimas leituras devolvem 0: o
abcnão é um número e fica na entrada, travando todas as leituras de%dseguintes. É por isso que um laço comscanfque não confere o retorno pode rodar para sempre. Solução: Validar a Entrada.
printf × scanf: as diferenças que confundem
| Assunto | printf | scanf |
|---|---|---|
| Argumentos | os 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 double | recebe um endereço e precisa saber se ali cabem 4 bytes (float) ou 8 (double) |
%i | igual a %d | entende 0x (hexadecimal) e 0 na frente (octal): 010 vira 8! |
* | largura ou precisão vem de um argumento | lê o campo e descarta |
| Largura | mínima, para alinhar | máxima, para não estourar o array (%19s) |
Precisão (.2) | casas, dígitos ou caracteres | não existe: scanf("%.2f", …) é inválido |
| Espaço no formato | sai um espaço | pula qualquer quantidade de espaços e Enters |
%s | mostra até o '\0' | lê só uma palavra (para no espaço); para a linha toda, %[^\n] ou, melhor, fgets |
%c | mostra um caractere | lê qualquer caractere, até o Enter que sobrou (use " %c") |
| Retorno | bytes escritos | campos 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):
| Linha | gcc 15 | Clang (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’ argument | more '%' conversions than data arguments | A 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 format | invalid 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
%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.
%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.
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.
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). Paraint64_t,PRId64. - Flags:
-esquerda,+sinal, espaço,0zeros,#forma alternativa.*tira largura ou precisão de um argumentoint. - No
scanf: endereços (&),%lfparadouble," %c"para pular o Enter,%19scom largura,%[^\n]para a linha; confira o retorno (campos lidos, 0 ouEOF). - Compile com
-Wall: ele pega formato errado,&esquecido eprintf(texto).