parametros ejecutar con compilar compilador c++ gcc g++ compiler-warnings undefined-behavior

c++ - ejecutar - gcc wikipedia



¿Por qué se engaña a GCC para que permita un comportamiento indefinido simplemente poniéndolo en un bucle? (2)

Todavía me gustaría entender mejor qué fue lo que salió mal y cómo puede afectar la confiabilidad de diagnósticos similares.

A diferencia de Clang, GCC no tiene lógica para detectar referencias autoinicializadas, por lo que obtener una advertencia aquí se basa en el código para detectar el uso de variables no inicializadas, lo cual es bastante temperamental y poco confiable (consulte la sección de Advertencias de Inicialización mejorada ).

Con un int el compilador puede darse cuenta de que escribes un int inicializar en el flujo, pero con un std::string aparentemente hay demasiadas capas de abstracción entre una expresión de tipo std::string y obteniendo el const char* que contiene , y GCC no detecta el problema.

por ejemplo, GCC da una advertencia para un ejemplo más simple con menos código entre la declaración y el uso de la variable, siempre que habilite alguna optimización:

extern "C" int printf(const char*, ...); struct string { string() : data(99) { } int data; void print() const { printf("%d/n", data); } }; int main() { for (int ii = 0; ii < 1; ++ii) { const string& str = str; // !! str.print(); } } d.cc: In function ‘int main()’: d.cc:6:43: warning: ‘str’ is used uninitialized in this function [-Wuninitialized] void print() const { printf("%d/n", data); } ^ d.cc:13:19: note: ‘str’ was declared here const string& str = str; // !! ^

Sospecho que este tipo de diagnóstico faltante es probable que solo afecte a un puñado de diagnósticos que dependen de la heurística para detectar problemas. Estos serían los que dan una advertencia de la forma " se puede usar sin inicializar" o " puede violar reglas estrictas de aliasing", y probablemente la advertencia "el subíndice de la matriz está por encima de los límites de la matriz". Esas advertencias no son 100% precisas y la lógica "complicada" como los bucles (!) Puede hacer que el compilador deje de intentar analizar el código y no pueda realizar un diagnóstico.

En mi humilde opinión, la solución sería agregar la verificación de referencias autoinicializadas en el punto de inicialización, y no confiar en que la detección no se inicializará más adelante cuando se use.

Lo siguiente es absurdo, pero se compila limpiamente con g++ -Wall -Wextra -Werror -Winit-self (probé GCC 4.7.2 y 4.9.0):

#include <iostream> #include <string> int main() { for (int ii = 0; ii < 1; ++ii) { const std::string& str = str; // !! std::cout << str << std::endl; } }

La línea marcada !! resulta en un comportamiento indefinido, sin embargo, no es diagnosticado por GCC. Sin embargo, comentar la línea for hace que GCC se queje:

error: ‘str’ is used uninitialized in this function [-Werror=uninitialized]

Me gustaría saber: ¿por qué es tan fácil engañar a GCC aquí? Cuando el código no está en un bucle, GCC sabe que está equivocado. Pero ponga el mismo código en un bucle simple y GCC ya no entiende. Esto me molesta porque confiamos mucho en el compilador para notificarnos cuando cometemos errores tontos en C ++, pero falla en un caso aparentemente trivial.

Trivia extra:

  • Si cambia std::string a int y int optimización, GCC diagnosticará el error incluso con el bucle.
  • Si compila el código roto con -O3 , GCC llama literalmente a la función de inserción ostream con un puntero nulo para el argumento de cadena. Si pensaste que estabas a salvo de las referencias nulas si no hiciste ningún casting inseguro, piénsalo de nuevo.

He presentado un error de GCC para esto: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63203 : todavía me gustaría tener una mejor comprensión aquí de lo que salió mal y cómo puede afectar al Fiabilidad de diagnósticos similares.


Usted afirma que es un comportamiento indefinido, pero cuando compilo los dos casos para ensamblar, definitivamente veo que la variable de ámbito de función no se inicializa en la pila, y la variable de ámbito de bloque se establece en NULL.

Esa es una gran respuesta que obtendrás de mí. Descargué la especificación de C ++ para resolver esto definitivamente, pero caí en una fuga tipo Lovecraftiana cuando la observé, para preservar mi frágil cordura ...

Sospecho fuertemente que el caso de ámbito de bloque no está realmente indefinido.