¿Deberíamos preocuparnos por las advertencias en la programación?

Mi opinión cuando comencé a programar por primera vez era “a quién le importa”, pero pronto me di cuenta de que era muy importante para mí silenciar las advertencias, y no siempre de la manera más directa. Tengo una enorme pieza de trabajo personal que tiene cientos de miles de líneas y se construye con 0 advertencias.

El uso de #pragma para desactivar o silenciar las advertencias no las soluciona.

Algunas cosas de las que le gustaría que le advirtieran solo pueden aparecer cuando utiliza herramientas de análisis de código.

A veces no ve advertencias cuando cree que debería hacerlo: corrija el código de todos modos y prepárese para usar las mejores prácticas cuando se trata de cometer errores comunes. Siempre hay una solución, pero a veces se necesita iniciativa para corregir grandes secciones de su programa. Ha habido algunas veces (como 10 veces en 100,000 líneas) cuando he encontrado advertencias que simplemente no pude arreglar porque parte del código estaba fuera de mi control. Sin embargo, dado que las advertencias no eran “importantes” (señalaron cosas que no eran un gran problema) usé #pragma para silenciarlas solo para secciones específicas de código.

Con respecto a sus advertencias, debe lidiar con cada una de ellas, con respecto a “sin inicializar”, y debe saber que todo debería inicializarse en C o C ++, en la medida en que no coincida el tipo, esto puede causar fugas de memoria, indefinido El comportamiento y, más comúnmente, la memoria se sobrescribe; a su vez, esto lleva a que los programas se bloqueen y se comporte de forma errática.

Veo que tienes las típicas respuestas cliched aquí. “¡Cada advertencia puede ser un error, así que corrígelos todos!”, Y “la mayoría de las advertencias NO son errores, ¡por lo que es más riesgo corregirlos!”

Excelente. Un montón de personas que han bebido el koolaid estándar de un lado, o tienen una respuesta emocional de una manera u otra y usan el koolaid estándar de ese lado para defenderlo. Supongo que la mayoría de las otras respuestas provienen de ese lugar, porque, ya saben, así es como se obtienen respuestas superficiales que se han repetido mil veces sin participar realmente en una discusión profunda y significativa de las realidades.

Así que vamos a tener una discusión profunda y significativa de las realidades.

Una advertencia no es exactamente una función de “advertencia sobre posibles errores”. Es el compilador (un intérprete de idiomas) que le dice que ha abusado del idioma de alguna manera. Como un francés que le dice a un americano que trató de hablar francés “Tu francés es terrible. Pero me las arreglé para conseguir lo que dices de todos modos “. El compilador le advierte que puede ser mal entendido, porque no se explicó claramente. Para continuar con la analogía con un chico francés, también puede decirte que una palabra que usaste está desactualizada y que algunos podrían no entenderla, y también puede que te diga que lo que estás pidiendo está mal visto (pero no es ilegal).

Para que quede claro: un compilador no puede advertirle que ha realizado una conversión incorrecta de millas a kilómetros, porque no entiende esos conceptos y no sabe qué son ni por qué lo haría. Un compilador puede advertirle si ha convertido un número a un tipo menos preciso, sin indicar específicamente que desea hacerlo. Eso podría ser un error. Tal vez intentaste truncar ese valor y simplemente olvidaste declararlo explícitamente. Tal vez no quiso truncar ese valor, pero los parámetros operativos son tales que nunca se encuentra el truncamiento. Tal vez no quiso truncar ese valor y los resultados son catastróficos e inmediatamente se evidencia que cometió un error. O tal vez sus pruebas de unidad (usted … tiene pruebas de unidad, ¿verdad? Llegaré a eso) no verificaron los valores lo suficientemente altos, y el truncamiento de valores no se ve hasta algún caso del mundo real donde el valor es más alto que el de cualquiera. Pensado para probar.

Tal vez ese valor fue el valor de empuje del cohete y el valor redondeado acaba de enviar a algunos astronautas a lanzarse hacia el sol. En este caso, tiene un error y la solución típica para la advertencia: “agregar un modelo” reparará la advertencia pero no solucionará el error.

O tal vez ese valor nunca exceda de 255, pero lo lee de una biblioteca que mete todo en enteros de 64 bits, y ahora necesita realizar un cálculo con ese valor contra otros enteros de 16 bits y almacenar el resultado en un campo de 16 bits. . Una vez más, la estrategia estándar de “agregar un reparto” solucionará la advertencia, pero para empezar, nunca hubo ningún error.

Tal vez el compilador le advierte que está comparando un int firmado con un int sin firmar, y lo arregla con un lanzamiento a int sin firmar. Ups. Si la firma firmada fue negativa, su comparación ahora tendrá falsos positivos y falsos negativos. Al corregir la advertencia, causaste un error que no estaba allí antes.

Hecho : la gran mayoría de las advertencias no son errores, y nunca causarán errores.

Hecho: Un pequeño porcentaje de advertencias son errores

Hecho: Un porcentaje muy pequeño de advertencias es catastrófico . Al igual que en, son un error en el que las personas podrían morir, las empresas o las economías podrían colapsar, las personas podrían perder sus empleos (incluido usted).

Hecho: Un pequeño porcentaje de advertencias no son errores, pero intentar solucionarlo es un riesgo de que pueda causar errores.

Ahora es la parte en la que voy a refutar a las personas que dicen que no deben limpiar las advertencias debido al riesgo del último caso. ¿Recuerdas antes cuando pregunté por pruebas de unidad? ¿Esas personas que están preocupadas por causar un error que no estaba allí antes? Es una buena posibilidad que no tengan ninguna prueba de unidad. Porque si tienen buenas pruebas de unidad, las pruebas detectarán su error, ya que no fue un caso no anticipado sino un caso de trabajo conocido que tuvo una buena prueba de unidad, ¿verdad? ¿¡¿¡¿CORRECTO?!?!?

Ahora voy a refutar el argumento de que no vale la pena el esfuerzo porque hay muchas advertencias y casi ninguna tiene errores (también conocido como costo / beneficio).

En dos ocasiones me han dado la propiedad de grandes bases de código existentes que estaban llenas de advertencias. En la primera ocasión, al principio no hice nada y viví con ello durante un año y medio. Entonces tuvimos un error bastante desastroso. Una vez que localicé el error, encontré que el culpable era un trazo de línea, con una advertencia sobre un tipo de problema de seguridad. Esa advertencia, en esa línea de código, habría sido completamente evidente que era una bomba de tiempo. Entonces, ¿por qué no se ha arreglado ya? Porque ese producto tenía más de 10.000 advertencias. Se perdió entre todos los sin sentido. Para ponerlo en términos de procesamiento de señales, la advertencia es una señal de emergencia, y todas las demás advertencias no importantes son ruido. Y al igual que con cualquier otra señal … demasiado ruido y no puede recibir el mensaje.

En ese momento hice de la limpieza de las advertencias una prioridad. Y establecí que era una regla de equipo que una vez que un módulo estaba libre de advertencias, las advertencias como errores se activaban para ese módulo y no se volvían a apagar. Tomó bastante tiempo, pero eventualmente ese código estuvo libre de avisos. En el proceso de limpieza, causamos 10 errores nuevos que fueron detectados rápidamente por nuestras pruebas de rutina y reparados. Encontramos 3 errores menores que nunca se habían reportado, pero que probablemente habían causado un comportamiento extraño en la producción, y 4 errores que también estaban marcando bombas de tiempo de proporciones catastróficas. Esas 4 bombas de tiempo se difundieron antes de explotar, y le ahorré a mi equipo 4 grandes dolores de cabeza.

Te diré algo más que aprendimos. Todas esas advertencias son de salida de cadena desde el compilador. ¡Eso es un montón de datos! Una vez que esas salidas de cadena desaparecieron, nuestro código se compiló más rápido y utilizó menos recursos del sistema durante la compilación. Si ese argumento no te entiende, ¡no sé qué haré!

Así que limpia las advertencias. Porque son peligrosos. Pero hazlo con cuidado. Inspecciona cada caso. Tome su tiempo. Tenga en cuenta las advertencias que tienen más probabilidades de ser errores y las advertencias que probablemente causen errores cuando intente solucionarlos. Y adopte el hábito de no dejar las advertencias en primer lugar con su propio código.

Las advertencias se encuentran en la mayoría de los casos de errores VAST (casi todos los casos en código de principiante). Entenderlos y arreglarlos. Si no los entiendes, entonces definitivamente debes tratarlos como bichos.

Algunos le están diciendo esencialmente que debe preocuparse a menos que esté seguro de que no importan. Esto puede ser cierto, pero los principiantes generalmente no tienen el conocimiento y el juicio para tomar la decisión. Por lo tanto, debe adoptar la práctica de comprender y abordar todas las advertencias (lo que exijo que hagan mis alumnos). Por cierto, todas las advertencias que cita arriba son errores en su programa. ¡Arreglalos! Si crees que el programa funciona, es posible que tengas suerte con los casos de prueba que has elegido o con la implementación en la que te estás ejecutando, pero estas son bombas de tiempo.

El uso que hace el compilador de las palabras en inglés “advertencia” y “error” no ayuda. Es mejor pensar en ellos de esta manera: “Error” significa “esto está roto; No puedo generar código para eso ”. Eso incluye cosas como variables no definidas o tipos en conflicto. “Advertencia” significa “esto está roto, pero puedo generar un código que hará algo con lo que no debería contar”. Por ejemplo, “no devolver el valor de la función no vacía” es una advertencia, porque el compilador simplemente puede poner Una función de retorno allí. Esto puede significar que en algunos sistemas, obtendrá un valor de retorno en ese caso que es lo que quedó en el registro del acumulador, puede obtener algo que parece razonable, o puede tener una implementación en la que la persona que llama aparecerá. el valor de retorno existente de la pila, y ahora el estado de la pila se ha arruinado y casi cualquier cosa puede suceder. Si realmente ha cubierto todas sus bases y el compilador está equivocado, entonces esto requiere documentación y tal vez un “otro” o “predeterminado” con una afirmación de que no puede suceder. De manera similar, los valores sin inicializar no impiden que se genere el código: el compilador puede leer el valor de una variable y obtendrá lo que obtiene (por cierto, he visto este cambio entre las versiones del compilador). Los 20 a 30 segundos que le toma para abordar la advertencia y corregir el programa pueden ahorrarle HORAS de depuración cuando se presente el problema.

Debes habilitar todas las advertencias y tu código debe compilarse sin reportar ninguna de ellas.

Eso lleva trabajo. Se requiere agregar código o pragmas que de otro modo no necesitaría para que el programa se compile de forma limpia. Hace que el programa sea más desordenado.

Pero hay dos muy buenas razones para poner ese esfuerzo y recibir ese golpe.

Cada advertencia es una oportunidad para eliminar un error potencial, y los dos que se muestran arriba están ciertamente en esta categoría. La habilitación de todas las advertencias le brinda la mejor oportunidad de detectar errores. Debes investigar cada informe hasta que entiendas exactamente por qué sucede y estás seguro de que es inocente.

Pero tenemos una API estándar para todos nuestros métodos que pasa una estructura de entrada y una estructura de salida.

Función nula (FUNCTION_PIN * pIn, FUNCTION POUT * pOut)

A veces no tenemos nada útil para ingresar, pero aún tenemos el parámetro allí, y el compilador nos avisa cada vez que lo hacemos. Eso puede ser docenas de veces en un solo programa. Es lo suficientemente simple como para deshacerse de él (al menos en VC).

Función nula (FUNCTION_PIN * pIn, FUNCTION POUT * pOut)
{
alfiler; // pIn no se usa

Si no hiciéramos eso, entonces todos los programas tendrían muchas líneas de advertencias sobre parámetros no utilizados, y uno de ellos podría ser un error . Si fuera así, lo más probable es que no lo notáramos; Solo los escanearíamos y no veríamos nada más que advertencias de parámetros no utilizados. Incluso podríamos perder un tipo diferente de advertencia entre los demás. El equipo que realiza las compilaciones normales ciertamente no tendrá idea de si se esperaba una advertencia o indicativo de un error introducido.

Por lo tanto, insistimos en que un programa no se acepta para su lanzamiento hasta que se compile con todas las advertencias habilitadas y ninguna reportada.

En una vida anterior, me formé como contador. El contador senior me dijo algo que creo que es relevante.

“No importa si los números no suman, siempre que sepas por qué no se suman”.

Básicamente, quiero decir que las advertencias están bien si sabes por qué y entiendes por qué están allí.

En su caso, tiene advertencias y no sabe cuál es el problema. Sí, tienes que preocuparte por eso.

Prefiero llevar mi software a una compilación sin advertencias, pero no siempre es práctico, por ejemplo, si Apple desaprueba una API, recibiré una advertencia, pero a menudo la API no se reemplaza fácilmente, seguro que lo haré Un día, pero no estoy reescribiendo todo un sistema FTP para deshacerme de una advertencia.

Sí.

Cada advertencia es un error potencial.

Veamos sus ejemplos –

  1. “Formato% s” espera un carácter *, pero el argumento es de tipo
    Estás pasando algo del tipo equivocado. Probablemente sea fácil de solucionar: la etiqueta de formato (% s,% d o similar) es incorrecta o el tipo es incorrecto. ¿Qué debe imprimir cuando se pasa un entero en lugar de un puntero?
    Los controles de tipo aseguran que las cosas encajen. Ellos son tus amigos
  2. “‘Cliente’ se puede utilizar sin inicializar en esta función”
    Si usa una variable antes de que se inicie, pueden ocurrir cosas malas. No se sabe qué sucederá, todo depende del valor que esté allí. La próxima vez que ejecute el programa, ese valor puede ser diferente y sucederán cosas inesperadas.
    Inicializa tus variables para que estés seguro de lo que sucederá.

Son fáciles de arreglar.
Es una buena práctica ejecutar el compilador al máximo nivel de advertencia y silenciar cada advertencia al corregir lo que sugiere. El código puede ejecutarse, puede que no sea “incorrecto” y, a veces, técnicamente se le puede permitir hacer lo que está intentando, pero hay algo que pica.

Si no estás convencido, aquí hay otra razón:
Has heredado un código que parece funcionar. A veces falla, pero bueno, así es la vida. Se compila con 573 avisos. Se te ha pedido que agregues una característica, la codificas. Después de la recompilación ahora hay 575 advertencias.
¿Cuáles eran las tuyas? ¿El código está fallando más? ¿Causaste un nuevo error o ya estaba allí? (*)

Apunta a 0 errores, 0 advertencias. Hazlo ahora antes de que tengas que arreglar 573.


(*) He sido el chico que tuvo que arreglar 573 avisos. Ese no fue un momento feliz.
Entre muchas otras cosas, por alguna razón, el código utilizó una mezcla de “int” y “unsigned int”. Algunas funciones tenían un retorno de “error” separado, otras utilizaban la convención de “los valores positivos son buenos, los valores negativos son errores”. Mezclar convenciones nunca es bueno.

Comprobando si una cadena de llamadas devuelve un error buscando

sin signo int retVal;

/ * El código va aquí, por lo que el int sin signo se desplaza de la pantalla * /

retVal = doFunction (…);
if (retVal <0) {

}

no va a funcionar Maldije al chico que vino antes que yo en voz baja.

No seas ese chico.

Esas advertencias particulares, sí.

#include
int main () {
int x;
char c = ‘x’;
printf (“x nunca recibió un valor, por lo que la basura en x es% d \ n”, x);
printf (“Quiero un puntero y me dio un carácter% s \ n”, c);
}

El segundo printf colapsará el programa en la mayoría de los casos.

Las advertencias deben tomarse en serio y eliminarse cuidadosamente.

Por supuesto, no todas las advertencias son signos de problemas, aunque los cuatro que muestran todos me parecen posibles problemas. Pero algunas advertencias definitivamente deben tomarse en serio. Y si tiene la costumbre de ignorar las advertencias, pronto ignorará las que son realmente importantes.

Por lo tanto, siempre debes entender las advertencias y trabajar para silenciarlas. En ocasiones, eso implica una pequeña cantidad de trabajo que no es estrictamente necesario. Ese es un pequeño precio para pagar los problemas que usted ahorrará en las advertencias necesarias.

Las advertencias son posibles errores.

Las advertencias que recibió son errores reales y pueden causar un comportamiento indefinido.

Intenta entender lo que te dice el compilador y arréglalo. Un compilador es más inteligente que el 90% de los programadores.

Siempre se deben activar todas las advertencias al compilar C y C ++.

Las dos primeras advertencias se deben a que le está diciendo al programa que espera una cadena (% s) pero que la variable en la que lo lee es un char (en lugar de char *)

Las dos últimas advertencias se deben a que declara una variable y quizás la inicializa en una declaración condicional y luego la usa. Dado que el compilador no sabe si la condición se alcanzará o no siempre, le indica que puede causarle problemas.

Los dos primeros se fijan cambiando% s a% c, los dos últimos inicializando la variable al declararla (es decir, int i = 0 en lugar de int i)

Es una buena práctica dejar el programa con 0 advertencias, ya que ayuda a prevenir errores y / o ecploits, pero no siempre es posible.

¡La mejor de las suertes!

Sí.
¡Sí!
¡SÍ!
Nunca se convertirá en un programador eficaz si no sabe por qué todos sus mensajes de advertencia saltan.
Luego, cuide de ser posible, cuídese de tal manera que no salgan y sepa por qué es seguro ignorar el uno o dos con los que se sienta cómodo.
Por ejemplo, el uso de datos no inicializados generalmente da como resultado un comportamiento aleatorio y potencialmente calamitoso en C. Es prudente considerar que el compilador puede dejar de estar inicializado en el cliente, determinar si está de acuerdo y abordarlo de todos modos. No tengo idea de qué es el cliente, pero al menos si lo establece en NULL o 0, obtendrá un resultado reproducible. C no tiene un estado predeterminado para datos no estáticos y lo que puede suceder es que, por coincidencia, la memoria relevante siempre se establece en un valor inofensivo, entonces un cambio aparentemente no relacionado en otra parte del proyecto significa que el valor cambia y esta parte se rompe.
Ese cambio podría estar pasando de la versión ‘debug’ a la versión ‘release’, que a menudo es más tardía de lo que le gustaría ver como aparece un comportamiento aleatorio alrededor del lugar.
Echa un vistazo a todas sus advertencias. Encienda todas las advertencias. Sigue todas las advertencias. Entiéndelos, sácalos. Casi siempre hay una forma de deshacerse de las advertencias de C (por ejemplo, el lanzamiento es peligrosamente poderoso), pero eso no significa que haya eliminado el problema. Puede que lo hayas escondido.

No son solo los principiantes, incluso para los programadores más experimentados, una advertencia inesperada significa (99% de las veces) “Usted acaba de hacer algo estúpido, peligroso e innecesario que posiblemente comprometa la validez de su programa”.
Si alguna vez tiene una carrera profesional y trabaja para algún tipo de equipo profesional y lo envía a una revisión, se le pedirá que lo arregle antes de que alguien se moleste en considerarlo.

Algunas advertencias son inofensivas, pero le aconsejamos que averigüe qué significan las advertencias antes de decidir si, de hecho, son inofensivas. Mi hábito personal (he estado programando durante casi 50 años) es eliminar todas las advertencias que pueda. Demasiados mensajes de advertencia inofensivos pueden hacer que sea más fácil pasar por alto los que importan. Según mi experiencia, las dos primeras advertencias anteriores probablemente reflejen cosas que deben solucionarse (en realidad me sorprende que no estén marcadas como errores), las dos segundas son más difíciles de asegurar sin ver su programa.

Obtener una advertencia no es algo bueno. Es una buena práctica mantener su aviso de programación libre. Cada vez que veo una advertencia en mi programa, la leo para que esté libre de advertencias haciendo algunos cambios en el código.

Saludos,
Crish Watson
Pasar la certificación de Microsoft sin tomar el examen

Por lo general, no me preocupo por las advertencias cuando estoy programando sobre la marcha … pero hay que admitir que esta práctica puede llevar a muy malos hábitos. Si está trabajando en un grupo o parte de un equipo, siempre preste atención a las advertencias … no solo mejorará el respeto entre pares, sino que las advertencias pueden convertirse en problemas importantes al integrar módulos de software.

Definitivamente, preocuparse por esos dos últimos: el uso de una variable sin inicializar no será agradable.

Lo mejor es corregir las advertencias tan pronto como sea posible e ir por cero errores, cero advertencias.

A veces, no puedes. Al igual que cuando se utiliza una biblioteca de terceros.

En esos casos, recomiendo usar #pragmas si puede (o anotaciones en Java, o lo que esté disponible) para marcar las advertencias como ‘ignorar advertencia’.

De esa manera, vuelve a las advertencias que significan algo y, como en los dos últimos, pueden estar tratando de decirle algo significativo.

Sin ver el código, las advertencias que recibe son errores potenciales, por lo que es mejor corregirlas para evitar resultados no válidos.

Considere un sistema que consiste en 100000 líneas de código. ¿Cómo sabes que ninguno de los cientos de advertencias generadas no son reales?

Al codificar en PHP, establezco el nivel de advertencia más alto y no permito ninguna advertencia. El motivo es que solo quiero ver advertencias y errores reales (y nuevos), no causados ​​por una programación descuidada / perezosa. Si hay alguna advertencia o error, se registra y recibo un correo electrónico, así que es mejor que lo repare de inmediato.

Sí, deberías preocuparte por esto, ya que cada uno de ellos causará problemas en el tiempo de ejecución.

Verifique el tipo de los argumentos 3 y 4 en la línea 101; son del tipo incorrecto (char en lugar de char *. Alternativamente, es posible que desee cambiar% sa% c si lo que está imprimiendo son caracteres únicos)

Las líneas 48 y 60 leen variables sin inicializar. Esto, dependiendo de su tipo, puede o no ser un problema, pero definitivamente es un signo de un defecto, ya que dudo que la intención de su código fuera leer la basura aleatoria. Verifique su lógica y asegúrese de que a las variables se les asignen los valores antes de leerlos.

No voy a depurar tu programa. Pero les voy a decir que cada compilador de C tiene una opción que dice “tratar todas las advertencias como errores”, y cada programador profesional activa esa opción como una cuestión de rutina. Así que su programa no “funciona muy bien”; Ni siquiera compila.

Sí, deberías preocuparte. Todos esos son errores bastante graves.