c# - long - uint c++
Enteros vs. sin signo para longitudes/conteos (4)
Para representar una longitud o una variable de conteo, ¿es mejor usar enteros con o sin signo ?
Me parece que C ++ STL tiende a preferir no firmado ( std::size_t
, como en std::vector::size() , en cambio, C # BCL tiende a preferir los enteros con signo (como en ICollection.Count .
Teniendo en cuenta que una longitud o un recuento son enteros no negativos, mi intuición elegiría sin signo ; pero no entiendo por qué los diseñadores .NET eligieron enteros con signo.
¿Cuál es el mejor enfoque? ¿Cuáles son los pros y los contras de cada uno?
C ++ usa valores sin signo porque necesitan el rango completo. En un sistema de 32 bits, el lenguaje debe permitir tener un vector de 4 GB, no solo uno de 2 GB. (Es posible que el sistema operativo no le permita usar los 4 GB, pero el lenguaje en sí no quiere estorbar)
En .NET, los enteros sin signo no son compatibles con CLS. Puede usarlos (en algunos lenguajes .NET), pero limita la portabilidad y la compatibilidad. Así que para la biblioteca de clases base, solo usan enteros con signo.
Sin embargo, estos son ambos casos de borde. Para la mayoría de los propósitos, un int
firmado es lo suficientemente grande . Por lo tanto, mientras ambos ofrezcan el rango que necesita, puede usar ambos.
Una ventaja que a veces tienen los enteros con signo es que facilitan la detección del flujo insuficiente. Supongamos que está calculando un índice de matriz, y debido a una entrada incorrecta, o quizás un error lógico en su programa, termina intentando acceder al índice -1
.
Con un entero con signo, eso es fácil de detectar. Con unsigned, se envolvería y se convertiría en UINT_MAX
. Eso hace que sea mucho más difícil detectar el error, porque esperaba un número positivo y obtuvo un número positivo.
Así que en realidad, depende. C ++ utiliza unsigned porque necesita el rango. .NET utiliza firmado porque necesita trabajar con idiomas que no tienen sin firmar.
En la mayoría de los casos, ambos funcionarán y, a veces, con la firma puede permitir que su código detecte errores de manera más sólida.
Es natural usar tipos sin firma para conteos y tamaños, a menos que estemos en un contexto en el que puedan ser negativos y, a la vez, significativos. Supongo que C ++ sigue la misma lógica de su hermano mayor C, en el que strlen()
devuelve size_t
y malloc()
toma size_t
.
El problema en C ++ (y C) con enteros con y sin signo es que debe saber cómo se convierten entre sí cuando se utiliza una mezcla de los dos tipos. Algunos abogan por el uso de entradas firmadas para todo el entero para evitar este problema de ignorancia e inatención de los programadores. Pero creo que los programadores deben saber cómo usar sus herramientas de intercambio (lenguajes de programación, compiladores, etc.). Tarde o temprano se verán afectados por la conversión, si no en lo que han escrito, y luego en lo que alguien más tiene. Es ineludible
Entonces, conozca sus herramientas, elija lo que tenga sentido en su situación.
Hay algunos aspectos aquí:
1) Valores máximos: por lo general, el valor máximo de un número firmado es la mitad del valor máximo sin signo correspondiente. Por ejemplo, en C, el valor corto máximo firmado es 32767, mientras que el valor corto máximo sin signo es 65535 (porque no se necesita la mitad del rango para los números -ve). Entonces, si espera longitudes o conteos que serán grandes, una representación sin firma tiene más sentido.
2) Seguridad: puede navegar por la red en busca de errores de desbordamiento de enteros, pero imagine un código como:
if (length <= 100)
{
// do something with file
}
... entonces, si ''longitud'' es un valor firmado, corre el riesgo de que ''longitud'' sea un número -ve (aunque intencionalmente malintencionado, algo de conversión, etc.) y el código no realice el esperado. He visto esto en un proyecto anterior donde se incrementó una secuencia para cada transacción, pero cuando el entero con signo que usamos llegó al valor int máximo firmado (2147483647) de repente se convirtió en -ve después del siguiente incremento y nuestro código no pudo manejar eso.
Solo algunas cosas en las que pensar, independientemente de las consideraciones de lenguaje / API subyacentes.
Si no está diseñando una biblioteca reutilizable (en términos de .NET, por ejemplo, un proyecto VB.NET consume su biblioteca de clase C #), entonces elija lo que más le convenga. Por supuesto, si está creando cualquier tipo de DLL, y es factible que su biblioteca se pueda usar en un proyecto con un idioma diferente (nuevamente, VB.NET viene a la mente), entonces debe tener en cuenta los tipos que no cumplen con las normas (sin firma ).