Fluxos padrão
Esta aula aborda os fluxos padrão da linguagem C (stdin, stdout, stderr), como redirecioná-los no shell, e o conceito de buffering com a função fflush. Inclui exemplos práticos, boas práticas e exercícios com respostas.
Na programação em C, a comunicação entre o programa e o ambiente externo é feita através de fluxos (streams). Os três fluxos padrão — stdin, stdout e stderr — são pré-definidos e abertos automaticamente quando um programa é iniciado. Eles permitem que o programa receba entrada do usuário (teclado), envie saída para a tela e reporte erros, respectivamente.
Entender como esses fluxos funcionam é essencial para criar programas robustos e interativos, além de possibilitar o uso de redirecionamento e pipelines no sistema operacional.
stdin, stdout, stderr
O fluxo stdin (do inglês standard input) é a entrada padrão, normalmente associada ao teclado. É usado por funções como scanf, getchar e fgets para ler dados. O fluxo stdout (standard output) é a saída padrão, normalmente a tela do terminal, usado por printf e puts para exibir resultados. Já o stderr (standard error) também é destinado à tela, mas é reservado para mensagens de erro, sendo usado por fprintf(stderr, ...).
Esses fluxos são implementados como ponteiros do tipo FILE* e podem ser usados em qualquer função que aceite um fluxo. Por exemplo, fgets(buffer, tamanho, stdin) lê do teclado, enquanto fprintf(stderr, "Erro") envia para o fluxo de erro. A separação entre stdout e stderr é importante porque permite que, ao redirecionar a saída de um programa para um arquivo, as mensagens de erro ainda apareçam no terminal.
#include <stdio.h>
int main() {
int numero;
printf("Digite um número: ");
scanf("%d", &numero);
printf("Você digitou: %d\n", numero);
fprintf(stderr, "Este é um erro de exemplo\n");
return 0;
}
Redirecionamento
O redirecionamento é uma funcionalidade do shell (como Bash ou CMD) que permite alterar a origem ou destino dos fluxos padrão. Por exemplo, ./programa < entrada.txt faz com que o stdin seja lido do arquivo entrada.txt em vez do teclado. Da mesma forma, ./programa > saida.txt envia o stdout para o arquivo saida.txt.
O stderr pode ser redirecionado separadamente usando 2>. Exemplo: ./programa 2> erros.log envia apenas as mensagens de erro para o arquivo. Também é possível redirecionar ambos para o mesmo arquivo com > saida.txt 2>&1. Esses recursos são poderosos para automação e logging.
$ ./meu_programa < dados.txt > resultado.txt 2> erros.txt
Buffering (fflush)
Os fluxos em C são geralmente bufferizados, ou seja, os dados são acumulados em um buffer antes de serem enviados ao dispositivo. Para stdout, o buffer é descarregado quando o buffer está cheio, quando o programa termina, ou quando o fluxo é fechado. Em alguns casos, pode ser necessário forçar a escrita imediata, especialmente em programas interativos ou quando se usa printf antes de uma leitura.
A função fflush é usada para descarregar o buffer de um fluxo de saída. Por exemplo, fflush(stdout) força a escrita imediata de tudo que está no buffer. Isso é útil em situações como mostrar um prompt antes de ler uma entrada, pois sem o fflush o prompt pode não aparecer até que o buffer encha ou o programa termine.
#include <stdio.h>
int main() {
printf("Digite algo: ");
fflush(stdout); // Garante que o prompt apareça
char buffer[100];
fgets(buffer, sizeof(buffer), stdin);
printf("Você digitou: %s", buffer);
return 0;
}
Exemplos
Vamos ver um exemplo completo que combina os conceitos de stdin, stdout e stderr. O programa lê um número, verifica se é positivo e imprime uma mensagem na saída padrão ou no erro, conforme o caso.
#include <stdio.h>
int main() {
int valor;
printf("Entre com um número: ");
fflush(stdout);
scanf("%d", &valor);
if (valor > 0) {
printf("Número positivo: %d\n", valor);
} else {
fprintf(stderr, "Erro: número não positivo\n");
}
return 0;
}
Outro exemplo mostra como redirecionar a saída para um arquivo dentro do próprio código, usando freopen. Isso é útil quando se deseja que a saída vá para um arquivo sem depender do shell.
#include <stdio.h>
int main() {
freopen("saida.txt", "w", stdout);
printf("Esta mensagem vai para o arquivo.\n");
fclose(stdout);
return 0;
}
Boas Práticas
Ao trabalhar com fluxos padrão, é importante usar stderr para mensagens de erro, pois isso permite que elas sejam separadas da saída normal. Sempre que ler dados com scanf, verifique o retorno para evitar problemas com entradas inválidas. Use fflush somente quando necessário, pois chamadas excessivas podem prejudicar a performance. Prefira fgets a scanf para ler strings, pois é mais seguro.
Referências
- C IO - cppreference.com
- Standard Streams - GNU C Library Manual
- stdin(3) - Linux manual page
- C library function - fflush()
- Standard streams - Wikipedia
- fprintf - POSIX
Exercícios
- Exercício 1: Escreva um programa em C que leia um caractere do
stdine imprima nostdoutse é uma letra maiúscula, minúscula ou dígito. Usegetchareprintf. - Exercício 2: Crie um programa que leia um número inteiro e imprima seu quadrado no
stdout, e uma mensagem de erro nostderrse o número for negativo. - Exercício 3: Explique o que acontece quando você executa o comando
./programa 2> erro.loge dê um exemplo de como isso pode ser útil. - Exercício 4: Escreva um programa que use
fflushpara exibir um prompt antes de ler uma string do usuário, e depois imprima a string emstdout. - Exercício 5: Crie um programa que leia dois números e exiba a soma no
stdout; se a soma for maior que 100, envie uma mensagem de aviso parastderr.
#include <stdio.h>
#include <ctype.h>
int main() {
int c = getchar();
if (isupper(c)) {
printf("Maiúscula\n");
} else if (islower(c)) {
printf("Minúscula\n");
} else if (isdigit(c)) {
printf("Dígito\n");
} else {
printf("Outro\n");
}
return 0;
}
#include <stdio.h>
int main() {
int num;
printf("Digite um número: ");
fflush(stdout);
scanf("%d", &num);
if (num < 0) {
fprintf(stderr, "Erro: número negativo\n");
return 1;
}
printf("Quadrado: %d\n", num * num);
return 0;
}
stderr para o arquivo erro.log. Assim, as mensagens de erro são gravadas no arquivo, enquanto a saída padrão (stdout) continua aparecendo no terminal. Isso é útil para separar logs de erro da saída normal, permitindo analisar erros sem poluir a saída principal.#include <stdio.h>
int main() {
char nome[100];
printf("Qual é o seu nome? ");
fflush(stdout);
fgets(nome, sizeof(nome), stdin);
printf("Olá, %s", nome);
return 0;
}
#include <stdio.h>
int main() {
int a, b;
printf("Digite dois números: ");
fflush(stdout);
scanf("%d %d", &a, &b);
int soma = a + b;
printf("Soma: %d\n", soma);
if (soma > 100) {
fprintf(stderr, "Aviso: soma maior que 100\n");
}
return 0;
}