c++ - unam - ¿Deben usarse ints sin firmar si no son necesarios?
todo sobre la redaccion (7)
¿Se debe declarar una variable como int sin firmar si no requieren el rango extra de valores? Por ejemplo, al declarar la variable en un bucle for, si sabe que no va a ser negativa, ¿importa? ¿Es uno más rápido que el otro? ¿Es malo declarar un int sin firmar como sin firmar en C ++?
Para reaparecer, ¿debería hacerse incluso si no se requiere el rango adicional ? Escuché que deberían evitarse porque causan confusión (IIRC es por eso que Java no los tiene).
Debe usar enteros sin signo cuando no tiene sentido que tengan valores negativos. Esto es completamente independiente de la cuestión de rango. Entonces, sí, debe usar tipos de enteros sin signo incluso si no se requiere el rango adicional , y no, no debe usar unsigned int
s unsigned int
(o cualquier otra cosa) si no es necesario, pero necesita revisar su definición de lo que es necesario.
El problema con el uso sistemático de unsigned
cuando los valores no pueden ser negativos no es que Java no tenga unsigned
, es que las expresiones con valores unsigned, especialmente cuando se mezclan con uno firmado, dan resultados a veces confusos si piensa en unsigned como un tipo entero con un rango desplazado. Unsigned es un tipo modular, no una restricción de enteros a positivo o cero.
Por lo tanto, la opinión tradicional es que unsigned
debe utilizarse cuando necesite un tipo modular o para la manipulación a nivel de bits. Esa vista está implícita en K&R; mire cómo se utilizan int y unsigned, y más explícita en TC ++ PL (2ª edición, p. 50):
Los tipos de enteros
unsigned
signo son ideales para usos que tratan el almacenamiento como una matriz de bits. El uso de ununsigned
lugar de unint
para obtener un bit más para representar enteros positivos es casi nunca una buena idea. Los intentos de garantizar que algunos valores sean positivos declarando que las variablesunsigned
normalmente serán vencidos por las reglas de conversión implícitas.
En casi todas las arquitecturas, el costo de la operación firmada y la operación no firmada es el mismo. Por lo tanto, en cuanto a la eficiencia, no obtendrá ninguna ventaja por usar unsigned sobre firmado. Pero como señalaste, si usas unsigned tendrás un rango mayor
Incluso si tiene variables que solo deben tomar valores no negativos sin firmar, puede ser un problema. Aquí hay un ejemplo. Supongamos que se le pide a un programador que escriba un código para imprimir todos los pares de números enteros (a, b) con 0 <= a <b <= n donde n es una entrada determinada. Un código incorrecto es
for (unsigned b = 0; b <= n; b++)
for (unsigned a=0; a <=b-1; b++)
cout << a << '','' << b << n ;
Esto es fácil de corregir, pero pensar con unsigned es un poco menos natural que pensar con int.
La mayoría de las veces, debes usar enteros sin signo.
Son más predecibles en términos de comportamiento indefinido en el desbordamiento y tal.
Este es un gran tema por sí mismo, por lo que no diré mucho más sobre él.
Es una muy buena razón para evitar los enteros con signo a menos que realmente necesite valores con signo.
Además, es más fácil trabajar con ellos cuando se verifica el rango, no es necesario verificar los valores negativos.
Reglas de pulgar típicas:
Si está escribiendo un bucle
for
forward con un índice como la variable de control, casi siempre desea tener enteros sin signo. De hecho, casi siempre quieressize_t
.Si está escribiendo un ciclo inverso
for
con un índice como la variable de control, probablemente debería usar enteros con signo, por razones obvias. Probablementeptrdiff_t
haría.
Lo único con lo que hay que tener cuidado es cuando se establece entre valores firmados y no firmados de diferentes tamaños.
Es probable que desee realizar una doble comprobación (o una triple comprobación) para asegurarse de que el yeso funciona de la manera que espera.
La razón para usar uints es que le da al compilador una variedad más amplia de optimizaciones. Por ejemplo, puede reemplazar una instancia de ''abs (x)'' con ''x'' si sabe que x es positivo. También abre una variedad de "reducciones de fuerza" a nivel de bits que solo funcionan para números positivos. Si siempre multiplica / divide un int por una potencia de dos, entonces el compilador puede reemplazar la operación con un cambio de bit (es decir, x * 8 == x << 3) que tiende a funcionar mucho más rápido. Desafortunadamente, esta relación solo se mantiene si ''x'' es positiva porque los números negativos están codificados de una manera que excluye esto. Con los ints, el compilador puede aplicar este truco si puede probar que el valor es siempre positivo (o puede ser modificado anteriormente en el código para que sea así). En el caso de los uints, este atributo es trivial de probar, lo que aumenta enormemente las probabilidades de que se aplique.
Otro ejemplo podría ser la ecuación y = 16 * x + 12
. Si x puede ser negativo, entonces sería necesario multiplicar y sumar. Sin embargo, si x siempre es positivo, entonces no solo se puede reemplazar el término x * 16 con x << 4, sino que, dado que el término siempre terminaría con cuatro ceros, esto abre el reemplazo del ''+ 12'' con un OR binario (siempre que como el término ''12'' es menor que 16). El resultado sería y = (x<<4) | 12
y = (x<<4) | 12
.
En general, el calificador ''no firmado'' le da al compilador más información sobre la variable, lo que a su vez le permite comprimir más optimizaciones.
int
es el tipo entero de propósito general. Si necesita un número entero, e int
cumple con sus requisitos (rango [-32767,32767]), entonces utilícelo.
Si tiene propósitos más especializados, entonces puede elegir otra cosa. Si necesita un índice en una matriz, use size_t
. Si necesita un índice en un vector, entonces use std::vector<T>::size_type
. Si necesita tamaños específicos, elija algo de <cstdint>
. Si necesita algo más grande que 64 bits, busque una biblioteca como gmp .
No puedo pensar en ninguna buena razón para usar unsigned int
. Al menos, no directamente ( size_t
y algunos de los tipos de tamaño específico de <cstdint>
pueden ser typedefs de unsigned int
).