Menu

Especificadores de formato em C: a referência completa de printf e scanf

Todos os especificadores de formato do C em um só lugar - o que %d, %s, %c, %f, %p e os demais significam, os modificadores de tamanho l, ll, h e z, largura, precisão e flags, e quais deles diferem entre printf e scanf.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

Um especificador de formato diz ao printf como transformar um valor em texto, e ao scanf como transformar texto em um valor. É a única parte do C em que o compilador não consegue te ajudar por padrão - os tipos dos argumentos são invisíveis para a função - então esta página é a tabela de consulta.

A forma geral de uma especificação é:

%[flags][largura][.precisao][tamanho]conversao

Só o % e a letra de conversão são obrigatórios. %-8.2lf usa as cinco partes.

A tabela de conversões

EspecificadorSignificadoArgumento no printfArgumento no scanf
%dinteiro decimal com sinalintint *
%iinteiro com sinalint (igual a %d)int * (aceita também 0x, octal com 0)
%udecimal sem sinalunsigned intunsigned int *
%fponto flutuante decimaldoublefloat *
%Fcomo %f, com INF/NAN em maiúsculasdouble-
%e / %Enotação científicadoublefloat *
%g / %Go mais curto entre %f / %edoublefloat *
%a / %Aponto flutuante hexadecimaldoublefloat *
%cum único caractereint (char promovido)char *
%sstringchar * (terminada em '\0')char * (buffer)
%pendereço de ponteirovoid *void **
%x / %Xhexadecimal sem sinalunsigned intunsigned int *
%ooctal sem sinalunsigned intunsigned int *
%nguarda quantos caracteres já foram escritosint *int *
%%um % literalnenhumcasa com um % literal
%[...]scanset (só no scanf)-char *

Duas entradas merecem um aviso. %n escreve na memória em vez de imprimir, e é o mecanismo por trás da vulnerabilidade de string de formato - nunca deixe texto do usuário chegar a uma string de formato. E %p exige um void *; passar um int * diretamente é indefinido, mesmo que todo compilador real imprima o endereço mesmo assim.

Modificadores de tamanho

O modificador de tamanho fica entre a precisão e a letra de conversão, e diz qual é o tamanho real do argumento.

As conversões com sinal (d i) e as sem sinal (u x o) recebem tipos diferentes - %lu quer um unsigned long, não um long, e errar isso é comportamento indefinido, não um deslize cosmético.

ModificadorCom d i (printf / scanf)Com u x o (printf / scanf)
hhint (vindo de signed char) / signed char *unsigned int (vindo de unsigned char) / unsigned char *
hint (vindo de short) / short *unsigned int (vindo de unsigned short) / unsigned short *
(nenhum)int / int *unsigned int / unsigned int *
llong / long *unsigned long / unsigned long *
lllong long / long long *unsigned long long / unsigned long long *
ztipo de tamanho com sinal / ponteiro para elesize_t / size_t *
jintmax_t / intmax_t *uintmax_t / uintmax_t *
tptrdiff_t / ptrdiff_t *contraparte sem sinal de ptrdiff_t

E para as famílias de ponto flutuante e de caracteres:

ModificadorComTipo no printfTipo no scanf
(nenhum)f e g adoublefloat *
lf e g adouble (aceito, sem efeito)double *
Lf e g along doublelong double *
lc swint_t / wchar_t *wchar_t *

As combinações que você de fato digita:

%zu é o que você precisa memorizar. sizeof, strlen e todo tamanho da biblioteca padrão devolvem size_t, que tem 8 bytes em um sistema de 64 bits, enquanto int tem 4. printf("%d", strlen(s)) é um bug real que aparenta funcionar em algumas plataformas - use %zu.

Os modificadores hh e h existem principalmente para o scanf, onde o tamanho do destino importa. No printf um short é promovido a int antes da chamada, então %d o imprimiria corretamente de qualquer forma; %hd apenas documenta a intenção.

Tipos de largura fixa

Para tipos de <stdint.h> como int32_t, os especificadores portáveis são macros de <inttypes.h>:

PRId32 se expande nas letras certas para a sua plataforma, e literais de string adjacentes se concatenam, que é por que o formato está partido em três pedaços. É feio; a alternativa - converter para long long e usar %lld - costuma ser mais legível e é sempre correta.

Flags

As flags vêm logo depois do % e mudam a apresentação. Elas valem só para o printf.

FlagEfeitoExemploSaída
-preenche depois do valor em vez de antes%-6d| com 4242 |
+sempre imprime um sinal para valores com sinal%+d com 42+42
espaçoimprime um espaço onde um + iria% d com 42 42
0preenche com zeros em vez de espaços%06d com 42000042
#forma alternativa: 0x para %x, 0 para %o, mantém o ponto no %g%#x com 2550xff

As flags podem se combinar: %-+8.2f é largura mínima 8, duas casas decimais, sempre com sinal, preenchido depois do número. O - vence o 0 se você escrever os dois.

Largura e precisão

Largura é um tamanho mínimo de campo - a saída é preenchida até alcançá-lo, e nunca é truncada para caber nele. Precisão (depois de um ponto) significa coisas diferentes para cada família:

ConversãoA precisão significa
%f %edígitos depois da vírgula decimal (padrão 6)
%gtotal de dígitos significativos (padrão 6)
%smáximo de caracteres a imprimir
%d %i %u %x %omínimo de dígitos, preenchido com zeros

Um * no lugar de qualquer um dos números o toma de um argumento int, lido antes do próprio valor. É assim que você monta tabelas cujas larguras de coluna são calculadas em tempo de execução.

No scanf a largura tem um papel diferente e bem mais importante: ela limita quanta entrada uma conversão vai consumir, que é a única coisa entre um %s e um estouro de buffer.

Onde printf e scanf divergem

Eles se parecem e discordam em quatro pontos. Cada um deles é uma fonte viva de bugs:

Situaçãoprintfscanf
Valor double%f (float promovido a double)%lf - %f significa float *
Larguratamanho mínimo de campo, preenchemáximo de entrada consumida, trunca
Argumentosvaloresponteiros - &x, ou o nome de um array
Um espaço no formatoimprime um espaçopula qualquer sequência de espaços em branco

Experimente 2.5 ola. O formato que lê e o formato que imprime usam letras diferentes para o mesmo double, e o 15 em %15s limita a entrada a 15 caracteres mais o terminador - exatamente o tamanho de palavra.

O scanset do scanf

%[...] lê um conjunto de caracteres em vez de uma palavra delimitada por espaços. É a forma de ler uma linha inteira com scanf, embora o fgets continue sendo a ferramenta melhor:

[^\n] significa "quaisquer caracteres exceto uma quebra de linha". %[0-9] leria só dígitos, %[abc] só essas três letras. Diferente do %s, um scanset não pula espaços em branco iniciais, então uma quebra de linha perdida no buffer faz com que ele não case com nada.

Errar é comportamento indefinido

O printf não enxerga o tipo real do seu argumento. Ele lê a quantidade de bytes que o especificador implica, interpretada como o especificador manda. Então um descasamento não é uma falha de formatação - é ler memória como a coisa errada:

printf("%d\n", 3.5);        /* imprime lixo: le os bits de um double como int */
printf("%s\n", 42);         /* trata 42 como um endereco - falha */
printf("%d\n", strlen(s));  /* size_t lido como int - errado em 64 bits */
printf("%f\n", 3);          /* int lido como double - lixo */

A defesa custa uma flag. Compile com -Wall e o GCC confere toda string de formato literal contra seus argumentos:

gcc -Wall -Wextra program.c -o program

Ele reporta cada uma das quatro linhas acima em tempo de compilação. Este é o aviso de maior valor em C, e ele está ligado por padrão no editor acima.

Para o lado prático de usar tudo isso - tabelas alinhadas, imprimir floats de forma legível, ler entrada sem o clássico bug da quebra de linha - veja printf e scanf.

Perguntas frequentes

O que %d significa em C?

%d é o especificador de conversão para um inteiro decimal com sinal - um int. No printf ele imprime o argumento como dígitos de base 10; no scanf ele lê dígitos de base 10 para um int *. %i significa a mesma coisa no printf, mas no scanf o %i também aceita hexadecimal 0x e octal com zero à esquerda.

Qual é a diferença entre %f e %lf em C?

No printf não há diferença prática: argumentos float são promovidos a double, então %f imprime os dois, e o C99 aceita %lf como sinônimo. No scanf a diferença é crítica - %f escreve em um float * (4 bytes) e %lf em um double * (8 bytes). Usar %f para um double corrompe memória.

Como imprimir um size_t ou um long long em C?

%zu para size_t (o que sizeof e strlen devolvem) e %lld / %llu para long long / unsigned long long. Usar %d para esses é comportamento indefinido - funciona por acaso em sistemas de 32 bits e se comporta mal nos de 64, que é por que o bug sobrevive tanto tempo.

O que acontece se você usar o especificador de formato errado em C?

É comportamento indefinido, não um erro de arredondamento. O printf lê o argumento como aquilo que o especificador alega, então printf("%d", 3.5) imprime lixo e printf("%s", 42) segue o número 42 como um endereço e normalmente trava. Compile com -Wall, que faz o GCC conferir as strings de formato contra os argumentos.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR