C Saída com printf
Um programa que não mostra nada é como uma calculadora sem visor: pode até fazer as contas, mas ninguém fica sabendo. Em C, o jeito mais comum de mostrar algo na tela é o printf, que você já usou no Olá, mundo. Nesta aula você vai conhecê-lo melhor, junto com dois parentes mais simples, o puts e o putchar, e vai dar o primeiro passo para mostrar números no meio do texto.
- Mostrar texto com
printf, exatamente como ele está entre as aspas. - Usar o
\npara quebrar a linha, e o que acontece sem ele. - Montar uma linha com vários
printf, ou várias linhas com um só. - Usar o
puts, que já pula a linha sozinho, e oputchar, que mostra um caractere. - Encaixar um número com
%de um texto com%s. - Os erros e avisos mais comuns com o
printf.
Os exemplos foram compilados na máquina fictícia da Ana com o comando padrão do guia, gcc -std=c23 -Wall -Wextra, na pasta ~/c.
A ideia: o letreiro e o formulário
Pense no printf de dois jeitos:
- Como um letreiro luminoso: você entrega um texto, e ele aparece na tela, letra por letra, do jeito que você escreveu.
- Como um formulário com lacunas: "A Ana tem ___ anos". Você entrega o formulário e o que vai em cada lacuna, e ele mostra tudo preenchido. Esse é o "f" de
printf, de formatted, "formatado" (seção 6).
Todas as funções desta aula vêm do cabeçalho stdio.h, por isso todo programa começa com #include <stdio.h>. Elas escrevem na saída padrão, que normalmente é a tela do terminal (mais sobre isso em stdin, stdout e stderr).
1. Mostrar um texto
#include <stdio.h>
int main(void) {
printf("Bom dia, Ana!\n");
return 0;
}
- printf
- O nome da função: print formatted, "imprimir formatado". Em programação, "imprimir" quase sempre quer dizer "mostrar na tela".
- ( … )
- Os parênteses chamam a função e levam o que ela vai mostrar.
- "Bom dia, Ana!\n"
- O texto, entre aspas duplas. Ele se chama string (aula Strings). As aspas não aparecem na tela.
- \n
- A quebra de linha (seção 2).
- ;
- Termina a instrução.
$ gcc -std=c23 -Wall -Wextra bomdia.c -o bomdia $ ./bomdia Bom dia, Ana!
O printf mostra o texto exatamente como está: espaços repetidos, espaços no começo, maiúsculas, acentos e símbolos, tudo aparece igual:
#include <stdio.h>
int main(void) {
printf("Ana Souza\n");
printf(" recuado\n");
printf("ÁÉÍÓÚ ç ã õ €\n");
return 0;
}
$ gcc -std=c23 -Wall -Wextra espacos2.c -o espacos2 $ ./espacos2 Ana Souza recuado ÁÉÍÓÚ ç ã õ €
Os acentos funcionam porque o arquivo e o terminal usam a mesma codificação, o UTF-8 (a aula Acentos e UTF-8 conta os detalhes). Há só alguns caracteres que precisam de cuidado dentro das aspas: a própria aspa ", a barra invertida \ e o %. Eles têm uma aula própria: Novas Linhas e Escapes.
2. A quebra de linha: \n
O printf não pula de linha sozinho. Ele mostra o texto e deixa o cursor parado logo depois da última letra. Quem manda pular é o \n. Veja o que acontece sem ele:
#include <stdio.h>
int main(void) {
printf("Arroz");
printf("Feijão");
printf("Café");
return 0;
}
$ gcc -std=c23 -Wall -Wextra grudado.c -o grudado $ ./grudado ArrozFeijãoCaféana@notebook:~/c$ « tudo grudado, até o prompt do terminal
As três palavras ficaram coladas, e o prompt do terminal (ana@notebook:~/c$) apareceu grudado no fim, porque o cursor ficou na mesma linha. Com um \n no fim de cada texto, tudo se arruma:
#include <stdio.h>
int main(void) {
printf("Lista de compras:\n- arroz\n- feijão\n- café\n");
return 0;
}
$ gcc -std=c23 -Wall -Wextra lista.c -o lista $ ./lista Lista de compras: - arroz - feijão - café
Repare que este exemplo usou um só printf com quatro \n: o \n pode ficar em qualquer lugar do texto, não só no fim. Cada \n é uma quebra.
\n
Além de não grudar o prompt, o \n faz o texto aparecer na hora: o C guarda o que você manda mostrar e só "despeja" na tela quando chega um \n (ou quando o programa termina). A aula Novas Linhas e Escapes mostra por que isso importa.
3. Vários printf: uma linha em pedaços
Como o printf não pula linha sozinho, dá para montar uma linha com vários printf. Só o último leva o \n:
#include <stdio.h>
int main(void) {
printf("Bom ");
printf("dia, ");
printf("Ana!\n");
printf("Hoje é sábado.\n");
return 0;
}
$ gcc -std=c23 -Wall -Wextra pedacos.c -o pedacos $ ./pedacos Bom dia, Ana! Hoje é sábado.
Os três primeiros printf escreveram, um depois do outro, na mesma linha. Repare nos espaços: "Bom " e "dia, " terminam com espaço, senão sairia Bomdia,Ana!. Isso vai ser útil quando uma parte da linha mudar e outra não, por exemplo com if (aula if e else).
Um printf por linha ou um printf com vários \n? Os dois funcionam. Um por linha deixa o código parecido com a saída e é mais fácil de mudar; o guia prefere esse.
Missão: o programa mostra a etiqueta de entrega da Ana toda grudada numa linha só. Acrescente os \n que faltam para a saída ficar em três linhas:
Ana Souza Rua das Flores, 10 Recife - PE
#include <stdio.h>
int main(void) {
printf("Ana Souza");
printf("Rua das Flores, 10");
printf("Recife - PE");
return 0;
}
#include <stdio.h>
int main(void) {
printf("Ana Souza\n");
printf("Rua das Flores, 10\n");
printf("Recife - PE\n");
return 0;
}
4. puts: mostra e já pula a linha
puts (de put string, "pôr o texto") é uma função mais simples: mostra o texto e acrescenta o \n sozinha.
#include <stdio.h>
int main(void) {
puts("Lista de compras:");
puts("- arroz");
puts("- feijão\n");
puts("Fim da lista");
return 0;
}
$ gcc -std=c23 -Wall -Wextra puts.c -o puts
$ ./puts
Lista de compras:
- arroz
- feijão
« linha em branco: o \n do texto + o \n do puts
Fim da lista
Na terceira linha do código, o texto já tinha um \n, e o puts pôs outro: o resultado foi uma linha em branco. Com puts, não escreva o \n no fim (a não ser que queira mesmo a linha em branco).
Outra diferença: o puts não encaixa valores. Ele mostra o texto como está, até com %:
$ cat puts3.c
#include <stdio.h>
int main(void) {
puts("Desconto de 10%");
return 0;
}
$ gcc -std=c23 -Wall -Wextra puts3.c -o puts3
$ ./puts3
Desconto de 10%
(No printf, o % é especial e, para mostrá-lo, é preciso escrever %%; veja Novas Linhas e Escapes.)
5. putchar: um caractere de cada vez
putchar (de put character, "pôr um caractere") mostra um único caractere. O caractere vai entre aspas simples, 'A', e não entre aspas duplas:
#include <stdio.h>
int main(void) {
putchar('O');
putchar('i');
putchar('!');
putchar('\n');
return 0;
}
$ gcc -std=c23 -Wall -Wextra putchar.c -o putchar $ ./putchar Oi!
- 'O'
- Um caractere, entre aspas simples. Dentro delas cabe um caractere só (aula Caracteres).
- '\n'
- A quebra de linha também é um caractere, e o
putcharnão pula linha sozinho.
Para mostrar uma frase inteira, putchar é trabalhoso demais. Ele brilha quando o programa produz um caractere de cada vez, como ao desenhar uma linha de * com um laço (aula for) ou ao copiar um texto letra por letra.
Em C, 'A' (simples) é um caractere e "A" (duplas) é um texto (que por acaso tem uma letra). Não são intercambiáveis: putchar("A") e printf('Olá') dão erro (veja "Quando dá errado").
6. Encaixar valores: %d e %s
Agora o "formulário com lacunas". Dentro do texto do printf, um % seguido de uma letra marca uma lacuna. O valor que vai na lacuna vem depois do texto, separado por vírgula:
#include <stdio.h>
int main(void) {
printf("A Ana tem %d anos.\n", 29);
printf("Daqui a 10 anos, terá %d.\n", 29 + 10);
return 0;
}
$ gcc -std=c23 -Wall -Wextra idade.c -o idade $ ./idade A Ana tem 29 anos. Daqui a 10 anos, terá 39.
- %d
- A lacuna para um número inteiro (decimal, na base 10). Ela some e, no lugar dela, aparece o número.
- , 29
- Depois do texto, uma vírgula e o valor que vai na lacuna.
- 29 + 10
- O valor pode ser uma conta: o C calcula primeiro (39) e depois encaixa.
Para encaixar um texto, a lacuna é %s (de string). E um mesmo printf pode ter várias lacunas: os valores entram na ordem, a primeira lacuna recebe o primeiro valor, a segunda recebe o segundo, e assim por diante:
#include <stdio.h>
int main(void) {
printf("A Ana tem %d anos.\n", 29);
printf("Ela mora em %s.\n", "Recife");
printf("%s comprou %d pães.\n", "Ana", 6);
printf("2 + 3 = %d\n", 2 + 3);
return 0;
}
$ gcc -std=c23 -Wall -Wextra formatos.c -o formatos $ ./formatos A Ana tem 29 anos. Ela mora em Recife. Ana comprou 6 pães. 2 + 3 = 5
Na terceira linha: %s recebeu "Ana" e %d recebeu 6. Na quarta, o texto 2 + 3 = foi mostrado como está (dentro das aspas não há conta nenhuma), e o %d recebeu o resultado da conta 2 + 3.
Por enquanto, escrever printf("A Ana tem %d anos.\n", 29); parece igual a escrever o 29 direto no texto. A graça vai aparecer na aula Variáveis, quando o valor vier de uma variável que muda, ou de algo que o usuário digitou (aula Entrada com scanf). Existem lacunas para números com vírgula (%f), caracteres (%c) e muito mais, com largura, casas decimais e alinhamento: está tudo em Formatos do printf.
Missão: a Ana fez aniversário. Faça o programa mostrar A Ana tem 30 anos., mas sem escrever o 30 dentro das aspas: use a lacuna %d e passe o número depois da vírgula.
#include <stdio.h>
int main(void) {
printf("A Ana tem 29 anos.\n");
return 0;
}
#include <stdio.h>
int main(void) {
printf("A Ana tem %d anos.\n", 30);
return 0;
}
Formas diferentes, e qual usar
| Função | Pula a linha sozinha? | Encaixa valores? | Use quando |
|---|---|---|---|
printf("texto\n"); | Não: escreva \n | Sim: %d, %s… | Quase sempre. É a mais versátil. |
puts("texto"); | Sim | Não | Texto fixo, uma linha inteira, sem valores. |
putchar('x'); | Não | Não: é um caractere só | O programa produz um caractere de cada vez. |
Qual usar? Na dúvida, printf: ele faz tudo o que os outros fazem. O guia usa printf na maioria dos exemplos para você se acostumar com ele, e puts/putchar quando deixam o código mais simples.
Quando dá errado
printf: makes pointer from integer without a cast
$ cat aspas.c
#include <stdio.h>
int main(void) {
printf('Olá, mundo!\n');
return 0;
}
$ gcc -std=c23 -Wall -Wextra aspas.c -o aspas
aspas.c: In function ‘main’:
aspas.c:4:12: warning: multi-character literal with 13 characters exceeds ‘int’ size of 4 bytes
4 | printf('Olá, mundo!\n');
| ^~~~~~~~~~~~~~~
aspas.c:4:12: error: passing argument 1 of ‘printf’ makes pointer from integer without a cast [-Wint-conversion]
4 | printf('Olá, mundo!\n');
| ^~~~~~~~~~~~~~~
| |
| int
In file included from aspas.c:1:
/usr/include/stdio.h:370:43: note: expected ‘const char * restrict’ but argument is of type ‘int’
370 | extern int printf (const char *__restrict __format, ...);
| ~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~
aspas.c:4:5: warning: format not a string literal and no format arguments [-Wformat-security]
4 | printf('Olá, mundo!\n');
| ^~~~~~
O que significa: o texto foi escrito com aspas simples. Para o C, '…' é um caractere (que vira um número), e o printf esperava um texto. A mensagem é assustadora, mas a primeira linha já dá a pista: multi-character literal, "caractere com vários caracteres". No editor do guia, o Clang diz character too large for enclosing character literal type.
Como consertar: texto vai entre aspas duplas: printf("Olá, mundo!\n");. O mesmo vale ao contrário: putchar("A") dá o erro makes integer from pointer without a cast; o certo é putchar('A').
‘Olá’ undeclared
$ gcc -std=c23 -Wall -Wextra semaspas.c -o semaspas « printf(Olá);
semaspas.c: In function ‘main’:
semaspas.c:4:12: error: ‘Olá’ undeclared (first use in this function)
4 | printf(Olá);
| ^~~
semaspas.c:4:12: note: each undeclared identifier is reported only once for each function it appears in
O que significa: sem aspas, o compilador acha que Olá é o nome de uma variável, que ninguém criou ("não declarada").
Como consertar: ponha o texto entre aspas duplas.
format ‘%d’ expects a matching ‘int’ argument
$ cat faltou.c
#include <stdio.h>
int main(void) {
printf("A Ana tem %d anos.\n");
return 0;
}
$ gcc -std=c23 -Wall -Wextra faltou.c -o faltou
faltou.c: In function ‘main’:
faltou.c:4:24: warning: format ‘%d’ expects a matching ‘int’ argument [-Wformat=]
4 | printf("A Ana tem %d anos.\n");
| ~^
| |
| int
$ ./faltou
A Ana tem 1958863448 anos. « um número qualquer, que muda de uma vez para outra
O que significa: o texto tem uma lacuna %d, mas nenhum valor foi passado. É só um aviso, então o programa foi criado, e o printf encaixou um "lixo" que estava na memória. No editor do guia, o Clang avisa more '%' conversions than data arguments ("mais lacunas do que valores").
Como consertar: para cada lacuna, um valor depois do texto, na mesma ordem: printf("A Ana tem %d anos.\n", 29);.
$ cat ordem.c
#include <stdio.h>
int main(void) {
printf("%s comprou %d pães.\n", 6, "Ana");
return 0;
}
$ gcc -std=c23 -Wall -Wextra ordem.c -o ordem
ordem.c: In function ‘main’:
ordem.c:4:14: warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘int’ [-Wformat=]
4 | printf("%s comprou %d pães.\n", 6, "Ana");
| ~^ ~
| | |
| char * int
| %d
ordem.c:4:25: warning: format ‘%d’ expects argument of type ‘int’, but argument 3 has type ‘char *’ [-Wformat=]
4 | printf("%s comprou %d pães.\n", 6, "Ana");
| ~^ ~~~~~
| | |
| int char *
| %s
$ ./ordem
Falha de segmentação (imagem do núcleo gravada) ./ordem
O que significa: os valores entraram na ordem errada: o %s recebeu o número 6 e o %d recebeu o texto. O gcc avisou (repare que ele até desenha o tipo de cada lado e sugere a lacuna certa embaixo), mas aviso não impede o programa de ser criado. Ao rodar, o printf tentou ler um texto num lugar da memória que não existe, e o sistema derrubou o programa: Falha de segmentação. No editor do guia, o mesmo programa não cai, mas mostra lixo; os dois estão errados.
Como consertar: os valores na mesma ordem das lacunas: printf("%s comprou %d pães.\n", "Ana", 6);. E leve a sério todo aviso que fala de format (aula Avisos).
expected ‘)’ before numeric constant
$ gcc -std=c23 -Wall -Wextra virgula.c -o virgula « printf("Idade: %d\n" 29);
virgula.c: In function ‘main’:
virgula.c:4:25: error: expected ‘)’ before numeric constant
4 | printf("Idade: %d\n" 29);
| ~ ^~~
| )
virgula.c:4:21: warning: format ‘%d’ expects a matching ‘int’ argument [-Wformat=]
4 | printf("Idade: %d\n" 29);
| ~^
| |
| int
O que significa: faltou a vírgula entre o texto e o valor. O compilador achou que o printf terminava depois do texto e esperava o ).
Como consertar: printf("Idade: %d\n", 29);.
too many arguments to function ‘puts’
$ gcc -std=c23 -Wall -Wextra puts2.c -o puts2 « puts("Idade:", 29);
puts2.c: In function ‘main’:
puts2.c:4:5: error: too many arguments to function ‘puts’; expected 1, have 2
4 | puts("Idade:", 29);
| ^~~~ ~~
In file included from puts2.c:1:
/usr/include/stdio.h:718:12: note: declared here
718 | extern int puts (const char *__s);
| ^~~~
O que significa: "argumentos demais para a função puts; esperava 1, recebeu 2". O puts só aceita o texto, não encaixa valores.
Como consertar: para encaixar valores, use printf("Idade: %d\n", 29);.
Referência rápida
| Escrita | O que mostra |
|---|---|
printf("Oi\n"); | Oi e pula a linha |
printf("Oi"); | Oi, e o cursor fica na mesma linha |
printf("a\nb\n"); | a e b em linhas separadas |
puts("Oi"); | Oi e pula a linha (o \n é automático) |
putchar('A'); | O caractere A (aspas simples!) |
printf("%d\n", 29); | 29: %d é a lacuna de número inteiro |
printf("%s\n", "Ana"); | Ana: %s é a lacuna de texto |
printf("%s tem %d\n", "Ana", 29); | Ana tem 29: os valores entram na ordem das lacunas |
Exercícios
1. A lacuna do número
Complete para mostrar Tenho 3 gatos.:
2. A lacuna do texto
Complete para mostrar Olá, Pedro!:
3. Sem o \n
Qual função mostra Fim e já pula a linha, sem você escrever \n?
4. O que aparece com printf("Ana"); printf("Maria\n");?
5. Quantas linhas mostra puts("Oi\n");?
6. Qual linha está certa?
Missão final: o recibo da lojinha
Missão: escreva o recibo da lojinha da Ana, exatamente assim:
=== Lojinha da Ana === Produto: caderno Quantidade: 3 Obrigada!
Regras: use puts em pelo menos uma linha; mostre o produto com %s (passando "caderno" depois da vírgula) e a quantidade com %d (passando 3). E nenhum aviso.
#include <stdio.h>
int main(void) {
// escreva o recibo aqui
return 0;
}
#include <stdio.h>
int main(void) {
puts("=== Lojinha da Ana ===");
printf("Produto: %s\n", "caderno");
printf("Quantidade: %d\n", 3);
puts("Obrigada!");
return 0;
}
Resumo
printf("…");mostra o texto exatamente como está entre as aspas duplas.- O
printfnão pula linha sozinho: use\n, em qualquer ponto do texto. Sem ele, tudo gruda, até o prompt. - Vários
printfpodem montar uma linha; umprintfpode mostrar várias linhas. puts("…");mostra o texto e já pula a linha; não encaixa valores.putchar('x');mostra um caractere, entre aspas simples.- No
printf,%dé a lacuna de um número inteiro e%s, a de um texto. Os valores vêm depois do texto, separados por vírgulas, na ordem das lacunas. - Lacuna sem valor ou com valor do tipo errado dá aviso, e o programa pode mostrar lixo ou até cair.
- Próximo passo: Novas Linhas e Escapes, para mostrar aspas, barras, tabulações e o próprio
%.