Exibindo e lendo dados com precisão — especificadores de formato, largura, casas decimais e locale
O printf (print formatted) é a principal função de saída em C. Ela imprime texto na tela com suporte a formatação precisa de valores numéricos, larguras de coluna e alinhamento.
printf("string de formato", var1, var2, ...);\n, \t) e especificadores de formato que começam com % e indicam o tipo e a apresentação de cada valor a ser impresso.
#include <stdio.h>
int main() {
int idade = 22;
float altura = 1.75;
char inicial = 'A';
printf("Idade: %d anos\n", idade);
printf("Altura: %.2f m\n", altura); // 2 casas decimais
printf("Inicial: %c\n", inicial);
printf("Idade=%d Altura=%.2f Inicial=%c\n", idade, altura, inicial);
return 0;
}
Idade: 22 anos
Altura: 1.75 m
Inicial: A
Idade=22 Altura=1.75 Inicial=A
Um especificador de formato é composto de vários campos opcionais, nessa ordem:
% [flags] [largura] [.precisão] [modificador] tipo
| Especificador | Tipo | Exemplo de uso | Saída |
|---|---|---|---|
%d ou %i |
int |
printf("%d", 42) |
42 |
%u |
unsigned int |
printf("%u", 3000000000U) |
3000000000 |
%ld |
long |
printf("%ld", 2000000000L) |
2000000000 |
%lld |
long long |
printf("%lld", 9000000000LL) |
9000000000 |
%f |
float / double |
printf("%f", 3.14) |
3.140000 |
%e |
float / double (notação científica) |
printf("%e", 0.000123) |
1.230000e-04 |
%g |
float / double (formato mais compacto) | printf("%g", 3.14) |
3.14 |
%c |
char |
printf("%c", 'Z') |
Z |
%s |
String (char[]) |
printf("%s", "Olá") |
Olá |
%p |
Ponteiro (endereço de memória) | printf("%p", &x) |
0x7fff5a... |
%o |
Inteiro em octal | printf("%o", 8) |
10 |
%x |
Inteiro em hexadecimal (minúsculo) | printf("%x", 255) |
ff |
%X |
Inteiro em hexadecimal (maiúsculo) | printf("%X", 255) |
FF |
%% |
Imprime o símbolo % literal | printf("100%%") |
100% |
O printf permite controlar a largura mínima do espaço ocupado pelo valor impresso e o número de casas decimais — muito útil para alinhar colunas em tabelas.
Um número entre % e o tipo define a largura mínima. O valor é alinhado à direita por padrão:
#include <stdio.h>
int main() {
printf("%5d\n", 42); // " 42" — 5 chars, alinhado à direita
printf("%5d\n", 1234); // " 1234"
printf("%5d\n", 99999); // "99999" — ultrapassa a largura, sem corte
printf("%-5d|\n", 42); // "42 |" — flag - alinha à esquerda
return 0;
}
42
1234
99999
42 |
O ponto seguido de um número após a largura define as casas decimais:
#include <stdio.h>
int main() {
double pi = 3.141592653589793;
printf("%.0f\n", pi); // "3" — sem casas decimais
printf("%.2f\n", pi); // "3.14"
printf("%.5f\n", pi); // "3.14159"
printf("%.10f\n", pi); // "3.1415926536"
printf("%10.3f\n", pi); // " 3.142" — largura 10, 3 decimais
return 0;
}
3
3.14
3.14159
3.1415926536
3.142
A flag 0 preenche com zeros à esquerda em vez de espaços:
printf("%05d\n", 42); // "00042"
printf("%08.2f\n", 3.14); // "00003.14"
00042
00003.14
#include <stdio.h>
int main() {
printf("%-12s %6s %8s\n", "Produto", "Qtd", "Preco");
printf("%-12s %6d %8.2f\n", "Caneta", 10, 2.50);
printf("%-12s %6d %8.2f\n", "Caderno", 3, 15.90);
printf("%-12s %6d %8.2f\n", "Regua", 5, 4.75);
return 0;
}
Produto Qtd Preco
Caneta 10 2.50
Caderno 3 15.90
Regua 5 4.75
O scanf (scan formatted) lê dados digitados pelo usuário e os armazena em variáveis. Sua sintaxe espelha a do printf, mas exige o operador & (endereço de memória) antes de cada variável — exceto strings.
scanf("string de formato", &var1, &var2, ...);& fornece ao scanf o endereço de memória de cada variável para que ele possa gravar o valor lido diretamente naquele local. Sem o &, o comportamento é indefinido — um dos erros mais comuns em iniciantes.
#include <stdio.h>
int main() {
int idade;
float altura;
char inicial;
printf("Digite sua idade: ");
scanf("%d", &idade);
printf("Digite sua altura (ex: 1.75): ");
scanf("%f", &altura);
printf("Digite sua inicial: ");
scanf(" %c", &inicial); // espaço antes de %c — descarta o '\n' residual
printf("\nIdade: %d | Altura: %.2f m | Inicial: %c\n",
idade, altura, inicial);
return 0;
}
Digite sua idade: 22
Digite sua altura (ex: 1.75): 1.75
Digite sua inicial: A
Idade: 22 | Altura: 1.75 m | Inicial: A
É possível ler vários valores de uma vez, separando os especificadores. O usuário deve digitar os valores separados por espaços ou Enter:
#include <stdio.h>
int main() {
int a, b;
printf("Digite dois inteiros: ");
scanf("%d %d", &a, &b);
printf("Soma: %d\n", a + b);
return 0;
}
Digite dois inteiros: 10 25
Soma: 35
scanf("%d"), o caractere \n (Enter) fica no buffer de entrada. Se o próximo scanf usa %c, ele captura esse \n em vez do caractere desejado. A solução é adicionar um espaço antes de %c:scanf(" %c", &letra); — o espaço instrui o scanf a descartar qualquer espaço em branco residual, incluindo o \n.
Atenção a uma diferença importante: no printf, tanto float quanto double usam %f. No scanf, double exige obrigatoriamente %lf:
| Tipo | scanf | printf |
|---|---|---|
float |
%f |
%f |
double |
%lf ⚠️ |
%f |
long double |
%Lf |
%Lf |
Por padrão, o C usa o locale "C" (inglês), que pode não exibir corretamente caracteres acentuados como ã, é, ç. Para ativar o suporte ao idioma do sistema operacional, usa-se a função setlocale da biblioteca <locale.h>:
#include <stdio.h>
#include <locale.h>
int main() {
setlocale(LC_ALL, ""); // ativa o locale do sistema (suporte a acentos)
printf("Olá, bem-vindo ao Curso de C!\n");
printf("Atenção: leia as instruções com cuidado.\n");
return 0;
}
Olá, bem-vindo ao Curso de C!
Atenção: leia as instruções com cuidado.
"" passada para setlocale significa "use o locale padrão do sistema operacional". Você também pode especificar explicitamente, por exemplo "pt_BR.UTF-8" no Linux ou "Portuguese_Brazil.1252" no Windows, mas usar "" é a forma mais portável.
O programa abaixo, diretamente da seção 4 do livro de referência do curso, demonstra na prática a leitura e escrita dos três tipos básicos — inteiro, ponto flutuante e caractere — com suporte a acentuação:
#include <stdio.h>
#include <locale.h>
void main() {
setlocale(LC_ALL, ""); // Permite usar acentos
printf("Olá \n");
// ─── Lendo valor inteiro ───────────────────────────────
int a = 50;
printf("O valor de a é = %d \n", a);
printf("Informe o novo valor de a: ");
scanf("%d", &a);
printf("O valor de a mudou para %d \n", a);
// ─── Lendo valor com casas decimais ────────────────────
float b = 5.5;
printf("O valor de b é = %f \n", b);
printf("Informe o novo valor de b: ");
scanf("%f", &b);
printf("O valor de b mudou para %f \n", b);
// ─── Lendo letra ────────────────────────────────────────
char letra = 't';
printf("O valor de c é = %c \n", letra);
printf("Informe o novo valor de c (letra): ");
scanf(" %c", &letra); // espaço antes de %c
printf("O valor de c mudou para %c\n", letra);
}
void main(): O material de referência usa void main(), o que é aceito por alguns compiladores (como o MinGW no Code::Blocks), mas tecnicamente a forma padrão ANSI C é int main() com return 0; ao final. Para novos programas, prefira sempre int main().
O Java também possui o método printf (na classe PrintStream), e ele usa os mesmos especificadores de formato. A principal diferença na leitura de dados é o uso de Scanner em Java versus scanf com & em C:
| Operação | C | Java |
|---|---|---|
| Imprimir inteiro | printf("%d\n", x) |
System.out.println(x) |
| Imprimir com 2 decimais | printf("%.2f\n", x) |
System.out.printf("%.2f%n", x) |
| Ler inteiro | scanf("%d", &x) |
x = sc.nextInt() |
| Ler double | scanf("%lf", &x) |
x = sc.nextDouble() |
| Ler char | scanf(" %c", &c) |
c = sc.next().charAt(0) |
| Suporte a acentos | setlocale(LC_ALL, "") |
Nativo (Unicode) |
Responda às questões abaixo para fixar printf, scanf e especificadores de formato: