¿Se utiliza demasiado el entero como tipo de datos?

9

¿La mayoría de los desarrolladores de aplicaciones usan enteros con signo en lugares donde realmente quieren usar enteros sin signo? Lo hago todo el tiempo, al igual que mis compañeros de trabajo. No he visto muchas otras bases de código extensas (aparte de Delphi VCL) y los ejemplos en Internet generalmente usan números enteros. Mientras que los desarrolladores de VCL usan sus propios tipos de datos (que sería la forma más fácil de declarar variables).

Algo parece un poco terrible sobre un código como este

TStuffRec = record
   recordID : Integer;
   thingID : Integer;
   otherThingID : Integer;
end;

cuando podría escribirse como

TStuffRec = record
   recordID : Cardinal;
   thingID : Cardinal;
   otherThingID : Cardinal;
end;

Funcionalmente, estos registros casi siempre funcionan igual (y con suerte continuarán funcionando igual incluso en Delphi de 64 bits). Pero números muy grandes tendrán problemas de conversión.

Pero también hay inconvenientes en el uso de entradas sin signo. Principalmente derivado de lo molesto que es mezclar los dos.

La verdadera pregunta es, ¿es esto algo que realmente se piensa o se incluye en las mejores prácticas? ¿Generalmente depende del desarrollador?

Peter Turner
fuente
55
Peter, ¿estás buscando solo respuestas específicas de Delphi?
Adam Lear
3
@Anna Comprender cómo funcionan los tipos de datos Delphi sería una excelente respuesta. Sin embargo, estoy razonablemente seguro de que los programadores de C podrían comprender y responder esta pregunta.
Peter Turner

Respuestas:

9

Una razón por la que no uso tanto los tipos enteros sin signo en Delphi es que pueden crear problemas cuando se mezclan con enteros con signo. Aquí hay uno que me mordió una vez:

for i := 0 to List.Count - 1 do
  //do something here

Había ideclarado como un entero sin signo (después de todo, es un índice en una lista que comienza en 0, nunca tiene que ser negativo, ¿verdad?), Pero cuando List.Countera 0, no cortocircuitaría el ciclo como se esperaba porque 0 - 1evalúa a un número positivo muy alto. ¡Uy!

Entre los posibles problemas de seguridad inherentes a la mezcla de enteros con y sin signo, y los problemas de rango (si va a necesitar números positivos mayores que high(signed whatever), es muy probable que también termine necesitando números positivos más grandes que high(unsigned whatever)eso, así que muévase hasta el siguiente tamaño más grande en lugar de cambiar de firmado a no firmado del mismo tamaño suele ser la acción correcta,) Realmente no he encontrado demasiados usos para enteros sin signo al representar la mayoría de los datos.

Mason Wheeler
fuente
2
Algo relacionado, uno de los principales riesgos de usar un tipo de datos que es potencialmente más pequeño de lo necesario (en lugar de solo sin firmar o firmado) es que si la condición de salida es más grande de lo planeado, en realidad puede terminar con un bucle infinito a medida que el contador se desborda una y otra vez. Suena estúpido en retrospectiva, pero una vez escribí un programa que se suponía que recorría todos los valores posibles de bytes, y tardó unos 15 minutos en convencerme finalmente de que no era posible hacer con un contador de bytes.
Aaronaught
@Aaronaught: No en Delphi. (Al menos no a menos que haga algo estúpido como deshabilitar la comprobación de desbordamiento incorporada). Terminará con una excepción cuando el contador se desborde, en lugar de un bucle infinito. Sigue siendo un error, pero es mucho más fácil de localizar.
Mason Wheeler
Si tú lo dices. Siempre desactivé la comprobación de desbordamiento en Delphi; después de ser bombardeado infinitamente con falsos positivos de cosas como códigos hash y sumas de verificación, me di por vencido con esa "característica" por completo. Pero supongo que tienes razón, habría captado ese error específico.
Aaronaught
@Aaronaught: Bueno, sí, querrás deshabilitarlo para cosas como códigos hash y sumas de verificación que están específicamente diseñados para desbordarse y ajustarse. Pero para los cálculos de uso general que no están diseñados para desbordarse y envolverse, es una característica de seguridad importante y apagarlo es como conducir sin el cinturón de seguridad.
Mason Wheeler
Quizás lo haya olvidado, pero las directivas de comprobación de desbordamiento y compilador eran increíblemente defectuosas en versiones anteriores de Delphi. Puedo recordar vívidamente arrancarme el cabello en múltiples ocasiones después de ver que el depurador se detiene directamente en medio de un bloque {$ O -} / {$ O +} para informar alegremente un desbordamiento. Después de un tiempo no pude soportarlo más y simplemente lo deshabilité globalmente. De nuevo, sí, habría captado este problema, pero todavía no creo que valga la cantidad de falsos positivos. ¡A cada uno lo suyo, por supuesto!
Aaronaught
3

Para ser honesto, tiendo a usar números enteros por costumbre. Me acostumbré al hecho de que ofrecen rangos lo suficientemente grandes para la mayoría de las situaciones y permiten valores negativos (como -1). De hecho, muchas veces usar bytes / palabra / shortint sería más apropiado. Ahora que lo pienso, puedo concentrarme en estos puntos:

  • Perspectiva. El tamaño del mapa de mosaico está limitado a 192x192 mosaicos, por lo que podría usar el byte para abordar mosaicos y bucles. Pero si se aumenta el tamaño del mapa, tendré que pasar por cada uso y reemplazarlo con, por ejemplo, palabra. Cuando necesito permitir objetos fuera del mapa, tendría que volver a cambiar a smallint.

  • Bucles. A menudo escribo un bucle "de i: = 0 a Count-1", lo que sucede si "i" es byte y Count = 0 es que el bucle va de 0 a 255. No es que lo quiera.

  • Uniforme Es más fácil recordar y aplicar "var i: integer"; que detenerse en cada caso y pensar "Hm ... aquí estamos tratando con 0..120 rango ... byte ... no, espera, podríamos necesitar -1 para no inicializado ... abreviatura ... espera ... ¿y si 128 es no es suficiente .. Arrgh! " o "¿Por qué es una moneda pequeña en este lugar, no una moneda corta?"

  • Combinatorio. Cuando necesito combinar dos o más clases juntas, pueden estar usando diferentes tipos de datos para sus propósitos, el uso de tipos más amplios permite omitir conversiones innecesarias.

  • -1. Incluso cuando los valores están en el rango 0..n-1, a menudo necesito establecer el valor "sin valor / desconocido / sin inicializar / vacío", que es una práctica común -1.

El uso de números enteros permite omitir todos estos problemas, olvidarse de la optimización de bajo nivel donde no es necesario, subir de nivel y centrarse en problemas más reales.

PD: ¿Cuándo uso otros tipos?

  • Los contadores, nunca son negativos y son de solo lectura fuera de su clase.
  • Razones de rendimiento / memoria, obligan a utilizar tipos de datos más cortos en ciertos lugares.
Kromster
fuente
1

La mejor práctica es utilizar un tipo de datos que se ajuste a las necesidades de los datos que se utilizan (datos esperados).

Ejemplos de C #: si solo necesitara admitir 0 a 255, usaría un byte.

Si necesitaba soportar 1,000,000 negativos y positivos, entonces int.

Más grande que 4.2 mil millones, luego use un largo.

Al elegir el tipo correcto, el programa usará la cantidad óptima de memoria, así como los diferentes tipos usan diferentes cantidades de memoria.

Aquí hay una referencia de C # int de MSDN.

int 
 -2,147,483,648 to 2,147,483,647
 Signed 32-bit integer

uint 
 0 to 4,294,967,295
 Unsigned 32-bit integer

long 
 -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
 Signed 64-bit integer

ulong 
 0 to 18,446,744,073,709,551,615
 Unsigned 64-bit integer
Jon Raynor
fuente
En C # (o .net en general), ¿se convertirían largos y largos en 128 bits en una máquina de 128 bits? Porque en Delphi, el Integertipo de datos es de 32 bits en una máquina de 32 bits y aparentemente será de 64 bits en una máquina de 64 bits.
Peter Turner
1
@ Peter Turner: No, en C # intes solo una abreviatura de System.Int32, sin importar en qué máquina se ejecute el código.
nikie
@nikie, ¿es como type int System.Int32o algo así? ¿Podría cambiarse tan fácilmente en una versión futura del marco?
Peter Turner
@ Peter Turner / nikie (sizeof (int) .ToString ()); ==> Devuelve 4 (sizeof (Int64) .ToString ()); ==> Devuelve 8 en mi sistema operativo Windows de 64 bits. Como nikie, estadísticas, un int es realmente justo e Int32.
Jon Raynor
1
Una cosa a tener en cuenta es que no todos los tipos cumplen con la Especificación de lenguaje común . uintes uno de esos tipos no conformes, lo que significa que no debe usarse en API expuesta públicamente para evitar romper la capacidad de usar esa API en lenguajes .NET distintos del que está escrita la biblioteca. Esta es también la razón por la cual el marco .NET La API en sí está usando intwhere uintwould would.
Adam Lear
1

Los tipos enteros sin signo solo deben usarse para representar números cardinales en idiomas donde representan números cardinales. Debido a la forma en que funcionaban las computadoras que ejecutaban C, los tipos enteros sin signo se comportaban como miembros de anillos algebraicos mod-2 ^ n (lo que significa que los cálculos que se desbordarían se "envolverían" de manera predecible), y el lenguaje especifica que en muchos casos tales tipos son requiere comportarse como anillos algebraicos abstractos incluso cuando dicho comportamiento sería inconsistente con el comportamiento de números cardinales o enteros matemáticos.

Si una plataforma admite tipos separados para números cardinales y anillos algebraicos, entonces sugeriría que los números cardinales se procesen usando tipos de números cardinales (y cosas que deben ajustarse usando los tipos de anillo). No solo estos tipos podrían almacenar números dos veces más grandes que los tipos con signo, sino que un método que recibe un parámetro de ese tipo no tendría que verificar si era negativo.

Sin embargo, dada la relativa falta de tipos de números cardinales, generalmente es mejor simplemente usar números enteros para representar números enteros matemáticos y números cardinales.

Super gato
fuente