Utilizo ints sin firmar para aclarar mi código y su intención. Una cosa que hago para protegerme de las conversiones implícitas inesperadas cuando hago aritmética con tipos con signo y sin signo es usar un short sin signo (generalmente 2 bytes) para mis variables sin signo. Esto es efectivo por un par de razones:
- Cuando hace aritmética con sus variables cortas y literales sin signo (que son de tipo int) o variables de tipo int, esto asegura que la variable sin signo siempre se promocionará a int antes de evaluar la expresión, ya que int siempre tiene un rango más alto que short . Esto evita cualquier comportamiento inesperado al hacer aritmética con tipos con signo y sin signo, suponiendo que el resultado de la expresión se ajuste a un int con signo, por supuesto.
- La mayoría de las veces, las variables sin signo que está usando no excederán el valor máximo de un corto de 2 bytes sin signo (65,535)
El principio general es que el tipo de sus variables sin signo debe tener un rango más bajo que el tipo de las variables con signo para garantizar la promoción al tipo con signo. Entonces no tendrá ningún comportamiento de desbordamiento inesperado. Obviamente, no puede garantizar esto todo el tiempo, pero (la mayoría de las veces) es factible garantizarlo.
Por ejemplo, recientemente tuve un bucle for algo como esto:
const unsigned short cuint = 5;
for(unsigned short i=0; i<10; ++i)
{
if((i-2)%cuint == 0)
{
//Do something
}
}
El literal '2' es de tipo int. Si yo fuera un unsigned int en lugar de un unsigned short, entonces en la sub-expresión (i-2), 2 se promocionaría a unsigned int (dado que unsigned int tiene una prioridad más alta que la firma int). Si i = 0, entonces la sub-expresión es igual a (0u-2u) = algún valor masivo debido al desbordamiento. La misma idea con i = 1. Sin embargo, dado que i es un corto sin signo, se promociona al mismo tipo que el literal '2', que está firmado int, y todo funciona bien.
Para mayor seguridad: en el raro caso en el que la arquitectura que está implementando hace que int sea de 2 bytes, esto podría hacer que ambos operandos en la expresión aritmética se promocionen a unsigned int en el caso en que la variable corta sin signo no encaje en el int de 2 bytes firmado, el último de los cuales tiene un valor máximo de 32,767 <65,535. (Consulte https://stackoverflow.com/questions/17832815/c-implicit-conversion-signed-unsigned para obtener más detalles). Para protegerse de esto, simplemente puede agregar un static_assert a su programa de la siguiente manera:
static_assert(sizeof(int) == 4, "int must be 4 bytes");
y no se compilará en arquitecturas donde int es de 2 bytes.
for(unsigned int n = 10; n >= 0; n --)
(bucles infinitos)