Advertencia de “uso no inicializado” en el comstackdor g ++

Estoy usando g ++ con nivel de advertencia -Wall -Wextra y trato las advertencias como errores ( -Werror ).

Ahora a veces me sale un error “la variable puede usarse sin inicializar en esta función”.

Por “a veces” quiero decir que tengo dos unidades de comstackción independientes que incluyen el mismo archivo de encabezado. Una unidad de comstackción comstack sin error, la otra da el error anterior.

El fragmento de código relevante en los archivos de encabezado es el siguiente. Como la función es bastante larga, solo he reproducido el bit relevante a continuación.

El error exacto es:

‘cmpres’ puede usarse sin inicializar en esta función

Y he marcado la línea con el error de * abajo.

 for (; ;) { int cmpres; // * while (b <= c and (cmpres = cmp(b, pivot)) = b and (cmpres = cmp(c, pivot)) >= 0) { if (cmpres == 0) ::std::iter_swap(d--, c); --c; } if (b > c) break; ::std::iter_swap(b++, c--); } 

( cmp es un functor que toma dos punteros x e y devuelve –1, 0 o +1 si *x < *y , *x == *y *x > *y respectivamente. Las otras variables son punteros a la misma formación.)

Este fragmento de código es parte de una función más grande, pero la variable cmpres se utiliza en ninguna otra parte . Por lo tanto, no entiendo por qué se genera esta advertencia. Además, el comstackdor obviamente entiende que cmpres nunca se leerá sin inicializar (o al menos, no siempre advierte, ver más arriba).

Ahora tengo dos preguntas:

  1. ¿Por qué el comportamiento inconsistente? ¿Es esta advertencia generada por una heurística? (Esto es plausible ya que emitir esta advertencia requiere un análisis de flujo de control que es NP difícil en el caso general y no siempre se puede realizar).

  2. ¿Por qué la advertencia? ¿Es mi código inseguro? He llegado a apreciar esta advertencia en particular porque me ha salvado de detectar errores muy difíciles en otros casos, por lo que esta advertencia es válida, al menos a veces. ¿Es válido aquí?

Un algoritmo que diagnostica variables no inicializadas sin falsos negativos o positivos debe (como una subrutina) incluir un algoritmo que resuelva el problema de detención . Lo que significa que no hay tal algoritmo . Es imposible para una computadora obtener este derecho el 100% del tiempo.

No sé cómo funciona exactamente el análisis de variables sin inicializar de GCC, pero sí sé que es muy sensible a lo que los pases de optimización iniciales han hecho al código. Así que no me sorprende en absoluto que a veces obtengas falsos positivos. Distingue los casos en los que es cierto de los casos en los que no puede ser cierto.

 int foo() { int a; return a; } 

produce “advertencia: ‘a’ se usa sin inicializar en esta función” (énfasis mío).

EDITAR: encontré un caso en el que las versiones recientes de GCC (4.3 y posteriores) no diagnostican una variable sin inicializar:

 int foo(int x) { int a; return x ? a : 0; } 

Las optimizaciones iniciales notan que si x es distinto de cero, el comportamiento de la función es indefinido, por lo que suponen que x debe ser cero y reemplazan todo el cuerpo de la función con ” return 0; ” Esto sucede mucho antes del pase que genera las advertencias sin inicializar usadas, así que no hay diagnóstico. Ver GCC bug 18501 para detalles sangrientos.

Menciono esto parcialmente para demostrar que los comstackdores de grado de producción pueden obtener diagnósticos de variable sin inicializar incorrectamente en ambos sentidos, y en parte porque es un buen ejemplo del punto en que el comportamiento indefinido puede propagarse hacia atrás en el tiempo de ejecución. No hay nada indefinido acerca de probar x , pero como el código que depende de x tiene un comportamiento indefinido, un comstackdor puede asumir que la dependencia de control nunca está satisfecha y descartar la prueba.

Hubo una discusión interesante sobre la lista de correo de correo electrónico de Clang relacionada con esas heurísticas esta semana.

La conclusión es: en realidad, es bastante difícil diagnosticar valores unitarios sin obtener un comportamiento exponencial …

Aparentemente (de la discusión), gcc usa un enfoque de base de predicado, pero dada su experiencia, parece que no siempre es suficiente.

Sospecho que tiene algo que ver con el hecho de que la asignación se mezcla dentro de la condición (y después de un operador de cortocircuito en eso …). ¿Lo has intentado sin?

Creo que tanto la gente de gcc como la de clang estarían muy interesadas con este ejemplo, ya que es una práctica relativamente común en C o C ++ y, por lo tanto, podría beneficiarse de algunos ajustes.

El código es correcto, pero el comstackdor no identifica que la variable nunca se usa sin inicialización.

Yo sugeriría que es probable que se trate de un error heurístico, para eso está el “may”. Sospecho que no hay muchas condiciones de bucle así. Ese código no es inseguro porque en todas las rutas de control, cmpres se asigna antes del uso. Sin embargo, ciertamente no encontraría mal inicializarlo primero.

Sin embargo, podría tener algún tipo de sombra variable aquí. Esa sería la única explicación que podría pensar para solo una de las dos unidades de traducción que da errores.