Largo largo en c99
En el estándar C99 lo introdujeron long long
. ¿Cuál es el propósito de esto? En mi (limitada) experiencia de programación en C, solo he visto un int de 4 bytes y uno de 8 bytes de longitud. Por ejemplo, desde Compiler Explorer:
Si long
ya es 8
entonces, ¿por qué es necesario agregar otro long long
tipo? ¿Qué le hace esto al compilador / arquitectura?
Respuestas
Si long ya es 8, ¿por qué es necesario agregar otro tipo long long? ¿Qué le hace esto al compilador / arquitectura?
"Si long ya es 8" no siempre es cierto, ya que existe mucho código que se basa en 32 bits long
y int
32 o 16 bits.
Requerir long
como 64 bits rompería las bases del código. Ésta es una gran preocupación.
Sin embargo, requerir long
permanecer en 32 bits (y no long long
) no daría acceso a números enteros estándar de 64 bits, de ahí que sea una justificación long long
.
Permitir long
como 32 bits o 64 bits (u otros) permite la transición.
Varias funciones pasan / regresan long
como fseek(), ftell()
. Se benefician de long
tener más de 32 bits para admitir archivos grandes.
La práctica recomendada fomenta una más amplia long
: "Los tipos utilizados para size_t
y ptrdiff_t
no deben tener un rango de conversión de números enteros mayor que el de a signed long int
menos que la implementación admita objetos lo suficientemente grandes como para que esto sea necesario". Esto se relaciona con tamaños de memoria que exceden los 32 bits.
Quizás en el futuro una implementación pueda usar int/long/long long/intmax_t
como 32/64/128/256 bits.
IAC, veo que los tipos de ancho fijo intN_t
aumentan en popularidad sobre long
y long long
. Tiendo a usar tipos de ancho fijo o bool
, ( unsigned
) char
, int
/ unsigned
,, size_t
( u
) intmax_t
y dejar signed char
, ( unsigned
) short
, ( unsigned
) long
, ( unsigned
) long long
para casos especiales.
El estándar C solo garantiza que an int
puede tener (en términos generales) 2 bytes, a long
puede tener 4 bytes y a long long
puede tener 8 bytes.
De hecho, MSVC todavía usa 4 bytes long
a pesar de que tiene 4 bytes int
.
El único requisito relevante para int
y long
, entonces y ahora, es que int
debe tener al menos 16 bits y long
debe tener al menos 32 bits. Los sistemas de 16 y 32 bits tienden a tener 32 bits long
, y las máquinas de 64 bits eran mucho menos comunes a fines de la década de 1990. Entonces, antes de C99, los programadores no podían confiar de manera portátil en tener un tipo entero de 64 bits disponible en absoluto. Ese problema se resolvió con la introducción de long long
, que debe tener al menos 64 bits. (Creo que GCC y tal vez otros compiladores ya lo proporcionaron como una extensión).
En estos días, muchos (pero no todos) los sistemas de 64 bits usan 64 bits long
y no se molestan en hacerlos long long
más grandes, por lo que también son de 64 bits y, en cierto sentido, son redundantes. Es de suponer que esos son los sistemas con los que está familiarizado, pero no representan todo lo que existe.
Creo que no se dio cuenta de que está haciendo una gran suposición errónea sobre cómo funcionan los requisitos de ancho de tipo C: ISO C solo establece un rango de valores mínimo como la magnitud más pequeña permitida LONG_MAX
y LONG_MIN
(-2147483647, no 8 porque ISO C permite el complemento de uno y los enteros con signo / magnitud con signo, no solo el complemento de 2). Las implementaciones reales pueden tener tipos más amplios, a menudo para coincidir con un ancho de registro o un tamaño de operando que la máquina de destino puede hacer de manera eficiente.
Se ha escrito mucho sobre esto en Stack Overflow y en otros lugares, que no intentaré repetir aquí. Ver tambiénhttps://en.cppreference.com/w/c/language/arithmetic_types
Eso lo llevó al error de mirar las opciones de ancho de tipo en la ABI de System V x86-64 y asumir que otras implementaciones de C son iguales, creo. x86-64 es un ISA de 64 bits que puede funcionar de manera eficiente con enteros de 64 bits, por lo que 64 bits long
fue una elección bastante sensata.
Ningún ABI sano para una máquina de 32 bits como i386 usaría 64 bits long
porque no es necesario, solo 32 bits. El uso de 64 bits significaría que no podría caber en un solo registro. Compile -m32
o compile para ARM de 32 bits. Godbolt también tiene GCC para AVR y MSP430. En esas máquinas de 8 y 16 bits, GCC elige los anchos más pequeños permitidos por ISO C (2 bytes int
, etc.)
En 1999, x86-64 ni siquiera existía. (Algunas otras ISA de 64 bits lo hicieron, como Alpha). Por lo tanto, mirar una de las 2 ABI principales para que comprenda las opciones de C99 no lo llevará muy lejos.
Por supuesto, C necesita un tipo que esté garantizado para ser de al menos 64 bits, para permitir que las personas escriban programas que realicen cálculos matemáticos enteros de 64 bits de manera eficiente.
Y por cierto, x86-64 puede hacer cosas enteras de 32 bits tan eficientemente como 64 bits, a veces de manera más eficiente. Por lo long
tanto, podría decirse que hacer un tipo de 64 bits no es genial. Algunos códigos se usan long
porque quieren un tipo que debe ser de 32 bits, pero no se beneficia de tenerlo más ancho. Para tal código, 64 bits long
solo desperdicia espacio de memoria caché / ancho de banda de memoria y tamaño de código (prefijos REX). En C99, la opción ideal sería int_least32_t
, pero es molestamente largo de escribir y rara vez se usa.
Pero a long
veces se espera que OTOH sea "el tipo más eficiente (1 registro)", aunque no existe tal garantía y las ABI LLP64 como Windows x64 con 32 bits long
no son así.
Otra lata de gusanos es C99 int_fast32_t
y x86-64 System V, la mala elección de la OMI de System V para convertirlo en un tipo de 64 bits. (Tengo una respuesta a medio escribir para Cpp uint32_fast_t se resuelve en uint64_t pero es más lento para casi todas las operaciones que un uint32_t (x86_64). ¿Por qué se resuelve en uint64_t? Lo que debería terminar ... int_fast32_t
plantea la pregunta de "rápido para qué propósito ", y en muchas implementaciones no es lo que esperarías en muchos casos.
Ver también
- C ++: ¿el tipo de entero más rápido?
- ¿Cómo se deben definir los tipos [u] int_fastN_t para x86_64, con o sin la ABI x32?
- ¿Por qué se preferiría uint32_t en lugar de uint_fast32_t?
- ¿Por qué uint_least16_t es más rápido que uint_fast16_t para la multiplicación en x86_64?
- Optimizaciones del compilador permitidas a través de los tipos de ancho no fijo "int", "mínimo" y "rápido" C / C ++
Hay algunos límites, pero el autor del compilador es libre de elegir las longitudes de los tipos de variables C estándar (char, short, int, long, long long). Naturalmente, char va a ser un byte para esa arquitectura (la mayoría con compiladores de C son de 8 bits). Y, naturalmente, no puede hacer que algo más pequeño sea más grande que algo más grande, long no puede ser más pequeño que un int. Pero ciertamente en 1999 vimos la transición x86 de 16 a 32 bits y, por ejemplo, int cambió de 16 a 32 con varias herramientas, pero permaneció durante mucho tiempo en 32. Más tarde, ocurrió la transición x86 de 32 a 64 bits y, según la herramienta, había tipos disponibles. ayudar.
El problema existía mucho antes de esto y la solución no fue fijar las longitudes de los tipos, están, dentro de las reglas, hasta los autores del compilador en cuanto al tamaño. Pero los autores del compilador deben crear un archivo stdint.h que coincida con la herramienta y el objetivo (stdint.h es específico de una herramienta y un objetivo como mínimo y puede ser una versión de la herramienta y opciones de compilación para esa herramienta, etc.). De modo que, por ejemplo, uint32_t siempre es de 32 bits. Algunos autores convertirán eso en int, otros en long, etc. en su stdint.h. Los tipos de variables del lenguaje C permanecen limitados a char, short, int, etc. según el lenguaje (uint32_t no es un tipo de variable, se convierte a un tipo de variable a través de stdint.h). Esta solución / solución alternativa fue una forma de evitar que todo se volviera loco y mantener vivo el lenguaje.
Los autores suelen elegir, por ejemplo, si los GPR son de 16 bits para que int sea de 16 bits, y si los de 32 bits sean de 32 bits, etc., pero tienen cierta libertad.
Sí, esto significa específicamente que no hay razón para suponer que dos herramientas para un objetivo en particular (la computadora en la que está leyendo esto, por ejemplo) usen las mismas definiciones para int y long en particular, y si desea escribir código para esta plataforma que puede migrar a través de estas herramientas (que admiten esta plataforma) luego use los tipos stdint.h y no int, long, etc ... Lo más seguro es que si está cruzando plataformas, una mcu msp430, una mcu arm, una máquina arm linux , una máquina basada en x86, que los tipos, incluso para la misma "cadena de herramientas" (gnu gcc y binutils, por ejemplo), no tienen las mismas definiciones para int y long, etc. char y short tienden a ser de 8 y 16 bits, int y long tienden a variar más, a veces del mismo tamaño entre sí, a veces diferentes, pero el punto es no asumir.
Es trivial detectar los tamaños, para una versión del compilador / destino / opciones de línea de comando, o simplemente seguir la ruta estándar para minimizar los problemas más adelante.