printf é a primeira função que todo programador C aprende e aquela cujo manual a maioria nunca leu até o fim. Vale dez minutos, porque a string de formato dele é uma pequena linguagem à parte - e porque errar um especificador não é um erro de digitação, é comportamento indefinido.
Ele mora em <stdio.h>, então todo programa que o usa começa com #include <stdio.h>.
O modelo da string de formato
O printf percorre a string de formato um caractere por vez. Tudo que não é % é impresso exatamente como escrito. Um % inicia uma especificação de conversão, que consome o próximo argumento e o imprime no formato pedido.
Os argumentos são casados com os especificadores estritamente por posição: o primeiro % pega count, o segundo pega name, e assim por diante. O printf não tem como conferir se você passou o que prometeu - ele não enxerga os tipos - então a string de formato é um contrato que cabe a você cumprir. É isso que torna o -Wall digno de ser ligado: o GCC lê a string de formato literal em tempo de compilação e avisa quando um argumento não bate.
Nada força uma quebra de linha. printf("Ola") deixa o cursor depois do a, e é por isso que quase toda string de formato termina com \n.
Os especificadores do dia a dia
Cinco deles cobrem a maior parte do código:
| Especificador | Imprime | Chamada de exemplo | Saída |
|---|---|---|---|
%d | um int com sinal | printf("%d", -42) | -42 |
%f | um double | printf("%f", 2.5) | 2.500000 |
%s | uma string (char *) | printf("%s", "oi") | oi |
%c | um único caractere | printf("%c", 'A') | A |
%p | o endereço de um ponteiro | printf("%p", (void*)&x) | 0x7ffd... |
Duas regras se escondem nesse trecho. Um % literal se escreve %% - um % sozinho no fim de uma string de formato é comportamento indefinido. E %p exige um void *, então converta o ponteiro; passar um int * direto é tecnicamente indefinido, mesmo que na prática imprima bem.
A tabela completa - todos os especificadores, todos os modificadores de comprimento, a coluna do scanf - está na página de especificadores de formato. Esta página fica nos que você usa toda hora.
Largura, precisão e alinhamento
Entre o % e a letra você pode colocar flags, uma largura e uma precisão. É assim que um laço vira uma tabela.
A largura é um mínimo, nunca um máximo: %3d com o valor 123456 imprime os seis dígitos. A precisão significa coisas diferentes por tipo - dígitos depois do ponto decimal para %f, máximo de caracteres para %s, mínimo de dígitos para %d.
As duas podem ser fornecidas em tempo de execução com um *, que lê um argumento int:
Mudar width reorganiza a tabela inteira. Essa é a maneira padrão de imprimir colunas alinhadas em C - sem precisar de nenhuma função auxiliar de preenchimento de string.
Imprimindo floats com bom senso
%f sem precisão sempre imprime seis dígitos depois do ponto, o que raramente é o que alguém quer:
Três escolhas, e uma regra prática para cada uma:
%.Nfpara dinheiro, medidas, qualquer coisa com um número natural de casas decimais.%.2fé o que você quer para moeda.%epara notação científica, sempred.dddddde±dd.%gquando você não conhece a ordem de grandeza de antemão. Ele escolhe%fou%e, o que for mais curto, e tira os zeros à direita - o padrão mais sensato para registrar um valor que você ainda não viu.
Uma armadilha que vale dizer com todas as letras: não existe %lf no printf. Argumentos float são promovidos a double antes de o printf sequer vê-los, então %f dá conta dos dois, e %lf só faz sentido no scanf, onde o tamanho do destino importa. (O C99 até aceita %lf no printf como sinônimo, mas o hábito de escrevê-lo leva as pessoas a escrever %f no scanf, o que aí sim quebra de verdade.)
Inteiros de outros tamanhos
%d é para int. Tipos maiores ou sem sinal precisam de um modificador de comprimento, e errar isso é comportamento indefinido, não um erro de arredondamento:
%zu para size_t é o que as pessoas mais erram - sizeof devolve size_t, não int, então printf("%d", sizeof x) é um bug que por acaso funciona em sistemas de 32 bits e se comporta mal nos de 64.
O valor de retorno
O printf devolve o número de caracteres que escreveu, ou um número negativo em caso de falha. Ele costuma ser ignorado, e na maior parte do tempo tudo bem - mas é genuinamente útil quando você precisa saber qual foi a largura da saída:
As parentes sprintf e snprintf escrevem num buffer em vez de na tela, e devolvem o comprimento que produziram. Prefira sempre o snprintf, que recebe o tamanho do buffer e não pode estourá-lo:
O snprintf devolve o comprimento que a string completa teria precisado, então um valor de retorno igual ou maior que o tamanho do buffer avisa que a saída foi truncada.
A vulnerabilidade de format string
Esse é o único problema de segurança do printf, e ele é grave. Nunca passe como string de formato dados que você não escreveu:
/* PERIGOSO - nunca faca isso */
printf(entrada_do_usuario);
/* Correto */
printf("%s", entrada_do_usuario);
Se entrada_do_usuario contiver %s, o printf lê um argumento que nunca foi passado e segue qualquer lixo em forma de ponteiro que encontrar, geralmente travando. %x repetido despeja o conteúdo da pilha na tela, o que pode incluir senhas ou chaves. E %n, que escreve a contagem de caracteres num argumento, historicamente transformou isso em corrupção arbitrária de memória e execução remota de código.
A regra é absoluta e não custa nada: a string de formato é sempre um literal escrito por você. Tudo que for variável entra como argumento. O -Wformat-security do GCC sinaliza o erro, e ele já vem incluído em -Wall -Wextra.
Bufferização: por que a saída às vezes aparece atrasada
O printf não escreve no terminal imediatamente. A saída é bufferizada - descarregada quando o buffer enche, quando aparece uma quebra de linha num terminal, ou quando o programa termina normalmente. Duas consequências:
- Um programa que trava pode perder saída que "já foi impressa". Se você está depurando com
printfe a última mensagem nunca aparece, o travamento aconteceu depois daquela linha, não antes. Acrescentefflush(stdout)ou mande a saída de depuração parastderr, que não é bufferizada. fprintf(stderr, ...)é a forma certa de imprimir erros. Ela não é bufferizada e vai para um fluxo separado que os usuários podem redirecionar independentemente da saída normal.
Perguntas frequentes
Como o printf funciona em C?
O printf recebe uma string de formato e a percorre caractere por caractere. Os caracteres comuns são impressos como estão; cada % inicia uma especificação de conversão que consome um dos argumentos seguintes e o imprime no formato pedido. printf("%d macas\n", 5) imprime 5 macas e uma quebra de linha.
Como imprimir um float com 2 casas decimais em C?
Use uma precisão: printf("%.2f\n", 3.14159) imprime 3.14. O número depois do ponto é quantos dígitos vêm após a vírgula decimal. %f sem precisão sempre imprime 6 dígitos, e é por isso que floats sem formatação ficam tão poluídos.
Por que o printf imprime lixo ou trava?
Quase sempre por uma incompatibilidade entre o especificador e o argumento. printf("%d", 3.5) ou printf("%s", 42) é comportamento indefinido - o printf não enxerga os tipos reais, ele confia na string de formato. Compile com -Wall, o que faz o GCC conferir o formato contra os argumentos.
Por que printf(entrada_do_usuario) é perigoso?
Se o texto do usuário contiver sequências %, o printf vai obedecê-las e ler argumentos que nunca foram passados - vazando memória da pilha e, com %n, até escrevendo na memória. Essa é a clássica vulnerabilidade de format string. Escreva sempre printf("%s", entrada_do_usuario) no lugar.