Trampa para los desprevenidos: ¿por qué unsigned int es una bomba de tiempo?
\n\nTodo desarrollador de C++ tarde o temprano se encuentra con una situación en la que los números se comportan como conejos enojados: saltan infinitamente de positivo a cero. Hablamos del desbordamiento de unsigned int. A diferencia de su contraparte con signo (signed int), el desbordamiento de unsigned en C++ está estrictamente definido por el estándar — no es UB (comportamiento indefinido). El número simplemente se envuelve módulo 2N. Pero esto no hace que el error sea menos fatal.
Imagina: calculas el saldo de una cuenta, la cantidad de elementos en un arreglo o el tiempo de funcionamiento de un temporizador. Un desbordamiento accidental y la lógica de la aplicación se derrumba. La pregunta del título "How do I detect unsigned integer overflow?" acumuló miles de visitas en Stack Overflow por una razón. Analicemos tres enfoques para la detección de desbordamiento: desde el ingenuo (pero funcional) hasta el profesional.
\n\n¿Por qué no funcionan las comprobaciones estándar?
\n\nLo primero que se le ocurre a un principiante:
\n\nunsigned int a = UINT_MAX;\nunsigned int b = 1;\nif (a + b < a) // Comprobación ingenua\n std::cout << "¡Desbordamiento!";\n\nEste código funciona, pero solo para la suma. Para la multiplicación, resta o tipos mixtos, la lógica se rompe. Además, el compilador puede considerar esta comprobación como redundante y eliminarla durante la optimización. Especialmente si los tipos tienen signo. No confíes en la magia: escribe explícitamente.
\n\nEnfoque n.º 1: Aritmética antes de la operación (Pre-check)
\n\nLa forma más confiable es verificar la posibilidad de desbordamiento antes de realizar la operación. Para la suma de dos números sin signo, la condición es simple:
\n\nSi a + b puede desbordarse, entonces a > (UINT_MAX - b).
#include <climits>\n#include <iostream>\n\nbool will_add_overflow(unsigned int a, unsigned int b) {\n return a > UINT_MAX - b;\n}\n\nint main() {\n unsigned int x = 4000000000;\n unsigned int y = 500000000;\n \n if (will_add_overflow(x, y)) {\n std::cerr << "Error: ¡desbordamiento en la suma!\\n";\n } else {\n std::cout << "Resultado: " << x + y << "\\n";\n }\n return 0;\n}\n\nMultiplicación: más compleja
\nPara la multiplicación, la comprobación es un poco más ingeniosa: a > UINT_MAX / b (y no olvides la división por cero).
bool will_mult_overflow(unsigned int a, unsigned int b) {\n if (b == 0) return false; // Multiplicar por cero es seguro\n return a > UINT_MAX / b;\n}\n\nVentajas: Multiplataforma, control total, sin dependencias ocultas.
\nDesventajas: Necesitas escribir funciones separadas para cada operación. El código se vuelve voluminoso.
Enfoque n.º 2: Comprobación después de la operación (Post-check)
\n\nEl estándar de C++ garantiza que para tipos unsigned, el resultado de una operación es (a + b) mod 2^N. Por lo tanto, podemos realizar la operación y luego verificar si el valor se "envolvió".
#include <iostream>\n#include <limits>\n\ntemplate<typename T>\nbool add_overflow(T a, T b, T& result) {\n result = a + b;\n return result < a; // Truco clave\n}\n\nint main() {\n unsigned int res;\n if (add_overflow(4000000000u, 500000000u, res)) {\n std::cout << "¡Desbordamiento detectado!\\n";\n } else {\n std::cout << "OK: " << res << "\\n";\n }\n return 0;\n}\n\nImportante: Este truco funciona solo si a y b son del mismo tipo sin signo. Para uint32_t es excelente. Para una mezcla de unsigned int e int es un fracaso.
Trampa para la resta
\nPara la resta, la condición es la inversa: result > a.
template<typename T>\