2012-10-04 39 views
9
#include <stdio.h> 
#include <limits.h> 

void sanity_check(int x) 
{ 
    if (x < 0) 
    { 
     x = -x; 
    } 
    if (x == INT_MIN) 
    { 
     printf("%d == %d\n", x, INT_MIN); 
    } 
    else 
    { 
     printf("%d != %d\n", x, INT_MIN); 
    } 
    if (x < 0) 
    { 
     printf("negative number: %d\n", x); 
    } 
    else 
    { 
     printf("positive number: %d\n", x); 
    } 
} 

int main(void) 
{ 
    sanity_check(42); 
    sanity_check(-97); 
    sanity_check(INT_MIN); 
    return 0; 
} 

Cuando puedo compilar el programa anterior con gcc wtf.c, consigo el resultado esperado:comportamiento extraño número entero con gcc -O2

42 != -2147483648 
positive number: 42 
97 != -2147483648 
positive number: 97 
-2147483648 == -2147483648 
negative number: -2147483648 

Sin embargo, cuando compilo el programa con gcc -O2 wtf.c, me sale una salida diferente :

42 != -2147483648 
positive number: 42 
97 != -2147483648 
positive number: 97 
-2147483648 != -2147483648 
positive number: -2147483648 

Tenga en cuenta las dos últimas líneas. ¿Qué demonios está pasando aquí? ¿Gcc 4.6.3 se está optimizando demasiado?

(también probado esto con g ++ 4.6.3, y pude observar el mismo comportamiento extraño, por lo tanto, la etiqueta de C++.)

+0

No se siente cómodo para dar consejos para desarrolladores posiblemente más experimentados, pero de todos modos podría ser útil para no tener tanta experiencia. Si veo diferencias "extrañas" causadas solo por el nivel de optimización, lo primero que buscaré es UB. – ThomasMore

Respuesta

15

Cuando lo hace - (INT_MIN) está invocando un comportamiento indefinido, ya que ese resultado no cabe en una int.

gcc -O2 nota que x nunca puede ser negativo y se optimiza a partir de entonces. No importa que haya desbordado el valor, ya que no está definido y puede tratarlo como lo desee.

+0

Nada impide que un compilador defina aquello que es UB en el estándar, como la aritmética de complemento de dos sin atrapar. Entonces uno debe preguntarse, ¿cuál es el * beneficio * de hacer la supuesta optimización? Ninguno, por lo que puedo ver. Es la optimización de un idiota, en mi humilde opinión. Entonces, aunque el compilador tiene formalmente sus derechos, es una ** implementación de baja calidad ** al menos en este aspecto. –

+2

Hay un ejemplo en la respuesta de pedr0. Para obtener más información, consulte aquí: http: //blog.llvm.org/2011/05/what-every-c-programmer-should-know.html # signed_overflow –

+0

¡muchas gracias por el enlace! tenga en cuenta que los primeros dos párrafos implican fuertemente falacias lógicas del tipo "X es una forma posible de hacer Y, por lo tanto, X se requiere para Y". Dejé de leer después de eso ... :-) –

12

creo que esto podría ayudar a que, a partir de aquí: here

-fstrict-overflow Permite al compilador asumir reglas de desbordamiento de firmas estrictas, según el idioma que se compile. Para C (y C++) esto significa que el desbordamiento al hacer aritmética con números con signo no está definido, lo que significa que el compilador puede suponer que no sucederá. Esto permite varias optimizaciones. Por ejemplo, el compilador supondrá que una expresión como i + 10> i siempre será verdadera para el signo i. Esta suposición solo es válida si el desbordamiento con signo no está definido, ya que la expresión es falsa si i + 10 se desborda al utilizar la aritmética de complemento a dos. Cuando esta opción está vigente, cualquier intento de determinar si una operación en números firmados se desbordará debe escribirse cuidadosamente para que no implique un desbordamiento. Esta opción también permite que el compilador asuma semántica de puntero estricta: dado un puntero a un objeto, si agregar un desplazamiento a ese puntero no produce un puntero al mismo objeto, la adición no está definida. Esto permite al compilador concluir que p + u> p siempre es verdadero para un puntero p y un entero sin signo u. Esta suposición solo es válida porque el envolvente del puntero no está definido, ya que la expresión es falsa si p + u se desborda utilizando la aritmética de complemento a dos.

Consulte también la opción -fwrapv. Usar -fwrapv significa que el desbordamiento con signo entero está completamente definido: se envuelve. Cuando se usa -fwrapv, no hay diferencia entre -flexible-desbordamiento y -fno-estricto-desbordamiento para enteros. Con -fwrapv se permiten ciertos tipos de desbordamiento. Por ejemplo, si el compilador obtiene un desbordamiento al hacer aritmética en las constantes, el valor desbordado aún se puede usar con -fwrapv, pero no de otra manera.

La opción -fue desbordamiento restringido está habilitada en niveles -O2, -O3, -Os.