Estaba leyendo C Standard el otro día, y noté que a diferencia del desbordamiento de entero con signo (que no está definido), el desbordamiento de entero sin signo está bien definido. Lo he visto usado en una gran cantidad de códigos para máximos, etc. pero dado el vudú sobre el desbordamiento, ¿se considera esto una buena práctica de programación? ¿Está de alguna manera inseguro? Sé que muchos lenguajes modernos como Python no lo admiten, sino que continúan ampliando el tamaño de los números grandes.¿Está utilizando una buena práctica de desbordamiento de enteros sin signo?
Respuesta
Desbordamiento de entero sin signo (en forma de envolvente) se aprovecha de forma rutinaria en las funciones de hash, y lo ha sido desde el año dot.
Una forma en la que podría pensar en un desbordamiento de enteros sin signo que causa un problema es cuando se resta de un pequeño valor sin signo, lo que hace que se ajuste a un gran valor positivo.
Consejos prácticos para los desbordamientos de enteros:
http://www.gnu.org/software/hello/manual/autoconf/Integer-Overflow-Basics.html#Integer-Overflow-Basics
Estoy preguntando sobre el desbordamiento sin firmar – user122147
Esto no tiene firma. Usando la aritmética sin signo, reste 200 de 100. Obtendrá un valor grande (qué tan grande depende del rango de valores). La aritmética sin signo en C se define como modular, o se podría decir wrap-around. –
@DavidThornley: Creo que la pregunta era si es una buena práctica confiar en tal comportamiento. Mi opinión personal es que está bien confiar en dicho comportamiento si y solo si uno usa tipos rígidos cada vez que uno desea aplicarlo. Si 'a' y' b' son, p. Ej.'UInt32', la expresión' (UInt32) (a-b) 'dará como resultado el comportamiento de ajuste, y quedará claro que se espera ese comportamiento. Sin embargo, la expectativa de que (a-b) se suponga que produzca un comportamiento de envolvimiento es mucho menos obvia, y en máquinas donde 'int' es mayor que 32 bits, de hecho * no * podría generar tal comportamiento. – supercat
no confiaría en él sólo por razones de legibilidad. Va a depurar su código durante horas antes de averiguar dónde está restableciendo esa variable a 0.
No realmente, siempre y cuando el comportamiento esté documentado y comentado. Además, existen motivos legítimos para el desbordamiento de enteros (no), por ejemplo, en sellos de tiempo de ancho fijo, donde la precisión es más importante y el desbordamiento se puede detectar razonablemente. –
Dado que los números con signo en las CPU se pueden representar de diferentes formas, el 99,999% de todas las CPU actuales usan la anotación de dos en complemento. Dado que esta es la mayoría de las máquinas que existen, es difícil encontrar un comportamiento diferente, aunque el compilador podría comprobarlo (posibilidad de grasa). Sin embargo, las especificaciones C deben representar el 100% de los compiladores, por lo que no han definido su comportamiento.
Por lo que haría las cosas más confusas, que es una buena razón para evitarlo. Sin embargo, si tiene una buena razón (por ejemplo, un aumento de rendimiento del factor de 3 para una parte crítica del código), documente bien y úselo.
Se refiere a enteros con signo. El estándar C documenta el comportamiento de los enteros sin signo, y de eso se trata la pregunta. –
Lo uso todo el tiempo para saber si es el momento de hacer algo.
UInt32 now = GetCurrentTime()
if(now - then > 100)
{
// do something
}
Mientras se comprueba el valor antes de 'ahora' vueltas 'entonces', que están muy bien para todos los valores de 'ahora' y 'después'.
EDIT: Creo que esto es realmente un underflow.
Creo que el código anterior fallará en los sistemas donde 'int 'es de 64 bits, ya que los operandos de la resta se extenderían a enteros de 64 bits firmados, entonces' ahora 'es 0 y' entonces' es 4294967295u (0xFFFFFFFF) el resultado de la resta no sería 1 sino -4294967295. Lanzar el resultado de la resta a un UInt32 antes de la comparación evitaría ese problema ya que (UInt32) (- 4294967295) sería 1. – supercat
Otro lugar donde desbordamiento sin firmar se puede utilizar de manera útil es cuando se tiene que recorrer hacia atrás a partir de un determinado tipo sin signo:
void DownFrom(unsigned n)
{
unsigned m;
for(m = n; m != (unsigned)-1; --m)
{
DoSomething(m);
}
}
Otras alternativas no son tan claras. Intentar hacer m >= 0
no funciona a menos que cambie m a signed, pero entonces podría estar truncando el valor de n - o peor - convirtiéndolo en un número negativo en la inicialización.
De lo contrario, tiene que hacer! = 0 o> 0 y luego hacer manualmente el caso 0 después del ciclo.
para (m = n; m
O "do {DoSomething (n);} while (n--! = 0); ". Con "if (n! = (Unsigned) -1)" al inicio, si es realmente deseable que -1 sea un valor mágico de entrada que significa "no hacer nada", como está en el código anterior. –
@Niki: no funciona, su ciclo no hace nada ya que m
Para decirlo brevemente:
es perfectamente legal/OK/seguro de usar desbordamiento de entero sin signo como mejor le parezca, siempre y cuando se presta atención y se adhieren a la definición (para cualquier propósito - optimización, super algoritmos inteligentes, etc.)
Está bien confiar en el desbordamiento, siempre que sepa CUÁNDO ocurrirá ...
Yo, por ejemplo, tuve problemas con la implementación C de MD5 al migrar a un compilador más reciente ... El código esperaba desbordamiento pero también esperaba 32 bits de entrada.
¡Con 64 bits los resultados fueron incorrectos!
Afortunadamente para eso son las pruebas automatizadas: Detecté el problema temprano pero podría haber sido una verdadera historia de terror si hubiera pasado desapercibido.
Se podría argumentar "pero esto no suele suceder": sí, pero eso es lo que lo hace aún más peligroso! Cuando hay un error, todos sospechan del código escrito en los últimos días. Nadie sospecha que el código "solo funcionó durante años" y, por lo general, nadie sabe cómo funciona ...
Este es un caso en el que una aserción en tiempo de compilación para verificar la suposición de que sizeof (int) == 4 era válido sería una buena idea. Eso habría arrojado un error incluso antes de que se ejecutaran las pruebas. – RBerteig
El hecho de que usted conozca las minucias de la norma no significa que la persona que mantiene su código sí lo hace. Esa persona puede perder el tiempo preocupándose por esto mientras realiza la depuración más tarde, o tiene que buscar el estándar para verificar este comportamiento más adelante.
Claro, esperamos competencia con las características razonables de un lenguaje en un programador en funcionamiento, y diferentes compañías/grupos tienen una expectativa diferente sobre dónde se encuentra esa competencia razonable. Pero para la mayoría de los grupos esto parece ser demasiado esperar que la próxima persona lo sepa de la cabeza y no tenga que pensar en ello.
Si esto no fuera suficiente, es más probable encontrarse con errores de compilación cuando se trabaja alrededor de los bordes de la norma. O peor, la persona que transfiere este código a una nueva plataforma puede toparse con ellos.
En resumen, ¡voto no lo hagas!
Si lo usa con prudencia (bien comentado y legible), puede beneficiarse de ella por tener el código más pequeño y más rápido.
siukurnin hace un buen punto, que lo que necesita saber cuándo se producirán desbordamientos. La forma más fácil de evitar el problema de portabilidad que describió es usar los tipos de enteros de ancho fijo de stdint.h. uint32_t es un entero de 32 bits sin signo en todas las plataformas y sistemas operativos, y no se comportará de manera diferente cuando se compila para un sistema diferente.
Yo sugeriría tener siempre una conversión explícita cualquier momento se va a confiar en los números sin signo de envolver. De lo contrario, puede haber sorpresas. Por ejemplo, si "int" es de 64 bits, un código como:
UInt32 foo,bar;
if ((foo-bar) < 100) // Report if foo is between bar and bar+99, inclusive)
... do something
puede fallar, ya que "foo" y "bar" se consiguen promovidos a enteros con signo de 64 bits. Agregar un typecast a UInt32 antes de verificar el resultado contra 100 evitaría problemas en ese caso.
Incidentalmente, creo que la única forma portátil de obtener directamente los 32 bits inferiores del producto de dos UInt32 es lanzar uno de los ints a un UInt64 antes de hacer el multiplicador. De lo contrario, el UInt32 podría convertirse a int64s firmados, con la multiplicación desbordante y produciendo resultados indefinidos.
- 1. MSVC++: Extrañeza con enteros sin signo y desbordamiento
- 2. ¿Está utilizando la buena práctica cfsqltype?
- 3. ¿Puedo evitar un desbordamiento de enteros en C# utilizando un desplazamiento a la derecha sin signo?
- 4. gcc: ¿Está utilizando -Werror y -pedantic como una buena práctica?
- 5. Calcule la diferencia absoluta entre enteros sin signo utilizando SSE
- 6. enteros sin signo en C++ para bucles
- 7. ¿Está utilizando una instanciación de objeto como una buena práctica de valor de una entrada enum?
- 8. Detectar desbordamiento de enteros
- 9. desbordamiento sin signo con operador de módulo en C
- 10. ¿Está utilizando las propiedades get set de C# como una buena práctica?
- 11. Problema de desbordamiento de enteros
- 12. Por qué el resultado de char sin signo << char sin signo no está sin signo char
- 13. de desbordamiento de enteros - ¿Por qué no
- 14. enteros de 32 bits sin signo en Javascript
- 15. Una buena manera de almacenar enteros únicos
- 16. ¿Se está utilizando una mala práctica "básica" aunque podría ser buena para la legibilidad?
- 17. Diseño de herencia utilizando Interface + clase abstracta. ¿Buena práctica?
- 18. ¿Está utilizando "NO EXISTE" una mala práctica de SQL?
- 19. Delphi - FieldByName.AsString - buena práctica
- 20. Pregunta sobre el comportamiento de C para el flujo insuficiente de enteros sin signo
- 21. Cómo evitar el desbordamiento de enteros?
- 22. Función hashing para cuatro enteros sin signo (C++)
- 23. Desbordamiento de enteros y comportamiento indefinido
- 24. ¿Resta sin desbordamiento?
- 25. Inicialización de std :: vector <unsigned int> con una lista de enteros sin signo consecutivos
- 26. ¿Está utilizando miembros de matriz flexibles en C mala práctica?
- 27. ¿Está utilizando Spring AOP para iniciar sesión una buena idea?
- 28. Inicializar matriz de bytes sin signo utilizando número hexadecimal
- 29. Diferencia entre int sin signo y sin signo en C++
- 30. ¿Está utilizando la instancia de Clase como clave de mapa una mejor práctica?
año punto: ¿Es eso 1970? –
Como los orígenes de la informática están en la criptografía, sospecho que es en algún momento en la década de 1940. –
Aunque en realidad no entiendo por qué insisto en * signed * integer overflow. Usar un entero sin signo es igual de bueno y se puede solucionar para detectar desbordamiento. –