C Erros do Sistema (errno e perror)
O fopen devolveu NULL. Mas por quê? O arquivo não existe? Existe, mas a Ana não tem permissão? O nome é de uma pasta? Para o usuário, "não consegui abrir" é pouco; "Permissão negada" diz exatamente o que fazer. Em C, quem guarda o motivo é uma variável especial chamada errno, e quem transforma o motivo em frase são o perror e o strerror. Nesta aula você vai usar os três, ver as mensagens em português e conhecer duas armadilhas que pegam até programador experiente.
- O que é
errnoe quando ele recebe um valor. - Como mostrar o motivo de um erro com
perrore montar sua própria mensagem comstrerror. - Como fazer as mensagens saírem em português com
setlocale(e por que no editor do guia elas continuam em inglês). - Os códigos mais comuns (
ENOENT,EACCES,EISDIR,ERANGE…) e como decidir o que fazer com cada um. - A regra de ouro: conferir o retorno antes de olhar o
errno, e guardar oerrnologo depois do erro. - Como o
strtolusaerrnopara avisar que o número não coube (ERANGE).
Todos os programas foram compilados com o gcc 15.2 na máquina fictícia da Ana (usuária ana, computador notebook, sistema em português), na pasta ~/c, com gcc -std=c23 -Wall -Wextra arquivo.c -o programa.
A ideia: o bilhete na porta
A Ana vai buscar uma encomenda na loja, e a loja está fechada. Ela sabe que não deu certo (a porta não abriu), mas não sabe por quê. Aí ela vê um bilhete colado na porta: "Fechado para almoço, volto às 14h". Agora ela sabe o que fazer.
Em C funciona assim:
- A porta que não abre é o valor de retorno:
fopendevolveNULL,fseekdevolve -1,mallocdevolveNULL. É isso que diz se deu errado. - O bilhete é o
errno: um número que a função deixa para dizer por que deu errado.
E o bilhete tem uma regra importante: ninguém o tira da porta quando a loja reabre. Se você olhar o bilhete sem antes tentar a porta, pode ler um aviso velho. Isso vai ser a regra de ouro da seção 5.
1. perror: a frase pronta
O jeito mais curto de mostrar o motivo é o perror ("print error", mostrar o erro):
#include <stdio.h>
int main(void) {
FILE *f = fopen("compras.txt", "r");
if (f == NULL) {
perror("compras.txt");
return 1;
}
printf("Abri a lista de compras!\n");
fclose(f);
return 0;
}
- if (f == NULL)
- Primeiro, a porta: o
fopenfalhou? Só então vale a pena perguntar o motivo. - perror("compras.txt")
- Escreve o texto que você passou, um
": ", a descrição do erro guardado emerrnoe uma quebra de linha. Tudo vai para a saída de erros (stderr), e não para a saída normal (aula stdin, stdout e stderr). - "compras.txt"
- O texto do começo é seu. O costume é pôr o nome do arquivo ou da função que falhou, para o usuário saber o que deu errado.
- return 1;
- Termina com código diferente de zero: para o sistema, o programa falhou (aula exit e atexit).
$ gcc -std=c23 -Wall -Wextra abrir.c -o abrir $ ./abrir compras.txt: No such file or directory $ echo $? 1 $ printf "arroz\nfeijão\n" > compras.txt « agora o arquivo existe $ ./abrir Abri a lista de compras!
"No such file or directory" quer dizer "arquivo ou diretório inexistente". Mas por que em inglês, se o sistema da Ana está em português? Já explicamos na seção 3.
2. errno e strerror: o número e a frase
O perror sempre escreve no mesmo formato. Para montar sua própria mensagem, use as peças separadas:
errno(de "error number", número do erro), declarado em<errno.h>: umintcom o código do último erro.strerror(codigo)("string error", o erro como texto), de<string.h>: devolve a frase que descreve aquele código.
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <locale.h>
int main(void) {
setlocale(LC_ALL, "");
char nome[100];
printf("Arquivo: ");
if (fgets(nome, sizeof nome, stdin) == NULL) {
return 1;
}
nome[strcspn(nome, "\n")] = '\0';
FILE *f = fopen(nome, "a");
if (f == NULL) {
int erro = errno;
fprintf(stderr, "Não consegui abrir %s: %s (errno %d)\n", nome, strerror(erro), erro);
return 1;
}
fprintf(f, "anotação nova\n");
fclose(f);
printf("Anotei em %s\n", nome);
return 0;
}
| Linha | O que faz |
|---|---|
setlocale(LC_ALL, "") | Usa o idioma do sistema nas mensagens (seção 3). |
fgets + strcspn | Lê o nome do arquivo e tira o '\n' do fim (aula Ler uma Linha). |
fopen(nome, "a") | Abre para acrescentar no fim (cria se não existir). |
int erro = errno; | Guarda o código logo depois do erro, antes de chamar qualquer outra função (a seção 6 mostra por quê). |
fprintf(stderr, …) | Escreve na saída de erros, como o perror faz. |
strerror(erro) | A frase do código. O número também vai na mensagem, só para você ver. |
Agora a Ana testa o mesmo programa em quatro situações. Antes, ela tirou a permissão de escrita da lista de compras com o comando chmod 444 (o arquivo fica "só leitura" para todos):
$ gcc -std=c23 -Wall -Wextra anotar.c -o anotar $ chmod 444 compras.txt $ ./anotar Arquivo: compras.txt Não consegui abrir compras.txt: Permissão negada (errno 13) $ ./anotar Arquivo: ../Documentos Não consegui abrir ../Documentos: É um diretório (errno 21) $ ./anotar Arquivo: viagem/roteiro.txt Não consegui abrir viagem/roteiro.txt: Arquivo ou diretório inexistente (errno 2) $ chmod 644 compras.txt « devolve a permissão $ ./anotar Arquivo: compras.txt Anotei em compras.txt
O mesmo fopen, três motivos diferentes, três mensagens que dizem ao usuário o que fazer: pedir permissão, escolher um arquivo em vez de uma pasta, criar a pasta viagem. Repare que, com "a", o fopen cria o arquivo que falta, mas não cria pastas.
3. Mensagens em português: setlocale
Todo programa em C começa no idioma neutro "C" (que, na prática, é o inglês), não importa o idioma do sistema. Para usar o idioma do usuário, chame setlocale(LC_ALL, ""), de <locale.h>, no começo do main. O "" quer dizer "o que estiver configurado no sistema" (as variáveis LANG e LC_*; veja Variáveis de Ambiente).
Este programa mostra o número e a frase de alguns códigos antes e depois do setlocale:
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <locale.h>
void mostrar(void) {
printf("ENOENT %3d %s\n", ENOENT, strerror(ENOENT));
printf("EACCES %3d %s\n", EACCES, strerror(EACCES));
printf("EISDIR %3d %s\n", EISDIR, strerror(EISDIR));
printf("ERANGE %3d %s\n", ERANGE, strerror(ERANGE));
}
int main(void) {
printf("Antes do setlocale:\n");
mostrar();
setlocale(LC_ALL, "");
printf("Depois do setlocale:\n");
mostrar();
printf("Código 0: %s; pi = %f\n", strerror(0), 3.14159);
return 0;
}
$ gcc -std=c23 -Wall -Wextra tabela.c -o tabela $ ./tabela Antes do setlocale: ENOENT 2 No such file or directory EACCES 13 Permission denied EISDIR 21 Is a directory ERANGE 34 Numerical result out of range Depois do setlocale: ENOENT 2 Arquivo ou diretório inexistente EACCES 13 Permissão negada EISDIR 21 É um diretório ERANGE 34 Resultado numérico fora de alcance Código 0: Sucesso; pi = 3,141590
Repare na última linha: o código 0 quer dizer "sem erro" ("Sucesso"), e o setlocale(LC_ALL, "") mudou também o jeito de escrever números: o %f passou a usar vírgula (3,141590), como se escreve no Brasil. O LC_ALL ("todas as categorias") troca mensagens, números, datas e a ordem alfabética de uma vez. Se você só quer as mensagens em português e os números com ponto, use setlocale(LC_MESSAGES, "") (do POSIX; existe no Linux e no macOS).
setlocale. A biblioteca C do editor (a wasi-libc, baseada na musl) não traz traduções. As frases também são um pouco diferentes das do Linux, e até os números mudam:
| Código | gcc + glibc (Linux) | Editor do guia |
|---|---|---|
ENOENT | 2 · Arquivo ou diretório inexistente | 44 · No such file or directory |
EACCES | 13 · Permissão negada | 2 · Permission denied |
EINVAL | 22 · Argumento inválido | 28 · Invalid argument |
ERANGE | 34 · Resultado numérico fora de alcance | 68 · Result not representable |
errno com um número escrito à mão. Use sempre os nomes (ENOENT, EACCES…), que valem em qualquer sistema.
Experimente: a caixa tem duas abas, lista.c e o arquivo compras.txt. O programa tenta abrir "compra.txt" (sem o "s") e falha: veja o número, a frase do strerror e a linha do perror. Corrija o nome no fopen e rode de novo. Depois acrescente #include <locale.h> e setlocale(LC_ALL, ""); e confira que, aqui no editor, a frase continua em inglês.
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main(void) {
FILE *f = fopen("compra.txt", "r");
if (f == NULL) {
int erro = errno;
printf("errno = %d\n", erro);
printf("strerror: %s\n", strerror(erro));
perror("compra.txt");
return 1;
}
char linha[100];
while (fgets(linha, sizeof linha, f) != NULL) {
printf("- %s", linha);
}
fclose(f);
return 0;
}
arroz feijão café
4. Decidir pelo código: errno == ENOENT
Nem todo erro é igual. Para a lista de compras, "o arquivo não existe" não é um problema: é só a primeira vez que a Ana usa o programa. Já "permissão negada" é um problema de verdade. Os nomes de <errno.h> deixam o programa decidir:
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main(void) {
FILE *f = fopen("compras.txt", "r");
if (f == NULL) {
if (errno == ENOENT) {
printf("Ainda não há lista de compras. Comece uma!\n");
return 0;
}
perror("compras.txt");
return 1;
}
char linha[100];
int n = 0;
while (fgets(linha, sizeof linha, f) != NULL) {
n++;
printf("%d. %s", n, linha);
}
fclose(f);
return 0;
}
$ gcc -std=c23 -Wall -Wextra lista.c -o lista $ rm -f compras.txt $ ./lista Ainda não há lista de compras. Comece uma! $ printf "arroz\nfeijão\n" > compras.txt $ ./lista 1. arroz 2. feijão $ chmod 000 compras.txt « ninguém pode ler $ ./lista compras.txt: Permission denied $ chmod 644 compras.txt
Os códigos que você mais vai encontrar com arquivos e números:
| Nome | Mensagem (glibc, em português) | Quando aparece |
|---|---|---|
ENOENT | Arquivo ou diretório inexistente | Abrir para leitura um arquivo que não existe; pasta do caminho não existe ("Error NO ENTry") |
EACCES | Permissão negada | Sem permissão de leitura ou escrita ("Error ACCESS") |
EISDIR | É um diretório | Abrir uma pasta para escrever |
ENOTDIR | Não é um diretório | Um pedaço do caminho é arquivo: compras.txt/x |
EEXIST | Arquivo existe | Criar com "wx" um arquivo que já existe (C11) |
ENOSPC | Não há espaço disponível no dispositivo | Disco cheio ao gravar |
EMFILE | Muitos arquivos abertos | Esqueceu os fclose num laço |
EINVAL | Argumento inválido | Valor sem sentido, como fseek para posição negativa |
ERANGE | Resultado numérico fora de alcance | strtol, strtod, pow com resultado que não cabe |
EDOM | Argumento numérico fora de domínio | Matemática impossível, como sqrt(-1) (se a biblioteca usar errno) |
ENOMEM | Não foi possível alocar memória | Falta de memória (o malloc do POSIX usa) |
Só EDOM, ERANGE e EILSEQ são do padrão C. Os outros vêm do POSIX (o padrão do Linux, macOS e outros Unix) e existem em quase todo lugar, inclusive no Windows. A lista completa do Linux está em man errno.
5. A regra de ouro: retorno primeiro, errno depois
O errno tem duas manias que confundem:
- Uma função que dá certo não zera o
errno. O bilhete velho fica na porta. - Uma função que dá certo pode mudar o
errnomesmo assim, por algum trabalho interno que ela tentou e não precisou.
Por isso, o errno só tem significado logo depois de uma função que avisou pelo retorno que falhou. Veja o que acontece com quem olha o errno direto:
#include <stdio.h>
#include <errno.h>
int main(void) {
FILE *velho = fopen("lista-antiga.txt", "r"); // não existe
if (velho != NULL) {
fclose(velho);
}
FILE *f = fopen("compras.txt", "r"); // existe!
if (errno != 0) { // ERRADO
perror("compras.txt");
return 1;
}
printf("Abri a lista.\n");
fclose(f);
return 0;
}
$ gcc -std=c23 -Wall -Wextra errado.c -o errado $ ls compras.txt compras.txt $ ./errado compras.txt: No such file or directory
O compras.txt existe e foi aberto, mas o programa diz que ele não existe. O errno ainda guardava o ENOENT do primeiro fopen, o da lista antiga. O teste certo é if (f == NULL): a porta primeiro, o bilhete depois.
| Função | Como avisa que falhou | Usa errno? |
|---|---|---|
fopen | devolve NULL | sim (POSIX) |
fclose, fflush | devolve EOF | sim (POSIX) |
fseek | devolve diferente de 0 | sim (POSIX) |
ftell | devolve -1 | sim |
fread, fwrite | devolvem menos itens que o pedido | depois de ferror(f) verdadeiro (POSIX) |
strtol, strtod | não têm valor especial: zere o errno antes e olhe depois | sim (ERANGE) |
malloc | devolve NULL | no POSIX (ENOMEM) |
6. Guarde o errno antes que ele mude
Qualquer função da biblioteca pode mexer no errno, inclusive as que você usa para mostrar o erro. Este programa mostra o motivo duas vezes, com perror e depois com strerror(errno):
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main(void) {
FILE *f = fopen("compras.txt", "r");
if (f == NULL) {
perror("compras.txt");
fprintf(stderr, "De novo: %s\n", strerror(errno));
return 1;
}
fclose(f);
return 0;
}
$ gcc -std=c23 -Wall -Wextra duas.c -o duas $ rm compras.txt $ ./duas compras.txt: No such file or directory De novo: No such file or directory $ ./duas 2> erros.txt « manda os erros para um arquivo $ cat erros.txt compras.txt: No such file or directory De novo: Invalid argument
No terminal, as duas linhas concordam. Mas quando a saída de erros vai para um arquivo (o 2> do shell, aula stdin, stdout e stderr), o próprio perror da glibc faz um teste interno que falha e deixa EINVAL no errno. A segunda linha mostra um motivo que não tem nada a ver com o problema. Isso é real, e não é defeito da glibc: o padrão permite que qualquer função mude o errno.
A solução é a do anotar.c: copie o errno para uma variável sua na primeira linha depois do erro (int erro = errno;) e use só a cópia.
7. strtol e ERANGE: o número não coube
O strtol ("string to long", texto para long) da aula Texto ↔ Número não tem como avisar pelo retorno que o número é grande demais, porque qualquer long é um resultado possível. Então ele faz assim: devolve o maior long (LONG_MAX) ou o menor (LONG_MIN) e põe ERANGE no errno. Como funções que dão certo não zeram o errno, você zera antes:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <locale.h>
int main(void) {
setlocale(LC_ALL, "");
const char *textos[] = {"42", "99999999999999999999", "-99999999999999999999", "12abc", "abc"};
for (int i = 0; i < 5; i++) {
char *fim;
errno = 0;
long v = strtol(textos[i], &fim, 10);
if (fim == textos[i]) {
printf("%-22s → não é um número\n", textos[i]);
} else if (errno == ERANGE) {
printf("%-22s → grande demais: %s (veio %ld)\n", textos[i], strerror(errno), v);
} else if (*fim != '\0') {
printf("%-22s → %ld, mas sobrou \"%s\"\n", textos[i], v, fim);
} else {
printf("%-22s → %ld\n", textos[i], v);
}
}
return 0;
}
- errno = 0;
- Limpa o bilhete antes de cada conversão. Sem isso, um
ERANGEda volta anterior faria o "42" parecer grande demais. - strtol(textos[i], &fim, 10)
- Converte em base 10 e aponta
fimpara o primeiro caractere que não fez parte do número. - fim == textos[i]
- Não andou nada: não havia número. Nesse caso o
strtoldevolve 0 e o padrão C não manda mexer noerrno. - errno == ERANGE
- Havia número, mas não cabia num
long. - *fim != '\0'
- Converteu um pedaço e sobrou texto.
$ gcc -std=c23 -Wall -Wextra converte.c -o converte $ ./converte 42 → 42 99999999999999999999 → grande demais: Resultado numérico fora de alcance (veio 9223372036854775807) -99999999999999999999 → grande demais: Resultado numérico fora de alcance (veio -9223372036854775808) 12abc → 12, mas sobrou "abc" abc → não é um número
Experimente: rode o mesmo programa aqui. O long do editor tem 4 bytes, então o "veio" mostra 2147483647, e a frase sai em inglês. Acrescente "3000000000" à lista (mude o 5 para 6): três bilhões cabem no long do Linux, mas e aqui? Depois apague a linha errno = 0; e veja se algum resultado muda.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <locale.h>
int main(void) {
setlocale(LC_ALL, "");
const char *textos[] = {"42", "99999999999999999999", "-99999999999999999999", "12abc", "abc"};
for (int i = 0; i < 5; i++) {
char *fim;
errno = 0;
long v = strtol(textos[i], &fim, 10);
if (fim == textos[i]) {
printf("%-22s → não é um número\n", textos[i]);
} else if (errno == ERANGE) {
printf("%-22s → grande demais: %s (veio %ld)\n", textos[i], strerror(errno), v);
} else if (*fim != '\0') {
printf("%-22s → %ld, mas sobrou \"%s\"\n", textos[i], v, fim);
} else {
printf("%-22s → %ld\n", textos[i], v);
}
}
return 0;
}
perror ou strerror: qual usar?
| Forma | Quando usar |
|---|---|
perror("nome") | O caso comum: uma linha, formato padrão nome: motivo, já vai para o stderr. |
fprintf(stderr, "… %s\n", strerror(erro)) | Quando a mensagem precisa de mais informação (o nome que o usuário digitou, o número da linha) ou de outra ordem. |
if (erro == ENOENT) … | Quando o programa deve reagir diferente a cada motivo. |
errno parece uma variável global, mas o padrão diz que ele é uma macro que se comporta como variável. Na glibc ele vira algo como (*__errno_location()), para que cada thread do programa tenha o seu próprio errno (aula Threads). Por isso, nunca declare int errno; por conta própria: inclua <errno.h>.
Quando dá errado
O que significa: o código testou errno != 0 no lugar do retorno, e o errno tinha um valor velho (o exemplo errado.c da seção 5).
Como consertar: teste sempre o retorno da função (f == NULL, r != 0, n < pedido). Olhe o errno só dentro desse if.
O que significa: entre a função que falhou e o strerror(errno), outra função mudou o errno (um printf, um perror, um fclose). A seção 6 mostrou o perror trocando ENOENT por EINVAL quando a saída de erros vai para um arquivo. "Success" (em português, "Sucesso") é a frase do código 0: o errno foi zerado no caminho.
Como consertar: int erro = errno; na primeira linha depois do erro e, daí em diante, só erro.
error: ‘errno’ undeclared (first use in this function)
Um programa parecido com o lista.c da seção 4, mas que só tem #include <stdio.h> no topo:
$ gcc -std=c23 -Wall -Wextra semerrno.c -o semerrno
semerrno.c: In function ‘main’:
semerrno.c:6:13: error: ‘errno’ undeclared (first use in this function)
6 | if (errno == ENOENT) {
| ^~~~~
semerrno.c:2:1: note: ‘errno’ is defined in header ‘<errno.h>’; this is probably fixable by adding ‘#include <errno.h>’
1 | #include <stdio.h>
+++ |+#include <errno.h>
2 |
semerrno.c:6:13: note: each undeclared identifier is reported only once for each function it appears in
6 | if (errno == ENOENT) {
| ^~~~~
semerrno.c:6:22: error: ‘ENOENT’ undeclared (first use in this function)
6 | if (errno == ENOENT) {
| ^~~~~~
O que significa: faltou #include <errno.h>. O gcc até sugere a linha a acrescentar (o +++ |+). O perror sozinho não precisa dele (está em <stdio.h>), mas errno e os nomes ENOENT, ERANGE… precisam. No editor, o Clang diz use of undeclared identifier 'errno'.
Como consertar: acrescente #include <errno.h> no topo. Para strerror, <string.h>; para setlocale, <locale.h>.
O que significa: o programa não chamou setlocale(LC_ALL, ""), então usa o idioma neutro "C". No editor do guia, saem em inglês de qualquer jeito (não há traduções).
Como consertar: setlocale(LC_ALL, ""); no começo do main. Atenção: no Linux em português ele também faz o printf escrever 3,141590 em vez de 3.141590 (e o strtod esperar vírgula). Se isso atrapalhar, use setlocale(LC_MESSAGES, ""), que muda só as mensagens.
strtol diz "grande demais" para um número pequeno
O que significa: o errno não foi zerado antes da conversão e guardava um ERANGE antigo.
Como consertar: errno = 0; imediatamente antes de cada strtol/strtod.
Referência rápida
| Escrita | O que faz |
|---|---|
#include <errno.h> | Traz errno e os nomes ENOENT, EACCES, ERANGE… |
errno | Código do último erro; só vale logo depois de uma função que avisou que falhou |
perror("texto") | Escreve texto: motivo no stderr (<stdio.h>) |
strerror(codigo) | A frase do código (<string.h>) |
int erro = errno; | Guarda o código antes que outra função o mude |
errno = 0; | Zera antes de funções sem retorno de erro (strtol, strtod) |
if (errno == ENOENT) | Decide pelo motivo (sempre pelo nome, nunca pelo número) |
setlocale(LC_ALL, "") | Mensagens no idioma do sistema (<locale.h>) |
man errno | No terminal: a lista de códigos do Linux |
Exercícios
1. A frase pronta
Complete para mostrar dados.csv: motivo quando o fopen falhar:
2. O arquivo não existe
Complete o nome do código de erro para "arquivo ou diretório inexistente":
3. Antes do strtol
Complete a linha que deve vir imediatamente antes de long v = strtol(s, &fim, 10);:
4. Qual é o jeito certo de saber se o fopen falhou?
5. Por que comparar errno == 2 é má ideia?
6. O programa chama perror e, na linha seguinte, strerror(errno), e as duas mensagens são diferentes. O que aconteceu?
Missão final: criar a configuração na primeira vez
Missão: o programa lê config.txt, que não existe na primeira vez, e hoje morre com perror. No lugar marcado, faça assim: se o fopen falhou e o motivo é ENOENT, crie o arquivo com "w", grave volume=5, feche, mostre Criei config.txt com o padrão. e abra de novo para leitura. Qualquer outro erro continua indo para o perror. A saída deve ser:
Criei config.txt com o padrão. Configuração: volume=5
#include <stdio.h>
#include <errno.h>
int main(void) {
FILE *f = fopen("config.txt", "r");
// se faltou o arquivo (ENOENT), crie com o padrão aqui
if (f == NULL) {
perror("config.txt");
return 1;
}
char linha[100];
while (fgets(linha, sizeof linha, f) != NULL) {
printf("Configuração: %s", linha);
}
fclose(f);
return 0;
}
#include <stdio.h>
#include <errno.h>
int main(void) {
FILE *f = fopen("config.txt", "r");
// se faltou o arquivo (ENOENT), crie com o padrão aqui
if (f == NULL && errno == ENOENT) {
FILE *novo = fopen("config.txt", "w");
if (novo == NULL) {
perror("config.txt");
return 1;
}
fprintf(novo, "volume=5\n");
fclose(novo);
printf("Criei config.txt com o padrão.\n");
f = fopen("config.txt", "r");
}
if (f == NULL) {
perror("config.txt");
return 1;
}
char linha[100];
while (fgets(linha, sizeof linha, f) != NULL) {
printf("Configuração: %s", linha);
}
fclose(f);
return 0;
}
O && aqui é essencial: f == NULL && errno == ENOENT só olha o errno se o fopen falhou (aula Operadores Lógicos). É a regra de ouro escrita numa linha.
Resumo
- O retorno diz se a função falhou; o
errno(de<errno.h>) diz por quê. Olhe oerrnosó depois que o retorno avisou o erro. perror("nome")mostranome: motivonostderr;strerror(codigo)devolve a frase para você montar sua mensagem.- Com
setlocale(LC_ALL, ""), a glibc fala português ("Arquivo ou diretório inexistente"). No editor do guia, as frases ficam em inglês e os números dos códigos são outros. - Compare com os nomes (
ENOENT,EACCES,EISDIR,ERANGE…), nunca com números. - Funções que dão certo não zeram o
errnoe qualquer função pode mudá-lo: guarde comint erro = errno;logo depois do erro. - Para
strtol/strtod:errno = 0;antes,errno == ERANGEdepois.