E/S Avançada e Linha de Comando
Argumentos de linha de comando são como fazer um pedido em um restaurante — você diz ao programa o que quer (argv), o programa conta quantos itens você pediu (argc) e então prepara seu pedido.
Argumentos de Linha de Comando
argc e argv
A função main pode receber dois parâmetros:
int main(int argc, char *argv[])
argc: contagem de argumentos, pelo menos 1 (o próprio nome do programa)argv: vetor de argumentos (array de strings),argv[0]é o nome do programa
#include <stdio.h>
int main(int argc, char *argv[]) {
printf("Contagem de argumentos: %d\n", argc);
for (int i = 0; i < argc; i++) {
printf("argv[%d] = %s\n", i, argv[i]);
}
return 0;
}
Execução:
./programa olá mundo 123
Contagem de argumentos: 4
argv[0] = ./programa
argv[1] = olá
argv[2] = mundo
argv[3] = 123
atoi, atol ou strtol.
Análise de Argumentos na Prática
Em projetos reais, argumentos de linha de comando tipicamente usam prefixos - para opções:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]) {
int verbose = 0;
int count = 1;
const char *filename = NULL;
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
verbose = 1;
} else if (strcmp(argv[i], "-n") == 0 && i + 1 < argc) {
count = atoi(argv[++i]);
} else if (argv[i][0] != '-') {
filename = argv[i];
} else {
fprintf(stderr, "Opção desconhecida: %s\n", argv[i]);
return 1;
}
}
if (filename == NULL) {
fprintf(stderr, "Uso: %s [-v] [-n contagem] arquivo\n", argv[0]);
return 1;
}
printf("Arquivo: %s, Contagem: %d, Verbose: %s\n",
filename, count, verbose ? "ligado" : "desligado");
return 0;
}
getopt, que trata tanto combinações de opções curtas quanto longas.
Variáveis de Ambiente
getenv
char *getenv(const char *name);
Retorna o valor da variável de ambiente, ou NULL se ela não existir.
#include <stdio.h>
#include <stdlib.h>
int main(void) {
const char *path = getenv("PATH");
if (path != NULL) {
printf("PATH = %s\n", path);
}
const char *home = getenv("HOME");
if (home != NULL) {
printf("HOME = %s\n", home);
}
return 0;
}
A Variável extern environ
C define uma variável global environ que aponta para um array de todas as strings de variáveis de ambiente:
#include <stdio.h>
extern char **environ;
int main(void) {
char **env = environ;
while (*env) {
printf("%s\n", *env);
env++;
}
return 0;
}
Tratamento de Erros com errno
O Mecanismo errno
Quando funções da biblioteca padrão do C encontram erros, elas tipicamente não falham diretamente. Em vez disso, definem a variável global errno e retornam um valor de erro:
#include <errno.h>
errno não é automaticamente zerado após uma chamada bem-sucedida de função da biblioteca! Você deve verificá-lo apenas após uma chamada de função ter falhado.
perror / strerror
void perror(const char *s);
char *strerror(int errnum);
perror exibe s: mensagem de erro em stderr. strerror retorna a descrição textual correspondente ao código de erro.
#include <stdio.h>
#include <errno.h>
#include <string.h>
int main(void) {
FILE *fp = fopen("arquivo_inexistente.txt", "r");
if (fp == NULL) {
perror("fopen");
printf("Código de erro: %d, Mensagem de erro: %s\n", errno, strerror(errno));
return 1;
}
fclose(fp);
return 0;
}
fopen: No such file or directory
Código de erro: 2, Mensagem de erro: No such file or directory
Exemplo
Padrão completo de tratamento de erros:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Uso: %s arquivo\n", argv[0]);
return 1;
}
FILE *fp = fopen(argv[1], "r");
if (fp == NULL) {
fprintf(stderr, "Não é possível abrir '%s': %s\n", argv[1], strerror(errno));
return 1;
}
int ch;
while ((ch = fgetc(fp)) != EOF) {
putchar(ch);
}
if (ferror(fp)) {
fprintf(stderr, "Ocorreu um erro de leitura: %s\n", strerror(errno));
fclose(fp);
return 1;
}
fclose(fp);
return 0;
}
ferror verifica se um fluxo sofreu um erro de leitura/escrita, o que é diferente de feof (que verifica o final do arquivo).
Fluxos Padrão
C abre automaticamente três fluxos quando um programa inicia:
| Fluxo | Nome | Dispositivo Padrão | Descritor de Arquivo |
|---|---|---|---|
stdin |
Entrada padrão | Teclado | 0 |
stdout |
Saída padrão | Tela | 1 |
stderr |
Erro padrão | Tela | 2 |
A diferença entre stdout e stderr: stdout é armazenado em buffer, stderr não é armazenado em buffer. Ao redirecionar, eles não afetam um ao outro.
#include <stdio.h>
int main(void) {
fprintf(stdout, "Esta é a saída normal\n");
fprintf(stderr, "Esta é a saída de erro\n");
return 0;
}
./programa > saida.txt
Após executar dessa forma, a "saída normal" vai para o arquivo, enquanto a "saída de erro" ainda aparece na tela.
Princípios de Redirecionamento
Na linha de comando:
>redirecionastdoutpara um arquivo2>redirecionastderrpara um arquivo<substituistdinpor um arquivo|conecta ostdoutde um programa aostdinde outro
./programa 2>&1 tudo.log
Esse comando mescla stderr em stdout e escreve ambos em tudo.log.
Arquivos Temporários
tmpfile
FILE *tmpfile(void);
Cria um arquivo temporário aberto no modo "wb+". O arquivo é automaticamente excluído ao ser fechado ou quando o programa termina.
#include <stdio.h>
int main(void) {
FILE *tmp = tmpfile();
if (tmp == NULL) {
perror("tmpfile");
return 1;
}
fprintf(tmp, "Dados temporários %d\n", 42);
rewind(tmp);
char buf[64];
while (fgets(buf, sizeof(buf), tmp) != NULL) {
printf("%s", buf);
}
fclose(tmp);
return 0;
}
Dados temporários 42
tmpnam
char *tmpnam(char *s);
Gera um nome de arquivo temporário que não conflita com arquivos existentes. Porém, há uma condição de corrida — entre gerar o nome e criar o arquivo, outro programa poderia criar um arquivo com o mesmo nome.
tmpfile — ele cria e abre o arquivo atomicamente, sendo mais seguro. tmpnam não é seguro em ambientes multi-thread.
Exemplo
Usando um arquivo temporário para processar dados intermediários:
#include <stdio.h>
#include <string.h>
int main(void) {
FILE *tmp = tmpfile();
if (tmp == NULL) {
perror("tmpfile");
return 1;
}
char *lines[] = {"banana", "apple", "orange", "grape"};
int n = 4;
for (int i = 0; i < n; i++) {
fprintf(tmp, "%s\n", lines[i]);
}
rewind(tmp);
printf("Antes da ordenação:\n");
char buf[64];
while (fgets(buf, sizeof(buf), tmp) != NULL) {
buf[strcspn(buf, "\n")] = '\0';
printf(" %s\n", buf);
}
rewind(tmp);
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - 1 - i; j++) {
char a[64], b[64];
long pos = ftell(tmp);
fgets(a, sizeof(a), tmp);
fgets(b, sizeof(b), tmp);
a[strcspn(a, "\n")] = '\0';
b[strcspn(b, "\n")] = '\0';
if (strcmp(a, b) > 0) {
fseek(tmp, pos, SEEK_SET);
fprintf(tmp, "%s\n%s\n", b, a);
} else {
fseek(tmp, pos, SEEK_SET);
fprintf(tmp, "%s\n%s\n", a, b);
}
fseek(tmp, pos + strlen(a) + strlen(b) + 2, SEEK_SET);
}
rewind(tmp);
}
rewind(tmp);
printf("Depois da ordenação:\n");
while (fgets(buf, sizeof(buf), tmp) != NULL) {
buf[strcspn(buf, "\n")] = '\0';
printf(" %s\n", buf);
}
fclose(tmp);
return 0;
}
Antes da ordenação:
banana
apple
orange
grape
Depois da ordenação:
apple
grape
orange
banana
Saída Formatada Avançada
snprintf
int snprintf(char *str, size_t size, const char *format, ...);
Comparado ao sprintf, snprintf adiciona um parâmetro size para limitar o comprimento da escrita, prevenindo estouro de buffer.
#include <stdio.h>
int main(void) {
char buf[10];
int n = snprintf(buf, sizeof(buf), "Olá, %s!", "Mundo");
printf("buf = \"%s\", comprimento necessário = %d\n", buf, n);
return 0;
}
buf = "Olá, Mu", comprimento necessário = 12
snprintf é o comprimento que a saída formatada deveria ter, não o número real de bytes escritos. Se o valor de retorno for maior que size-1, a saída foi truncada.
❓ Perguntas Frequentes
P: Qual é a ordem dos argumentos em argv? R: argv[0] é o próprio nome do programa, e argv[1] em diante são os argumentos fornecidos pelo usuário, na mesma ordem em que aparecem na linha de comando.
P: Por que perror exibe em stderr em vez de stdout? R: Porque mensagens de erro não devem ser perdidas quando stdout é redirecionado para um arquivo. stderr não é armazenado em buffer e é independente de stdout, garantindo que as mensagens de erro estejam sempre visíveis.
P: Quando errno é zerado? R: Ele nunca é automaticamente zerado. Você deve definir errno = 0 manualmente antes de chamar uma função que pode falhar, e então verificá-lo após a função retornar um valor de erro.
P: Onde tmpfile cria seu arquivo? R: O local depende do sistema, tipicamente um diretório temporário (como /tmp). Você não precisa se preocupar com o caminho porque tmpfile retorna um FILE, e o arquivo desaparece automaticamente quando o programa termina.*
📖 Resumo
argc/argvrecebem argumentos de linha de comando, comargv[0]sendo o nome do programagetenvlê variáveis de ambiente,environpercorre todas as variáveis de ambienteerrno+perror/strerroré a abordagem padrão para tratamento de erros da biblioteca padrãostdouté armazenado em buffer,stderrnão é armazenado em buffer, e são independentes ao redirecionartmpfilecria um arquivo temporário de exclusão automática, que é mais seguro quetmpnam
📝 Exercícios
- Escreva um programa de linha de comando que aceite
-cpara contar caracteres,-lpara contar linhas e-wpara contar palavras em um arquivo - Escreva um programa que use
tmpfilepara reverter o conteúdo de um arquivo (escreva tudo em um arquivo temporário, depois leia de volta em ordem reversa) - Escreva um programa que demonstre a diferença entre redirecionamento de
stdoutestderr, usando>e2>para capturar cada tipo de saída separadamente



