2010-10-16 18 views
14

Hay muchas preguntas sobre la detección del desbordamiento de enteros ANTES de la suma/sustracción real debido a posible undefined behavior. Entonces, mi pregunta esDesbordamiento de enteros y comportamiento indefinido

¿Por qué se producirá este undefined behavior en primer lugar?

I puede pensar en 2 causas:

1) Un procesador que genera una excepción en este caso. Claro, se puede apagar, y lo más probable es que un CRT bien escrito lo haga.

2) Un procesador que utiliza otras representaciones binarias de números (complemento 1? Base 10?). En ese caso, el comportamiento indefinido se manifestará como un resultado diferente (¡pero no se bloqueará!). Bueno, podríamos vivir con eso.

Entonces, ¿por qué alguien debería evitar causarlo? ¿Me estoy perdiendo de algo?

+1

Enlace obligatorio: http://blog.regehr.org/archives/213 –

+0

relacionado: http://stackoverflow.com/questions/18195715/why-is-unsigned-integer-overflow-defined-behavior-but- signed-integer-overflow-es –

Respuesta

11

Aunque la mayoría de CPU modernas utilizan complemento a 2, y los resultados de desbordamiento de entero en envolvente módulo predecible, esto es de ninguna manera universal - para mantener el idioma lo suficientemente general que se puede utilizar en una amplia gama de arquitecturas que es mejor especifique que el desbordamiento de enteros es UB.

+1

Cualquier CPU es complemento a dos si el compilador simplemente omite generar los códigos de operación aritméticos firmados inútiles y utiliza los sin firmar tanto para los tipos firmados como para los no firmados. Esto puede incurrir en pérdidas de rendimiento menores al hacer comparaciones (por ejemplo, ahora 'x <-1 'requiere 2 comparaciones a nivel de máquina en lugar de solo 1), pero yo todavía preferiría hacerlo de esa manera. –

+0

El complemento @R: 2 no es el único problema, por ejemplo, algunas CPU (DSP, por ejemplo) tienen aritmética de saturación en lugar de aritmética de módulo. –

+1

La aritmética modular es necesaria para soportar tipos sin firmar de todos modos ... si su máquina no la tiene, supongo que debe implementarla. Un enfoque podría ser usar el bit superior como relleno y ponerlo a cero después de cada operación. –

4

Los bits undefined behavior en la especificación implican cierta optimización del compilador. Por ejemplo:

if (a > 0 && b > 0) { 
    if (a + b <= 0) { 
     // this branch may be optimized out by compiler 
    } else { 
     // this branch will always run 
    } 
} 

Los compiladores de C modernos no son tan simples, hacen muchas conjeturas y optimizaciones.

+0

Buen punto, pero esto solo significa que el compilador de C no lo ayudará a detectarlo. – ruslik

+0

otro punto: mientras el compilador podría detectar esta condición aritmética, algunas operaciones de bit podrían engañarlo para que la condición no se optimice. Además, como lo sugirió nategoose, podríamos declarar la suma como 'volátil'. – ruslik

+3

Este no es un uso válido de la palabra clave 'volátil'. Si funciona, es el resultado de una implementación específica, un comportamiento no especificado. Es decir. aún es un comportamiento indefinido. –

0

Creo que su suposición 1) que esto se puede desactivar para cualquier procesador dado ha sido falsa en al menos una arquitectura histórica importante, el CDC, si mi memoria es correcta.

+0

Este es realmente viejo. Prefiero manejar estos casos con '# ifdef'. – ruslik

+0

@ruslik: La pregunta no es ** cómo ** manejar esto, '# ifdef' es solo una forma de evitar el UB. Pero ya ves, como dices tú mismo, manejarías esto de alguna manera. –

+0

compatibilidad hacia atrás tiene sus límites. En este caso, prefiero escribir código con errores, pero simple. – ruslik

11

Si bien el desbordamiento firmado de razón histórica se especificó como comportamiento indefinido probablemente fueron estas representaciones heredadas falsas (complemento/magnitud de signo) y las interrupciones de desbordamiento, la razón moderna para que siga siendo un comportamiento indefinido es la optimización. Como J-16 SDiZ insinuó, el hecho de que el desbordamiento firmado sea un comportamiento indefinido le permite al compilador optimizar algunos condicionales cuya verdad algebraica (pero no necesariamente la verdad del nivel de representación) ya han sido establecidos por una rama previa. También puede permitir al compilador simplificar algebraicamente algunas expresiones (especialmente aquellas que involucran multiplicación o división) de forma que podrían dar resultados diferentes al orden de evaluación originalmente escrito si una subexpresión contiene un desbordamiento, ya que el compilador puede suponer que se desborda. no sucede con los operandos que le has dado.

El otro gran ejemplo de comportamiento indefinido con el fin de permitir la optimización son las reglas de aliasing.

+3

dern histéricas pasas –

+0

Se podría argumentar que los enteros sin signo deberían comportarse de manera consistente, porque no asumir que a + b> = a y a + b> = b para enteros sin signo en el optimizador (similar a concluir a + b> 0 para un > 0 yb> 0 en el ejemplo de J-16 SDiZ para enteros con signo)? En mi opinión, esto es más una rareza en la especificación C. – nucleon

+1

@nucleon: los tipos sin signo * se especifican como aritmética modular * (donde esas equivalencias algebraicas de desigualdades no se cumplen). Los tipos firmados no son. Si te gusta llamar a esto una rareza en C, que así sea, pero así es y no es algo que puedas cambiar. –

Cuestiones relacionadas