Como buen programador, uno debe escribir códigos robustos que manejen cada resultado de su programa. Sin embargo, casi todas las funciones de la biblioteca C devolverán 0 o -1 o NULL cuando haya un error.
A veces es obvio que se necesita una comprobación de errores, por ejemplo, cuando intenta abrir un archivo. Pero a menudo ignoro la comprobación de errores en funciones como printf
o incluso malloc
porque no me parece necesario.
if(fprintf(stderr, "%s", errMsg) < 0){
perror("An error occurred while displaying the previous error.");
exit(1);
}
¿Es una buena práctica simplemente ignorar ciertos errores, o hay una mejor manera de manejar todos los errores?
c
error-handling
Derek 朕 會 功夫
fuente
fuente
try
declaración, por lo que no tiene que verificar cada llamada u operación. (También tenga en cuenta que algunos idiomas son mejores que otros para detectar errores simples como la anulación de referencia o el índice de matriz fuera de los límites).errno
! En caso de que no esté familiarizado, si bien es cierto que "casi todas las funciones de la biblioteca C devolverán 0 o -1 oNULL
cuando haya un error", también establecen laerrno
variable global , a la que puede acceder mediante el uso#include <errno.h>
y luego simplemente leyendo el valor deerrno
. Entonces, por ejemplo, siopen
(2) regresa-1
, es posible que desee verificar sierrno == EACCES
, lo que indicaría un error de permisos, oENOENT
, lo que indicaría que el archivo solicitado no existe.try
/catch
, aunque podría simularlo con saltos.Respuestas:
En general, el código debe tratar condiciones excepcionales donde sea apropiado. Sí, esta es una declaración vaga.
En los lenguajes de nivel superior con manejo de excepciones de software, esto a menudo se expresa como "detectar la excepción en el método en el que realmente puede hacer algo al respecto". Si se produjo un error en el archivo, tal vez deje que suba la pila al código de la interfaz de usuario que realmente puede decirle al usuario "su archivo no se pudo guardar en el disco". El mecanismo de excepción efectivamente absorbe "cada pequeño error" y lo maneja implícitamente en el lugar apropiado.
En C, no tienes ese lujo. Hay algunas formas de manejar los errores, algunas de las cuales son funciones de lenguaje / biblioteca, algunas de las cuales son prácticas de codificación.
¿Ignorar ciertos errores? Tal vez. Por ejemplo, es razonable suponer que la escritura en la salida estándar no fallará. Si falla, ¿cómo le diría al usuario, de todos modos? Sí, es una buena idea ignorar ciertos errores o codificar a la defensiva para evitarlos. Por ejemplo, verifique cero antes de dividir.
Hay formas de manejar todos, o al menos la mayoría de los errores:
En cascada
if
s:Pruebe la primera condición, por ejemplo, abrir o crear un archivo, luego suponga que las operaciones posteriores tienen éxito.
El código robusto es bueno, y uno debe verificar y manejar los errores. El método que mejor se adapte a su código depende de lo que haga el código, cuán crítico sea un fallo, etc., y solo usted puede realmente responderlo. Sin embargo, estos métodos se prueban en batalla y se usan en varios proyectos de código abierto donde puede ver cómo el código real verifica si hay errores.
fuente
stdout
puede ser redirigido a un archivo, o incluso no existe dependiendo del sistema.stdout
no es una transmisión de "escritura en consola", generalmente es eso, pero no tiene que ser así.stdout
... obvio :-)dump_database > backups/db_dump.txt
no puede escribir en la salida estándar en cualquier punto dado, no quisiera que continúe y salga con éxito. (No es que las bases de datos estén respaldadas de esa manera, pero el punto sigue en pie)Sí, pero sabes a qué función llamaste, ¿no?
En realidad, tiene mucha información que podría incluir en un mensaje de error. Usted sabe qué función se llamó, el nombre de la función que la llamó, qué parámetros se pasaron y el valor de retorno de la función. Esa es mucha información para un mensaje de error muy informativo.
No tiene que hacer esto para cada llamada de función. Pero la primera vez que vea el mensaje de error "Se produjo un error al mostrar el error anterior", cuando lo que realmente necesitaba era información útil, será la última vez que vea ese mensaje de error allí, porque inmediatamente va a cambiar el mensaje de error a algo informativo que lo ayudará a solucionar el problema.
fuente
if (!myfunc()) {/*proceed*/}
. 0 no fue ningún error, y cualquier cosa que no sea cero fue algún tipo de error y cada tipo de error tuvo su propio código distinto de cero. La forma en que lo expresó un amigo mío fue "solo hay una Verdad, pero muchas falsedades". "0" debe ser "verdadero" en laif()
prueba y todo lo que no sea cero debe ser "falso".if(function()) { // do something }
se lee como "si la función se ejecuta sin error, haga algo". o, mejor aún, "si la función se ejecuta con éxito, haga algo". En serio, los que entienden la convención no se confunden con esto. Devolverfalse
(o 0) cuando ocurre un error no permitiría usar códigos de error. Cero significa falso en C porque así es como funcionan las matemáticas. Ver programmers.stackexchange.com/q/198284TLDR; casi nunca debes ignorar los errores.
El lenguaje C carece de una buena característica de manejo de errores que permita a cada desarrollador de bibliotecas implementar sus propias soluciones. Los lenguajes más modernos tienen excepciones incorporadas que hacen que este problema en particular sea mucho más fácil de manejar.
Pero cuando estás atrapado con C no tienes tales ventajas. Desafortunadamente, simplemente tendrá que pagar el precio cada vez que llame a una función que existe una posibilidad remota de falla. O bien, sufrirá consecuencias mucho peores, como sobrescribir los datos en la memoria sin querer. Por lo tanto, como regla general, debe verificar los errores siempre.
Si no verifica el regreso
fprintf
, es muy probable que deje un error que, en el mejor de los casos, no hará lo que el usuario espera y, en el peor de los casos, explotará todo durante el vuelo. No hay excusa para debilitarte de esa manera.Sin embargo, como desarrollador de C, también es su trabajo hacer que el código sea fácil de mantener. Por lo tanto, a veces puede ignorar los errores de manera segura en aras de la claridad si (y solo si) no representan una amenaza para el comportamiento general de la aplicación. .
Es el mismo problema que hacer esto:
Si ves que sin buenos comentarios dentro del bloque catch vacío, este es ciertamente un problema. No hay razón para pensar que una llamada
malloc()
se ejecutará con éxito el 100% del tiempo.fuente
malloc()
¡es una función en la que definitivamente desea verificar los valores de retorno!La pregunta no es en realidad específica del idioma, sino específica del usuario. Piensa en la pregunta desde la perspectiva del usuario. El usuario hace algo, como escribir el nombre del programa en una línea de comando y presionar enter. ¿Qué espera el usuario? ¿Cómo pueden saber si algo salió mal? ¿Pueden permitirse el lujo de interceder si se produce un error?
En muchos tipos de código, tales controles son excesivos. Sin embargo, en el código crítico de seguridad de alta confiabilidad, como los de los reactores nucleares, la verificación de errores patológicos y las rutas de recuperación planificadas son parte de la naturaleza cotidiana del trabajo. Se considera que vale la pena tomarse el tiempo para preguntar "¿Qué sucede si X falla? ¿Cómo vuelvo a un estado seguro?" En un código menos confiable, como los de los videojuegos, puede escapar con mucha menos comprobación de errores.
Otro enfoque similar es ¿cuánto puede mejorar realmente el estado al detectar el error? No puedo contar la cantidad de programas de C ++ que orgullosamente capturan excepciones, solo para volver a lanzarlos porque en realidad no sabían qué hacer con ellos ... pero sabían que se suponía que debían manejar las excepciones. Tales programas no ganaron nada del esfuerzo extra. Solo agregue el código de verificación de errores que cree que realmente puede manejar la situación mejor que simplemente no verificar el código de error. Dicho esto, C ++ tiene reglas específicas para manejar excepciones que ocurren durante el manejo de excepciones con el fin de atraparlas de una manera más significativa (y con eso, me refiero a llamar a terminar () para asegurarse de que la pira funeraria que ha construido para usted mismo se enciende en su propia gloria)
¿Qué tan patológico puedes ser? Trabajé en un programa que definió un "SafetyBool" cuyos valores verdaderos y falsos fueron elegidos cuidadosamente para tener una distribución uniforme de 1 y 0, y fueron elegidos para que cualquiera de una serie de fallas de hardware (volteos de un solo bit, bus de datos rastros que se rompen, etc.) no causaron que un booleano se malinterpretara. No hace falta decir que no diría que se trata de una práctica de programación de propósito general para ser utilizada en cualquier programa antiguo.
fuente
fuente
Un poco de resumen de la cuestión. Y no es necesariamente para el lenguaje C.
Para programas más grandes, tendría una capa de abstracción; quizás una parte del motor, una biblioteca o dentro de un marco. Esa capa no se preocupan por el tiempo se obtiene un conjunto de datos válidos o la salida sería algún valor por defecto:
0
,-1
,null
etc.Luego hay una capa que sería su interfaz con la capa abstracta, que manejaría muchos errores y tal vez otras cosas como inyecciones de dependencia, escucha de eventos, etc.
Y luego tendría su capa de implementación concreta donde realmente establece las reglas y maneja la salida.
Entonces, mi opinión sobre esto es que a veces es mejor excluir completamente el manejo de errores de una parte del código porque esa parte simplemente no hace ese trabajo. Y luego tenga algún procesador que evalúe la salida y señale un error.
Esto se hace principalmente para separar las responsabilidades que conducen a la legibilidad del código y a una mejor escalabilidad.
fuente
En general, a menos que tenga una buena razón para no verificar esa condición de error, debería hacerlo. La única buena razón por la que puedo pensar para no verificar una condición de error es cuando no es posible hacer algo significativo si falla. Sin embargo, esta es una barra muy difícil de cumplir, porque siempre hay una opción viable: salir con gracia.
fuente