En multiplataforma C ++ de hoy (o C) mundo en el que tenemos :
Data model | short | int | long | long long | pointers/size_t | Sample operating systems
...
LLP64/IL32P64 16 32 32 64 64 Microsoft Windows (x86-64 and IA-64)
LP64/I32LP64 16 32 64 64 64 Most Unix and Unix-like systems, e.g. Solaris, Linux, BSD, and OS X; z/OS
...
Lo que esto significa hoy, es que para cualquier número entero "común" (con signo), int
será suficiente y posiblemente todavía se pueda usar como el tipo entero predeterminado al escribir código de aplicación C ++. También, para fines prácticos actuales , tendrá un tamaño constante en todas las plataformas.
Si un caso de uso requiere al menos 64 bits, hoy podemos usarlo long long
, aunque posiblemente use uno de los tipos que especifican bitness o el __int64
tipo podría tener más sentido.
Esto queda long
en el medio, y estamos considerando prohibir por completo el uso de long
nuestro código de aplicación .
¿Tendría sentido , o hay un caso para usar long
en código C ++ (o C) moderno que tiene que correr multiplataforma? (la plataforma es de escritorio, dispositivos móviles, pero no cosas como microcontroladores, DSP, etc.)
Posiblemente interesantes enlaces de fondo:
- ¿Qué indica el estándar C ++ del tamaño del tipo int, long?
- ¿Por qué el equipo Win64 eligió el modelo LLP64?
- Modelos de programación de 64 bits: ¿por qué LP64? (algo envejecido)
- ¿Se
long
garantiza que tenga al menos 32 bits? (Esto aborda la discusión de comentarios a continuación. Respuesta ).
fuente
long
es la única forma de garantizar 32 bits.int
puede ser de 16 bits, por lo que para algunas aplicaciones no es suficiente. Sí, aint
veces es de 16 bits en los compiladores modernos. Sí, la gente escribe software en microcontroladores. Yo diría que más personas escriben software que tiene más usuarios en microcontroladores que en el PC con el auge de los dispositivos iPhone y Android no mencionar el aumento de Arduino, etc.int
todavía hay 16 bits. Odio decirlo, pero si vas a escribir sobre el "mundo multiplataforma de hoy", no puedes ignorar todo el subcontinente indio.Respuestas:
La única razón por la que usaría
long
hoy es cuando llamo o implemento una interfaz externa que lo usa.Como dice en su publicación, short e int tienen características razonablemente estables en todas las principales plataformas de escritorio / servidor / móviles hoy en día y no veo ninguna razón para que eso cambie en el futuro previsible. Entonces veo pocas razones para evitarlos en general.
long
Por otro lado es un desastre. En todos los sistemas de 32 bits, sé que tenía las siguientes características.Se escribieron grandes cantidades de código en función de una o más de estas características. Sin embargo, con el cambio a 64 bits no fue posible preservarlos a todos. Las plataformas tipo Unix eligieron LP64 que conservó las características 2 y 3 a costa de la característica 1. Win64 eligió LLP64 que conservó la característica 1 a costa de las características 2 y 3. El resultado es que ya no puede confiar en ninguna de esas características y que la OMI deja pocas razones para usar
long
.Si desea un tipo que tenga exactamente 32 bits de tamaño, debe usarlo
int32_t
.Si desea un tipo que tenga el mismo tamaño que un puntero, debe usar
intptr_t
(o mejoruintptr_t
).Si desea un tipo que sea el elemento más grande en el que se pueda trabajar en un solo registro / instrucción, desafortunadamente no creo que el estándar proporcione uno.
size_t
debería estar en la mayoría de las plataformas comunes pero no en x32 .PD
No me molestaría con los tipos "rápido" o "mínimo". Los tipos "menos importantes" solo importan si le importa la portabilidad para ocultar realmente las arquitecturas donde
CHAR_BIT != 8
. El tamaño de los tipos "rápidos" en la práctica parece ser bastante arbitrario. Linux parece hacerlos al menos del mismo tamaño que el puntero, lo cual es una tontería en plataformas de 64 bits con soporte rápido de 32 bits como x86-64 y arm64. IIRC iOS los hace lo más pequeños posible. No estoy seguro de lo que hacen otros sistemas.PPS
Una razón para usar
unsigned long
(pero no es simplelong
) es porque se garantiza que tiene un comportamiento de módulo. Desafortunadamente, debido a las reglas de promoción arruinadas de C, los tipos sin signo más pequeños queint
no tienen un comportamiento de módulo.En todas las plataformas principales hoy en día
uint32_t
es del mismo tamaño o más grande que int y, por lo tanto, tiene un comportamiento de módulo. Sin embargo, ha habido históricamente y, en teoría, podría haber en el futuro plataformas dondeint
sea de 64 bits y, poruint32_t
lo tanto , no tenga un comportamiento de módulo.Personalmente, diría que es mejor entrar en el hábito de forzar el comportamiento del módulo usando "1u *" o "0u +" al comienzo de sus ecuaciones, ya que esto funcionará para cualquier tamaño de tipo sin signo.
fuente
Como mencionas en tu pregunta, el software moderno tiene que ver con la interoperabilidad entre plataformas y sistemas en Internet. Los estándares C y C ++ proporcionan rangos para tamaños de tipo entero, no tamaños específicos (en contraste con lenguajes como Java y C #).
Para asegurarse de que su software compilado en diferentes plataformas funcione con los mismos datos de la misma manera y para garantizar que otro software pueda interactuar con su software usando los mismos tamaños, debe usar enteros de tamaño fijo.
Ingrese
<cstdint>
cuál proporciona exactamente eso y es un encabezado estándar que todas las plataformas de compilación y biblioteca estándar deben proporcionar. Nota: este encabezado solo se requería a partir de C ++ 11, pero muchas implementaciones de bibliotecas más antiguas lo proporcionaron de todos modos.¿Quieres un entero sin signo de 64 bits? Utilizar
uint64_t
. Entero de 32 bits firmado Utilizarint32_t
. Si bien los tipos en el encabezado son opcionales, las plataformas modernas deben admitir todos los tipos definidos en ese encabezado.A veces se necesita un ancho de bit específico, por ejemplo, en una estructura de datos utilizada para comunicarse con otros sistemas. Otras veces no lo es. Para situaciones menos estrictas,
<cstdint>
proporciona tipos que tienen un ancho mínimo.Hay menos variantes:
int_leastXX_t
será un tipo entero de mínimo XX bits. Utilizará el tipo más pequeño que proporciona XX bits, pero se permite que el tipo sea mayor que el número especificado de bits. En la práctica, estos son típicamente los mismos que los tipos descritos anteriormente que dan un número exacto de bits.También hay variantes rápidas :
int_fastXX_t
es de al menos XX bits, pero debe usar un tipo que funcione rápidamente en una plataforma en particular. La definición de "rápido" en este contexto no está especificada. Sin embargo, en la práctica, esto generalmente significa que un tipo más pequeño que el tamaño de registro de una CPU puede alias a un tipo del tamaño de registro de la CPU. Por ejemplo, el encabezado de Visual C ++ 2015 especifica queint_fast16_t
es un número entero de 32 bits porque la aritmética de 32 bits es en general más rápida en x86 que la aritmética de 16 bits.Todo esto es importante porque debe poder usar tipos que puedan contener los resultados de los cálculos que realiza su programa independientemente de la plataforma. Si un programa produce resultados correctos en una plataforma pero resultados incorrectos en otra debido a diferencias en el desbordamiento de enteros, eso es malo. Al usar los tipos enteros estándar, usted garantiza que los resultados en diferentes plataformas serán los mismos con respecto al tamaño de los enteros utilizados (por supuesto, podría haber otras diferencias entre las plataformas además del ancho del entero).
Entonces sí,
long
debería ser prohibido del código C ++ moderno. Por lo que debeint
,short
ylong long
.fuente
std
espacio de nombres cuando#include
d en una unidad de compilación C ++, pero la documentación que vinculé no lo menciona y a Visual Studio parece no importarle cómo accedo a ellos.int
puede ser ... ¿excesiva? (Lo consideraría si el código necesita ser extremadamente portátil en todas las plataformas oscuras (y no tan oscuras). Prohibirlo para "código de aplicación" puede no estar muy bien con nuestros desarrolladores.#include <cstdint>
Se requiere @Snowman para poner los tiposstd::
y (lamentablemente) opcionalmente también se permite ponerlos en el espacio de nombres global.#include <stdint.h>
Es exactamente lo contrario. Lo mismo se aplica a cualquier otro par de encabezados C. Ver: stackoverflow.com/a/13643019/2757035 Desearía que el estándar hubiera requerido que cada uno solo afectara su respectivo espacio de nombres requerido, en lugar de aparentemente ceñirse a las convenciones pobres establecidas por algunas implementaciones, pero bueno, aquí estamos.No, prohibir los tipos enteros integrados sería absurdo. Sin embargo, tampoco deben ser abusados.
Si necesita un número entero que tenga exactamente N bits de ancho, use (o si necesita una versión). Pensar en un entero de 32 bits y como un entero de 64 bits es simplemente incorrecto. Puede ser que sea así en sus plataformas actuales, pero esto se basa en un comportamiento definido por la implementación.
std::intN_t
std::uintN_t
unsigned
int
long long
El uso de tipos enteros de ancho fijo también es útil para interactuar con otras tecnologías. Por ejemplo, si algunas partes de su aplicación están escritas en Java y otras en C ++, es probable que desee hacer coincidir los tipos enteros para obtener resultados consistentes. (Tenga en cuenta que el desbordamiento en Java tiene una semántica bien definida, mientras que el
signed
desbordamiento en C ++ es un comportamiento indefinido, por lo que la coherencia es un objetivo importante). También serán invaluables al intercambiar datos entre diferentes hosts informáticos.Si no necesita exactamente N bits, pero solo un tipo que sea lo suficientemente ancho , considere usar (optimizado para el espacio) u (optimizado para la velocidad). Nuevamente, ambas familias también tienen contrapartes.
std::int_leastN_t
std::int_fastN_t
unsigned
Entonces, ¿cuándo usar los tipos incorporados? Bueno, dado que el estándar no especifica su ancho con precisión, úselos cuando no le interese el ancho de bits real sino otras características.
A
char
es el número entero más pequeño que el hardware puede direccionar. El lenguaje en realidad te obliga a usarlo para aliasing memoria arbitraria. También es el único tipo viable para representar cadenas de caracteres (estrechas).Un
int
será generalmente el tipo más rápido que la máquina puede manejar. Será lo suficientemente ancho como para que pueda cargarse y almacenarse con una sola instrucción (sin tener que enmascarar o cambiar bits) y lo suficientemente estrecho para que pueda operarse con (las) instrucciones de hardware más eficientes. Por lo tanto,int
es una opción perfecta para pasar datos y hacer operaciones aritméticas cuando el desbordamiento no es una preocupación. Por ejemplo, el tipo subyacente predeterminado de enumeraciones esint
. No lo cambie a un entero de 32 bits solo porque pueda. Además, si tiene un valor que solo puede ser –1, 0 y 1, unint
es una opción perfecta, a menos que vaya a almacenar grandes matrices de ellos, en cuyo caso es posible que desee utilizar un tipo de datos más compacto a costa de tener que pagar un precio más alto para acceder a elementos individuales. El almacenamiento en caché más eficiente probablemente dará sus frutos. Muchas funciones del sistema operativo también se definen en términos deint
. Sería una tontería convertir sus argumentos y resultados de un lado a otro. Todo lo que esto podría hacer es introducir errores de desbordamiento.long
generalmente será el tipo más ancho que puede manejarse con instrucciones de una sola máquina. Esto lo hace especialmenteunsigned long
atractivo para tratar datos sin procesar y todo tipo de cosas de manipulación de bits. Por ejemplo, esperaría verunsigned long
en la implementación de un vector de bits. Si el código se escribe con cuidado, no importa qué tan ancho sea realmente el tipo (porque el código se adaptará automáticamente). En plataformas donde la palabra máquina nativa es de 32 bits, la matriz de respaldo del vector de bits es una matriz deunsigned
Los enteros de 32 bits son lo más deseable porque sería una tontería usar un tipo de 64 bits que debe cargarse mediante instrucciones costosas solo para cambiar y enmascarar los bits innecesarios nuevamente de todos modos. Por otro lado, si el tamaño de palabra nativo de la plataforma es de 64 bits, quiero una matriz de ese tipo porque significa que operaciones como "buscar el primer conjunto" pueden ejecutarse hasta el doble de rápido. Por lo tanto, el "problema" dellong
tipo de datos que está describiendo, que su tamaño varía de una plataforma a otra, en realidad es una característica que puede aprovecharse. Solo se convierte en un problema si piensa en los tipos incorporados como tipos de cierto ancho de bits, que simplemente no lo son.char
,int
ylong
son tipos muy útiles como se describe anteriormente.short
ylong long
no son tan útiles porque su semántica es mucho menos clara.fuente
long
entre Windows y Unix. Podría estar malentendido, pero su descripción de la diferencia en el tamaño delong
ser una "característica" en lugar de un "problema" tiene sentido para mí al comparar modelos de datos de 32 y 64 bits, pero no para esta comparación en particular. En el caso particular de esta pregunta, ¿es realmente una característica? ¿O es una característica en otras situaciones (es decir, en general) e inofensiva en este caso?uint32_t
los valores se llevará a cabo como firmado ,int
la aritmética -width en una plataforma en la queint
es más ancha queuint32_t
. (Con ABIs de hoy, esto es muchísimo más probable que sea un problema parauint16_t
.)long
generalmente será el tipo más amplio que puede manejarse con instrucciones de una sola máquina ...", y esto es exactamente incorrecto . Mira el modelo de datos de Windows. En mi humilde opinión, todo el siguiente ejemplo se descompone, porque en x64 Windows largo todavía es de 32 bits.Otra respuesta ya elabora los tipos de cstdint y las variaciones menos conocidas.
Me gustaría agregar a eso:
usar nombres de tipo específicos de dominio
Es decir, no declare que sus parámetros y variables son
uint32_t
(¡ciertamente nolong
!), Sino nombres comochannel_id_type
,room_count_type
etc.sobre bibliotecas
Las bibliotecas de terceros que usan
long
o no pueden ser molestas, especialmente si se usan como referencias o punteros a ellas.Lo mejor es hacer envoltorios.
En general, mi estrategia es crear un conjunto de funciones similares a las del molde que se utilizarán. Se sobrecargan para aceptar solo aquellos tipos que coinciden exactamente con los tipos correspondientes, junto con cualquier variación de puntero, etc. que necesite. Se definen específicamente para el os / compiler / settings. Esto le permite eliminar advertencias y, sin embargo, garantizar que solo se usen las conversiones "correctas".
En particular, con diferentes tipos primitivos que producen 32 bits, su elección de cómo
int32_t
se define podría no coincidir con la llamada a la biblioteca (por ejemplo, int vs long en Windows).La función similar a la conversión documenta el choque, proporciona una verificación en tiempo de compilación del resultado que coincide con el parámetro de la función y elimina cualquier advertencia o error si y solo si el tipo real coincide con el tamaño real involucrado. Es decir, está sobrecargado y definido si paso (en Windows) una
int*
o unalong*
y da un error en tiempo de compilación de lo contrario.Entonces, si la biblioteca se actualiza o alguien cambia lo que
channel_id_type
es, esto continúa siendo verificado.fuente