Como um bom programador, deve-se escrever códigos robustos que lidem com todos os resultados do seu programa. No entanto, quase todas as funções da biblioteca C retornarão 0 ou -1 ou NULL quando houver um erro.
Às vezes, é óbvio que a verificação de erros é necessária, por exemplo, quando você tenta abrir um arquivo. Mas muitas vezes eu ignoro a verificação de erros em funções como printf
ou mesmo malloc
porque não me sinto necessário.
if(fprintf(stderr, "%s", errMsg) < 0){
perror("An error occurred while displaying the previous error.");
exit(1);
}
É uma boa prática ignorar apenas certos erros ou existe uma maneira melhor de lidar com todos os erros?
c
error-handling
Derek 朕 會 功夫
fonte
fonte
try
instrução, para que você não precise verificar todas as chamadas ou operações. (Observe também que alguns idiomas são melhores que outros na detecção de erros simples, como desreferenciação nula ou índice de matriz fora dos limites.)errno
! Caso você não esteja familiarizado, embora seja verdade que "quase todas as funções da biblioteca C retornarão 0 ou -1 ouNULL
quando houver um erro", elas também definem aerrno
variável global , que você pode acessar usando#include <errno.h>
e simplesmente lendo o valor deerrno
. Portanto, por exemplo, seopen
(2) retornar-1
, convém verificar seerrno == EACCES
, o que indicaria um erro de permissão ou oENOENT
que indicaria que o arquivo solicitado não existe.try
/catch
, embora você possa simulá-lo com saltos.Respostas:
Em geral, o código deve lidar com condições excepcionais sempre que apropriado. Sim, esta é uma afirmação vaga.
Em linguagens de nível superior, com manipulação de exceção de software, isso geralmente é declarado como "captura a exceção no método em que você realmente pode fazer algo a respeito". Se ocorreu um erro no arquivo, talvez você permita que a pilha chegue ao código da interface do usuário que pode realmente dizer ao usuário "seu arquivo não foi salvo no disco". O mecanismo de exceção engole efetivamente "todo pequeno erro" e o envolve implicitamente no local apropriado.
Em C, você não tem esse luxo. Existem algumas maneiras de lidar com erros, alguns dos quais são recursos de linguagem / biblioteca, outros são práticas de codificação.
Ignorar certos erros? Talvez. Por exemplo, é razoável supor que a gravação na saída padrão não falhará. Se falhar, como você diria ao usuário, afinal? Sim, é uma boa ideia ignorar certos erros ou codificar defensivamente para evitá-los. Por exemplo, verifique zero antes de dividir.
Existem maneiras de lidar com todos ou pelo menos a maioria dos erros:
Cascading
if
s:Teste a primeira condição, por exemplo, abrir ou criar um arquivo e, em seguida, supor que as operações subsequentes tenham êxito.
Código robusto é bom e deve-se verificar e manipular erros. Qual método é o melhor para o seu código depende do que o código faz, do quão crítica é uma falha etc., e somente você pode realmente responder a isso. No entanto, esses métodos são testados em batalha e usados em vários projetos de código aberto, onde você pode ver como o código real verifica se há erros.
fonte
stdout
pode ser redirecionado para um arquivo ou até não existir, dependendo do sistema.stdout
não é um fluxo "write to console", geralmente é isso, mas não precisa ser.stdout
... óbvio :-)dump_database > backups/db_dump.txt
falhar na gravação da saída padrão em um determinado momento, eu não gostaria que ele continuasse e saísse com êxito. (Não que os bancos de dados são copiados dessa maneira, mas o ponto ainda está de pé)Sim, mas você sabe para qual função chamou, não sabe?
Você realmente tem muitas informações que pode colocar em uma mensagem de erro. Você sabe qual função foi chamada, o nome da função que a chamou, quais parâmetros foram passados e o valor de retorno da função. São muitas informações para uma mensagem de erro muito informativa.
Você não precisa fazer isso para todas as chamadas de função. Mas a primeira vez que você vir a mensagem de erro "Ocorreu um erro ao exibir o erro anterior", quando o que você realmente precisava era de informações úteis, será a última vez que você verá essa mensagem de erro, porque você mudará imediatamente a mensagem de erro para algo informativo que o ajudará a solucionar o problema.
fonte
if (!myfunc()) {/*proceed*/}
. 0 não foi um erro e qualquer coisa diferente de zero foi um tipo de erro e cada tipo de erro teve seu próprio código diferente de zero. da maneira que um amigo meu disse: "existe apenas uma verdade, mas muitas falsidades". "0" deve ser "verdadeiro" noif()
teste e qualquer coisa diferente de zero deve ser "falso".if(function()) { // do something }
lê como "se a função for executada sem erro, faça alguma coisa". ou, melhor ainda, "se a função for executada com êxito, faça alguma coisa". Sério, quem entende a convenção não se confunde com isso. Retornarfalse
(ou 0) quando ocorrer um erro não permitiria o uso de códigos de erro. Zero significa falso em C, porque é assim que a matemática funciona. Veja programmers.stackexchange.com/q/198284TLDR; você quase nunca deve ignorar erros.
A linguagem C não possui um bom recurso de manipulação de erros, deixando para cada desenvolvedor de biblioteca implementar suas próprias soluções. Os idiomas mais modernos têm exceções, o que facilita muito o manuseio desse problema em particular.
Mas quando você está preso ao C, não tem essas vantagens. Infelizmente, você simplesmente terá que pagar o preço toda vez que chamar uma função, com a possibilidade remota de falha. Caso contrário, você sofrerá consequências muito piores, como sobrescrever dados na memória sem querer. Portanto, como regra geral, você sempre deve verificar se há erros.
Se você não verificar o retorno,
fprintf
é muito provável que você deixe um bug para trás que, na melhor das hipóteses, não fará o que o usuário espera e, na pior das hipóteses, explodirá a coisa toda durante o vôo. Não há desculpa para se prejudicar dessa maneira.No entanto, como desenvolvedor C, também é seu trabalho facilitar a manutenção do código. Portanto, às vezes, você pode ignorar com segurança os erros por uma questão de clareza se (e somente se) eles não representarem nenhuma ameaça ao comportamento geral do aplicativo. .
É o mesmo problema que fazer isso:
Se você perceber que sem bons comentários dentro do bloco de captura vazio, esse é certamente um problema. Não há motivo para pensar que uma chamada
malloc()
seja executada com sucesso 100% do tempo.fonte
malloc()
é uma função em que você definitivamente deseja verificar os valores de retorno!A questão não é realmente específica do idioma, mas específica do usuário. Pense na pergunta da perspectiva do usuário. O usuário faz algo, como digitar o nome do programa em uma linha de comando e pressionar Enter. O que o usuário espera? Como eles podem dizer se algo deu errado? Eles podem se permitir interceder se ocorrer um erro?
Em muitos tipos de código, essas verificações são um exagero. No entanto, em códigos críticos de segurança de alta confiabilidade, como os de reatores nucleares, a verificação de erros patológicos e os caminhos de recuperação planejados fazem parte da natureza cotidiana do trabalho. Considera-se vale o custo gastar um tempo para perguntar "O que acontece se o X falhar? Como faço para voltar a um estado seguro?" Em códigos menos confiáveis, como os de videogame, você pode obter muito menos verificação de erros.
Outra abordagem semelhante é quanto você pode realmente melhorar o estado capturando o erro? Não posso contar o número de programas em C ++ que orgulhosamente capturam exceções, apenas para repassá-los porque eles realmente não sabiam o que fazer com eles ... mas eles sabiam que deveriam lidar com exceções. Tais programas não ganharam nada com o esforço extra. Adicione apenas o código de verificação de erros que você acha que pode realmente lidar com a situação melhor do que simplesmente não verificar o código de erro. Dito isto, o C ++ possui regras específicas para lidar com exceções que ocorrem durante o tratamento de exceções, a fim de capturá-las de uma maneira mais significativa (e com isso quero dizer chamar terminate () para garantir que a pira funerária que você construiu para si acenda em sua própria glória)
Quão patológico você pode ficar? Eu trabalhei em um programa que definia um "SafetyBool" cujos valores verdadeiros e falsos eram cuidadosamente escolhidos para ter uma distribuição uniforme de 1 e 0 e foram escolhidos para que qualquer uma das várias falhas de hardware (inversão de bit único, barramento de dados rastreamentos quebrados etc.) não fizeram com que um booleano fosse mal interpretado. Desnecessário dizer que eu não afirmaria que essa é uma prática de programação de uso geral a ser usada em qualquer programa antigo.
fonte
fonte
Um pouco de uma visão abstrata sobre a questão. E não é necessariamente para a linguagem C.
Para programas maiores, você teria uma camada de abstração; talvez uma parte do mecanismo, uma biblioteca ou dentro de uma estrutura. Essa camada não se preocupam com o tempo você começa a dados válidos ou a saída seria algum valor padrão:
0
,-1
,null
etc.Depois, há uma camada que seria sua interface para a camada abstrata, que faria muito tratamento de erros e talvez outras coisas como injeções de dependência, escuta de eventos etc.
E mais tarde, você teria sua camada de implementação concreta, na qual você realmente define as regras e lida com a saída.
Portanto, minha opinião é que às vezes é melhor excluir completamente o tratamento de erros de uma parte do código, porque essa parte simplesmente não faz esse trabalho. E, em seguida, tenha algum processador que avalie a saída e aponte para um erro.
Isso é feito principalmente para separar responsabilidades que levam à legibilidade do código e a uma melhor escalabilidade.
fonte
Em geral, a menos que você tenha um bom motivo para não verificar essa condição de erro, deve. A única boa razão pela qual posso pensar para não verificar uma condição de erro é quando você não pode fazer algo significativo se ela falhar. Esta é uma barra muito difícil de encontrar, porque sempre há uma opção viável: sair normalmente.
fonte