c

El compilador no detecta la variable obviamente no inicializada



(4)

Todos los compiladores de C que he probado no detectarán variables no inicializadas en el fragmento de código a continuación. Sin embargo, el caso es obvio aquí.

No te preocupes por la funcionalidad de este fragmento. No es un código real, y lo eliminé para investigar este problema.

BOOL NearEqual (int tauxprecis, int max, int value) { int tauxtrouve; // Not initialized at this point int totaldiff; // Not initialized at this point for (int i = 0; i < max; i++) { if (2 < totaldiff) // At this point totaldiff is not initialized { totaldiff = 2; tauxtrouve = value; // Commenting this line out will produce warning } } return tauxtrouve == tauxprecis ; // At this point tauxtrouve is potentially // not initialized. }

Por otro lado, si comento tauxtrouve = value ; , "local variable ''tauxtrouve'' used without having been initialized" .

Probé estos compiladores:

  • GCC 4.9.2 con -Wall -WExtra
  • Microsoft Visual C ++ 2013 con todas las advertencias habilitadas

Esta respuesta solo se dirige a GCC.

Después de más investigaciones y comentarios, hay más cosas que en mi respuesta anterior. Este fragmento de código tiene dos variables no inicializadas, y cada una de ellas no se detecta por una razón diferente.

En primer lugar, la documentación de GCC para la opción -Wuninitialized dice:

Debido a que estas advertencias dependen de la optimización, las variables o elementos exactos para los cuales existen advertencias dependen de las opciones de optimización precisas y la versión de GCC utilizada.

Las versiones anteriores del manual de GCC lo expresaron de manera más explícita. Aquí hay un extracto del manual para GCC 3.3.6 :

Estas advertencias solo son posibles en la optimización de la compilación, ya que requieren información del flujo de datos que se calcula solo al optimizar. Si no especifica -O, simplemente no recibirá estas advertencias.

Parece que la versión actual puede dar algunas advertencias sin variables no inicializadas sin -O , pero aún así obtendrá mejores resultados.

Si compilo su ejemplo usando gcc -std=c99 -Wall -O , obtengo:

foo.c: In function ‘NearEqual’: foo.c:15:21: warning: ‘tauxtrouve’ is used uninitialized in this function [-Wuninitialized] return tauxtrouve == tauxprecis ; // at this point tauxtrouve is potentially ^

(Tenga en cuenta que esto es con GCC 4.8.2 ya que no tengo instalado 4.9.x, pero el principio debería ser el mismo).

Entonces eso detecta el hecho de que tauxtrouve no está inicializado.

Sin embargo, si arreglamos parcialmente el código agregando un inicializador para tauxtrouve (pero no para totaldiff ), entonces gcc -std=c99 -Wall -O acepta sin ninguna advertencia. Esto parecería ser una instancia del "error" citado en la respuesta de los hacks .

Hay dudas sobre si esto realmente debería considerarse un error: GCC no promete capturar todas las instancias posibles de una variable no inicializada. De hecho, no puede hacerlo con una precisión perfecta, porque ese es el problema de detención . Entonces, advertencias como esta pueden ser útiles cuando funcionan, ¡pero la ausencia de advertencias no prueba que su código esté libre de variables no inicializadas! Realmente no son un sustituto para verificar cuidadosamente su propio código.

En el informe de error vinculado por haccks , se discute mucho sobre si el error es incluso reparable, o si intentar detectar esta construcción en particular daría como resultado una tasa de falsos positivos inaceptable para otro código correcto.


La evidencia con la que esta variable no se inicializa es exagerada. El análisis de ruta cuesta tiempo y los proveedores de su compilador no querían implementar la función o pensaron que le costaría demasiado tiempo, o simplemente no se inscribió explícitamente.

Por ejemplo, con clang :

$ clang -Wall -Wextra -c obvious.c $ clang -Wall -Wextra --analyze -c obvious.c obvious.c:9:11: warning: The right operand of ''<'' is a garbage value if (2 < totaldiff) // at this point totaldiff is not initialized ^ ~~~~~~~~~ obvious.c:16:21: warning: The left operand of ''=='' is a garbage value return tauxtrouve == tauxprecis ; // at this point tauxtrouve is potentially ~~~~~~~~~~ ^ 2 warnings generated.

La diferencia en el tiempo de ejecución para estos ejemplos ingenuos es insignificante. Pero imagine una unidad de traducción con miles de líneas, decenas de funciones, cada una con bucles y anidamiento pesado. El número de rutas se agrava rápidamente y se convierte en una gran carga para analizar si la primera iteración a través del ciclo si la asignación ocurrirá antes de esa comparación.

EDITAR: @Matthieu señala que con LLVM / clang, el análisis de ruta requerido para encontrar el uso del valor no inicializado no se combina a medida que aumenta el anidamiento debido a la notación SSA utilizada por el IR.

No era tan simple como " -S -emit-llvm " como esperaba, pero encontré la salida de notación SSA que describió. Seré sincero, no estoy lo suficientemente familiarizado con LLVM IR para estar seguro, pero aceptaré la palabra de Matthieu.

En --analyze : use clang con --analyze , o convenza a alguien para que solucione el error de gcc .

; Function Attrs: nounwind uwtable define i32 @NearEqual(i32 %tauxprecis, i32 %max, i32 %value) #0 { br label %1 ; <label>:1 ; preds = %7, %0 %tauxtrouve.0 = phi i32 [ undef, %0 ], [ %tauxtrouve.1, %7 ] %i.0 = phi i32 [ 0, %0 ], [ %8, %7 ] %2 = icmp slt i32 %i.0, %max br i1 %2, label %3, label %9 ; <label>:3 ; preds = %1 %4 = icmp slt i32 2, 2 br i1 %4, label %5, label %6 ; <label>:5 ; preds = %3 br label %6 ; <label>:6 ; preds = %5, %3 %tauxtrouve.1 = phi i32 [ %value, %5 ], [ %tauxtrouve.0, %3 ] br label %7 ; <label>:7 ; preds = %6 %8 = add nsw i32 %i.0, 1 br label %1 ; <label>:9 ; preds = %1 %10 = icmp eq i32 %tauxtrouve.0, %tauxprecis %11 = zext i1 %10 to i32 ret i32 %11 }


Michael, no sé en qué versión de Visual Studio 2013 probaste esto, pero ciertamente está desactualizado. Visual Studio 2013 Update 4 produce correctamente el siguiente mensaje de error en el primer uso de totaldiff :

error C4700: uninitialized local variable ''totaldiff'' used

Debería considerar actualizar su entorno de trabajo.

Por cierto, esto es lo que veo directamente en el editor:


Sí, debería generar una advertencia sobre esa variable no inicializada, pero es un error de GCC . El ejemplo dado allí es:

unsigned bmp_iter_set (); int something (void); void bitmap_print_value_set (void) { unsigned first; for (; bmp_iter_set (); ) { if (!first) something (); first = 0; } }

Y diagnosticado con -O2 -W -Wall .

¡Lamentablemente, este año es el décimo aniversario de este error!