c++ - que - ¿Por qué se preferiría uint32_t en lugar de uint_fast32_t?
long long c++ range (11)
¿Por qué muchas personas usan
uint32_t
lugar deuint32_fast_t
?
Nota: El nombre
uint32_fast_t
debe ser
uint_fast32_t
.
uint32_t
tiene una especificación más estricta que
uint_fast32_t
y, por lo tanto, ofrece una funcionalidad más consistente.
uint32_t
pros:
- Varios algoritmos especifican este tipo. OMI: la mejor razón para usar.
- Ancho y rango exactos conocidos.
- Las matrices de este tipo no generan desperdicio.
- la matemática entera sin signo con su desbordamiento es más predecible.
- Coincidencia más estrecha en el rango y las matemáticas de los tipos de 32 bits de otros idiomas.
- Nunca acolchado.
uint32_t
contras:
-
No siempre está disponible (sin embargo, esto es raro en 2018).
Por ejemplo: plataformas que carecen de enteros de 8/16/32 bits (9/18/36 bits, others ).
Por ejemplo: plataformas que usan un complemento que no es 2. viejo 2200
uint_fast32_t
pros:
-
Siempre disponible.
Esto siempre permite que todas las plataformas, nuevas y antiguas, usen tipos rápidos / mínimos. - Tipo "Fastest" que admite un rango de 32 bits.
uint_fast32_t
contras:
- El alcance es mínimamente conocido. Ejemplo, podría ser un tipo de 64 bits.
- Las matrices de este tipo pueden ser un desperdicio de memoria.
-
Todas las respuestas (las mías también al principio), la publicación y los comentarios usaron el nombre incorrecto
uint32_fast_t
. Parece que muchos simplemente no necesitan y usan este tipo. ¡Ni siquiera usamos el nombre correcto! - Relleno posible - (raro).
-
En casos seleccionados, el tipo "más rápido" realmente puede ser otro tipo.
Entonces
uint_fast32_t
es solo una aproximación de primer orden.
Al final, lo mejor depende del objetivo de codificación.
A menos que codifique para una portabilidad muy amplia o alguna función de rendimiento, use
uint32_t
.
Hay otro problema al usar estos tipos que entra en juego: su rango en comparación con
int/unsigned
Presumiblemente,
uint_fastN_t
sería al menos el rango de
unsigned
.
Esto no se especifica, pero es una condición cierta y comprobable.
Por lo tanto,
uintN_t
es más probable que
uint_fastN_t
sea más angosto que el
unsigned
.
Esto significa que el código que usa
uintN_t
matemáticas
uintN_t
está más sujeto a promociones de enteros que
uint_fastN_t
cuando se trata de portabilidad.
Con esta preocupación: ventaja de portabilidad
uint_fastN_t
con operaciones matemáticas seleccionadas.
Nota al
int32_t
sobre
int32_t
lugar de
int_fast32_t
: en máquinas raras,
INT_FAST32_MIN
puede ser -2,147,483,647 y no -2,147,483,648.
El punto más grande:
(u)intN_t
tipos
(u)intN_t
están bien especificados y conducen a un código portátil.
Parece que
uint32_t
es mucho más frecuente que
uint_fast32_t
(me doy cuenta de que esto es evidencia anecdótica).
Sin embargo, eso me parece contrario a la intuición.
Casi siempre que veo que una implementación usa
uint32_t
, todo lo que realmente quiere es un número entero que pueda contener valores de hasta 4,294,967,295 (generalmente un límite mucho más bajo en algún lugar entre 65,535 y 4,294,967,295).
Parece extraño usar
uint32_t
, ya que no se necesita la garantía
''exactamente 32 bits''
, y la garantía
''más rápida disponible> = 32 bits''
de
uint_fast32_t
parece ser exactamente la idea correcta.
Además, aunque generalmente se implementa, no se garantiza que
uint32_t
exista.
¿Por qué, entonces, se
uint32_t
?
¿Es simplemente más conocido o hay ventajas técnicas sobre el otro?
¿Por qué muchas personas usan
uint32_t
lugar deuint32_fast_t
?
Respuesta tonta:
-
No hay un tipo estándar
uint32_fast_t
, la ortografía correcta esuint_fast32_t
.
Respuesta práctica:
-
Muchas personas realmente usan
uint32_t
oint32_t
por su semántica precisa, exactamente 32 bits con aritmética sin signo (uint32_t
) o representación de complemento a 2 (int32_t
). Los tiposxxx_fast32_t
pueden ser más grandes y, por lo tanto, inapropiados para almacenarlos en archivos binarios, usarlos en matrices y estructuras empaquetadas o enviarlos a través de una red. Además, puede que ni siquiera sean más rápidos.
Respuesta pragmática:
-
Muchas personas simplemente no saben (o simplemente no les importa) sobre
uint_fast32_t
, como se demuestra en los comentarios y respuestas, y probablemente suponen queunsigned int
tiene la misma semántica, aunque muchas arquitecturas actuales todavía tienenint
s de 16 bits y algunas Las muestras raras del museo tienen otros tamaños int extraños menores de 32.
Respuesta UX:
-
Aunque posiblemente sea más rápido que
uint32_t
,uint_fast32_t
es más lento de usar: toma más tiempo escribir, especialmente teniendo en cuenta la búsqueda de ortografía y semántica en la documentación de C ;-)
La elegancia importa (obviamente basada en la opinión):
-
uint32_t
ve tan mal que muchos programadores prefieren definir su propio tipou32
ouint32
... Desde esta perspectiva,uint_fast32_t
parece torpe sinuint_fast32_t
reparación. No es de extrañar que seuint_least32_t
en el banco con sus amigosuint_least32_t
y tal.
En muchos casos, cuando un algoritmo funciona en una matriz de datos, la mejor manera de mejorar el rendimiento es minimizar el número de errores de caché. Cuanto más pequeño es cada elemento, más pueden caber en el caché. Esta es la razón por la que todavía se escribe mucho código para usar punteros de 32 bits en máquinas de 64 bits: no necesitan nada cercano a 4 GiB de datos, pero el costo de hacer que todos los punteros y compensaciones necesiten ocho bytes en lugar de cuatro Sería sustancial.
También hay algunos ABI y protocolos especificados para necesitar exactamente 32 bits, por ejemplo, direcciones IPv4. Eso es lo que
uint32_t
realmente significa: usar
exactamente
32 bits, independientemente de si eso es eficiente en la CPU o no. Estos solían declararse como
long
o
unsigned long
, lo que causó muchos problemas durante la transición de 64 bits. Si solo necesita un tipo sin signo que contenga números de al menos 2³²-1, esa ha sido la definición
unsigned long
desde que salió el primer estándar C. Sin embargo, en la práctica, el código antiguo suficiente suponía que a
long
podía contener cualquier puntero o desplazamiento de archivo o marca de tiempo, y el código antiguo suficiente suponía que tenía exactamente 32 bits de ancho, que los compiladores no necesariamente pueden hacer
long
lo mismo
int_fast32_t
sin romper demasiadas cosas.
En teoría, sería más a prueba de futuro para un programa usar
uint_least32_t
, y tal vez incluso cargar
uint_least32_t
elementos en una
uint_fast32_t
variable para realizar cálculos. ¡Una implementación que no tenía ningún
uint32_t
tipo podría incluso declararse en cumplimiento formal del estándar! (Simplemente no sería capaz de compilar muchos programas existentes.) En la práctica, no hay arquitectura ya dónde
int
,
uint32_t
y
uint_least32_t
no son los mismos, y ninguna ventaja,
actualmente
, a la realización de
uint_fast32_t
. Entonces, ¿por qué complicar demasiado las cosas?
Sin embargo, mire la razón por la que todos los
32_t
tipos necesitaban existir cuando ya la teníamos
long
, y verá que esas suposiciones han explotado en nuestras caras antes.
Su código podría terminar ejecutándose algún día en una máquina donde los cálculos de ancho exacto de 32 bits sean más lentos que el tamaño de la palabra nativa, y habría sido mejor usarlo
uint_least32_t
para el almacenamiento y el
uint_fast32_t
cálculo religioso.
O si cruzará ese puente cuando llegue a él y solo quiera algo simple, hay
unsigned long
.
Para dar una respuesta directa: creo que la verdadera razón por la que
uint32_t
se usa
uint_fast32_t
o
uint_least32_t
simplemente es que es más fácil de escribir y, debido a que es más corto, mucho más agradable de leer: si crea estructuras con algunos tipos, y algunos de ellos son
uint_fast32_t
o similar, entonces a menudo es difícil alinearlos bien con
int
u
bool
otros tipos en C, que son bastante cortos (caso en punto:
char
vs.
character
).
Por supuesto, no puedo respaldar esto con datos duros, pero las otras respuestas solo pueden adivinar la razón también.
En cuanto a las razones técnicas para preferir
uint32_t
, no creo que existan, cuando absolutamente
necesita
un int sin signo exacto de 32 bits, entonces este tipo es su
única
opción estandarizada. En casi todos los demás casos, las otras variantes son técnicamente preferibles, específicamente,
uint_fast32_t
si le preocupa la velocidad y
uint_least32_t
si le preocupa el espacio de almacenamiento. El uso
uint32_t
en cualquiera de estos casos corre el riesgo de no poder compilar ya que no es necesario que exista el tipo.
En la práctica, los
uint32_t
tipos relacionados y existen en todas las plataformas actuales, excepto algunos DSP muy raros (hoy en día) o implementaciones de broma, por lo que hay poco riesgo real al usar el tipo exacto.
Del mismo modo, si bien puedes encontrar penalizaciones de velocidad con los tipos de ancho fijo, ya no son (en los cpus modernos) paralizantes.
Por eso, creo, el tipo más corto simplemente gana en la mayoría de los casos, debido a la flojera del programador.
Desde el punto de vista de la corrección y la facilidad de codificación,
uint32_t
tiene muchas ventajas sobre
uint_fast32_t
en particular debido al tamaño definido con mayor precisión y la semántica aritmética, como han señalado muchos usuarios anteriores.
Lo que tal vez se haya perdido es que la
supuesta ventaja
de
uint_fast32_t
que puede ser
más rápido
, pero nunca se materializó de manera significativa.
La mayoría de los procesadores de 64 bits que han dominado la era de 64 bits (x86-64 y Aarch64 en su mayoría) evolucionaron a partir de arquitecturas de 32 bits y tienen operaciones nativas
rápidas de
32 bits incluso en modo de 64 bits.
Entonces
uint_fast32_t
es igual que
uint32_t
en esas plataformas.
Incluso si algunas de las plataformas "también ejecutadas" como POWER, MIPS64, SPARC solo ofrecen operaciones de ALU de 64 bits, la gran mayoría de las operaciones interesantes de 32 bits se pueden realizar perfectamente en registros de 64 bits: la parte inferior de 32 bits funcionará tener los resultados deseados (y todas las plataformas principales al menos le permiten cargar / almacenar 32 bits). El desplazamiento a la izquierda es el principal problema, pero incluso eso puede optimizarse en muchos casos mediante optimizaciones de seguimiento de valor / rango en el compilador.
Dudo que ocasionalmente el desplazamiento a la izquierda un poco más lento o la multiplicación 32x32 -> 64 supere el doble del uso de memoria para dichos valores, en todas las aplicaciones, excepto en las más oscuras.
Finalmente, notaré que si bien la compensación se ha caracterizado en gran medida como "uso de memoria y potencial de vectorización" (a favor de
uint32_t
) versus conteo / velocidad de instrucciones (a favor de
uint_fast32_t
), incluso eso no está claro para mí.
Sí, en algunas plataformas necesitará instrucciones adicionales para
algunas
operaciones de 32 bits, pero también
guardará
algunas instrucciones porque:
-
El uso de un tipo más pequeño a menudo permite que el compilador combine inteligentemente operaciones adyacentes mediante el uso de una operación de 64 bits para lograr dos de 32 bits.
Un ejemplo de este tipo de "vectorización del pobre" no es infrecuente.
Por ejemplo, cree una
struct two32{ uint32_t a, b; }
constantestruct two32{ uint32_t a, b; }
struct two32{ uint32_t a, b; }
enrax
comotwo32{1, 2}
se puede optimizar en un solomov rax, 0x20001
mientras que la versión de 64 bits necesita dos instrucciones. En principio, esto también debería ser posible para operaciones aritméticas adyacentes (misma operación, operando diferente), pero no lo he visto en la práctica. - Un "uso de memoria" más bajo a menudo también lleva a menos instrucciones, incluso si la huella de memoria o caché no es un problema, ya que cualquier estructura de tipo o matrices de este tipo se copian, obtienes el doble de dinero por cada dólar copiado.
-
Los tipos de datos más pequeños a menudo explotan las convenciones de llamadas modernas mejores, como el SysV ABI, que empaqueta datos de estructura de datos de manera eficiente en registros. Por ejemplo, puede devolver hasta una estructura de 16 bytes en los registros
rdx:rax
. Para una función que devuelve la estructura con 4 valores deuint32_t
(inicializados desde una constante), eso se traduce enret_constant32(): movabs rax, 8589934593 movabs rdx, 17179869187 ret
La misma estructura con 4
uint_fast32_t
64 bits necesita un movimiento de registro y cuatro almacenes en la memoria para hacer lo mismo (y la persona que llama probablemente tendrá que leer los valores de la memoria después de la devolución):ret_constant64(): mov rax, rdi mov QWORD PTR [rdi], 1 mov QWORD PTR [rdi+8], 2 mov QWORD PTR [rdi+16], 3 mov QWORD PTR [rdi+24], 4 ret
De manera similar, cuando se pasan argumentos de estructura, los valores de 32 bits se empaquetan aproximadamente el doble en los registros disponibles para los parámetros, por lo que es menos probable que se quede sin argumentos de registro y tenga que pasar a la pila 1 .
-
Incluso si elige usar
uint_fast32_t
para lugares donde "la velocidad importa", a menudo también tendrá lugares donde necesita un tipo de tamaño fijo. Por ejemplo, al pasar valores para salida externa, desde entrada externa, como parte de su ABI, como parte de una estructura que necesita un diseño específico, o porque usa de manera inteligenteuint32_t
para grandes agregaciones de valores para ahorrar en la huella de memoria. En los lugares donde susuint_fast32_t
y `` uint32_t` necesitan interconectarse, puede encontrar (además de la complejidad del desarrollo) extensiones de letrero innecesarias u otro código relacionado con la falta de coincidencia de tamaño. Los compiladores hacen un buen trabajo al optimizar esto en muchos casos, pero aún no es inusual ver esto en una salida optimizada cuando se mezclan tipos de diferentes tamaños.
Puedes jugar con algunos de los ejemplos anteriores y más sobre godbolt .
1
Para ser claros, la convención de empacar estructuras estrechamente en registros no siempre es una clara victoria para valores más pequeños.
Sí significa que los valores más pequeños pueden tener que "extraerse" antes de que puedan usarse.
Por ejemplo, una función simple que devuelve la suma de los dos miembros de la estructura juntos necesita un
mov rax, rdi; shr rax, 32; add edi, eax
mov rax, rdi; shr rax, 32; add edi, eax
mov rax, rdi; shr rax, 32; add edi, eax
mientras que para la versión de 64 bits cada argumento tiene su propio registro y solo necesita un solo
add
o
lea
.
Sin embargo, si acepta que el diseño de "apretar las estructuras al pasar" tiene sentido en general, los valores más pequeños aprovecharán más esta característica.
Muchas rasones.
- Muchas personas no saben que existen los tipos ''rápidos''.
- Es más detallado escribir.
- Es más difícil razonar sobre el comportamiento de sus programas cuando no conoce el tamaño real del tipo.
- El estándar en realidad no es más rápido, ni puede realmente qué tipo es realmente más rápido puede ser muy dependiente del contexto.
- No he visto evidencia de que los desarrolladores de plataformas piensen en el tamaño de estos tipos al definir sus plataformas. Por ejemplo, en Linux x86-64, los tipos "rápidos" son todos de 64 bits, aunque x86-64 tiene soporte de hardware para operaciones rápidas en valores de 32 bits.
En resumen, los tipos "rápidos" son basura inútil. Si realmente necesita averiguar qué tipo es el más rápido para una aplicación determinada, debe comparar su código en su compilador.
No he visto evidencia de que
uint32_t
se use para
su
rango.
En cambio, la mayoría de las veces que
he
visto que se usa
uint32_t
, es para contener exactamente 4 octetos de datos en varios algoritmos, ¡con semántica envolvente y de desplazamiento garantizada!
También hay otras razones para usar
uint32_t
lugar de
uint_fast32_t
: a menudo es que proporcionará ABI estable.
Además, el uso de la memoria se puede conocer con precisión.
Esto compensa en gran medida la ganancia de velocidad de
uint_fast32_t
,
siempre
que ese tipo sea distinto del de
uint32_t
.
Para valores <65536, ya hay un tipo útil, se llama
unsigned int
(
unsigned short
debe tener al menos ese rango, pero
unsigned int
es del tamaño de la palabra nativa) Para valores <4294967296, hay otro llamado
unsigned long
.
Y, por último, la gente no usa
uint_fast32_t
porque es molestamente largo de escribir y fácil de escribir mal: D
Para fines prácticos,
uint_fast32_t
es completamente inútil.
Está definido incorrectamente en la plataforma más extendida (x86_64), y realmente no ofrece ninguna ventaja en ningún lado a menos que tenga un compilador de muy baja calidad.
Conceptualmente, nunca tiene sentido usar los tipos "rápidos" en las estructuras / matrices de datos: cualquier ahorro que obtenga del tipo que es más eficiente para operar se verá reducido por el costo (errores de caché, etc.) de aumentar el tamaño de Su conjunto de datos de trabajo.
Y para variables locales individuales (contadores de bucles, temperaturas, etc.), un compilador que no sea de juguete generalmente puede funcionar con un tipo más grande en el código generado si eso es más eficiente, y solo truncarse al tamaño nominal cuando sea necesario para la corrección (y con tipos con signo, nunca es necesario).
La única variante que es teóricamente útil es
uint_least32_t
, para cuando necesita poder almacenar cualquier valor de 32 bits, pero quiere ser portátil para máquinas que carecen de un tipo de 32 bits de tamaño exacto.
Prácticamente, hablando, sin embargo, eso no es algo de lo que deba preocuparse.
Se garantiza que
uint32_t
tiene casi las mismas propiedades en cualquier plataforma que lo admita.
1
uint_fast32_t
tiene muy pocas garantías sobre cómo se comporta en diferentes sistemas en comparación.
Si cambia a una plataforma donde
uint_fast32_t
tiene un tamaño diferente, todo el código que usa
uint_fast32_t
debe
uint_fast32_t
a
uint_fast32_t
y validarse.
Todos los supuestos de estabilidad van a estar fuera de la ventana.
Todo el sistema funcionará de manera diferente.
Al escribir su código, es posible que ni siquiera tenga
acceso
a un sistema
uint_fast32_t
que no tenga un tamaño de 32 bits.
uint32_t
no funcionará de manera diferente (ver nota al pie).
La corrección es más importante que la velocidad. La corrección prematura es, por lo tanto, un mejor plan que la optimización prematura.
En el caso de que estuviera escribiendo código para sistemas donde
uint_fast32_t
tenía 64 o más bits, podría probar mi código para ambos casos y usarlo.
Salvo la necesidad y la oportunidad, hacerlo es un mal plan.
Finalmente,
uint_fast32_t
cuando lo está almacenando por cualquier período de tiempo o número de instancias puede ser más lento que
uint32
simplemente debido a problemas de tamaño de caché y ancho de banda de memoria.
Las computadoras de hoy en día están mucho más vinculadas a la memoria que a la CPU, y
uint_fast32_t
podría ser más rápido de forma aislada, pero no después de tener en cuenta la sobrecarga de memoria.
1
Como @chux ha señalado en un comentario, si
unsigned
es mayor que
uint32_t
, la aritmética en
uint32_t
pasa por las promociones enteras habituales, y si no, permanece como
uint32_t
.
Esto puede causar errores.
Nada es perfecto
Según tengo entendido, inicialmente se suponía que
int
era un tipo entero "nativo" con garantía adicional de que debería tener al menos 16 bits de tamaño, algo que se consideraba tamaño "razonable" en ese momento.
Cuando las plataformas de 32 bits se hicieron más comunes, podemos decir que el tamaño "razonable" ha cambiado a 32 bits:
-
Windows moderno usa
int
32 bits en todas las plataformas. -
POSIX garantiza que
int
es de al menos 32 bits. -
C #, Java tiene un tipo
int
que garantiza exactamente 32 bits.
Pero cuando la plataforma de 64 bits se convirtió en la norma, nadie expandió
int
para ser un entero de 64 bits debido a:
-
Portabilidad: una gran cantidad de código depende de que
int
tenga un tamaño de 32 bits. -
Consumo de memoria: duplicar el uso de memoria para cada
int
puede no ser razonable en la mayoría de los casos, ya que en la mayoría de los casos los números en uso son mucho más pequeños que 2 mil millones.
Ahora, ¿por qué preferirías
uint32_t
a
uint_fast32_t
?
Por la misma razón, los lenguajes, C # y Java siempre usan enteros de tamaño fijo: el programador no escribe código pensando en posibles tamaños de diferentes tipos, escribe para una plataforma y prueba el código en esa plataforma.
La mayor parte del código depende implícitamente de tamaños específicos de tipos de datos.
Y esta es la razón por la cual
uint32_t
es una mejor opción para la mayoría de los casos: no permite ninguna ambigüedad con respecto a su comportamiento.
Además, ¿es
uint_fast32_t
realmente el tipo más rápido en una plataforma con un tamaño igual o mayor a 32 bits?
Realmente no.
Considere este compilador de código de GCC para x86_64 en Windows:
extern uint64_t get(void);
uint64_t sum(uint64_t value)
{
return value + get();
}
El ensamblaje generado se ve así:
push %rbx
sub $0x20,%rsp
mov %rcx,%rbx
callq d <sum+0xd>
add %rbx,%rax
add $0x20,%rsp
pop %rbx
retq
Ahora, si cambia
get()
el valor de retorno de
uint_fast32_t
(a 4 bytes en Windows x86_64) obtendrá esto:
push %rbx
sub $0x20,%rsp
mov %rcx,%rbx
callq d <sum+0xd>
mov %eax,%eax ; <-- additional instruction
add %rbx,%rax
add $0x20,%rsp
pop %rbx
retq
Observe cómo el código generado es casi el mismo, excepto para la
mov %eax,%eax
instrucción
adicional
después de la llamada a la función, que pretende expandir el valor de 32 bits a un valor de 64 bits.
No existe tal problema si solo usa valores de 32 bits, pero probablemente usará aquellos con
size_t
variables (¿tamaños de matriz probablemente?) Y esos son 64 bits en x86_64.
En Linux
uint_fast32_t
es de 8 bytes, por lo que la situación es diferente.
Muchos programadores usan
int
cuando necesitan devolver un valor pequeño (digamos en el rango [-32,32]).
Esto funcionaría perfectamente si
int
se tratara de plataformas de tamaño entero nativo, pero dado que no está en plataformas de 64 bits, otro tipo que coincida con el tipo nativo de plataforma es una mejor opción (a menos que se use con frecuencia con otros enteros de menor tamaño).
Básicamente, independientemente de lo que diga el estándar, de
uint_fast32_t
todas formas se rompe en algunas implementaciones. Si le interesan las instrucciones adicionales generadas en algunos lugares, debe definir su propio tipo de entero "nativo". O puede usar
size_t
para este propósito, ya que generalmente coincidirá con el
native
tamaño (no incluyo plataformas viejas y oscuras como 8086, solo plataformas que pueden ejecutar Windows, Linux, etc.).
Otra señal que se muestra que
int
se supone que es un tipo entero nativo es "regla de promoción de enteros". La mayoría de las CPU solo pueden realizar operaciones en nativo, por lo que la CPU de 32 bits generalmente solo puede hacer sumas, restas, etc. de 32 bits (las CPU Intel son una excepción aquí). Los tipos enteros de otros tamaños solo son compatibles con las instrucciones de carga y almacenamiento. Por ejemplo, el valor de 8 bits debe cargarse con las instrucciones apropiadas "cargar con 8 bits con signo" o "cargar con 8 bits sin signo" y expandirá el valor a 32 bits después de la carga. Sin la regla de promoción de enteros, los compiladores de C tendrían que agregar un poco más de código para las expresiones que usan tipos más pequeños que los nativos. Desafortunadamente, esto ya no se cumple con las arquitecturas de 64 bits, ya que los compiladores ahora tienen que emitir instrucciones adicionales en algunos casos (como se mostró anteriormente).
Una razón es que
unsigned int
ya es "más rápido" sin la necesidad de ningún tipo de definición especial o la necesidad de incluir algo.
Entonces, si lo necesita rápido, simplemente use el tipo
int
fundamental o
unsigned int
.
Si bien el estándar no garantiza explícitamente que sea el más rápido,
indirectamente lo
hace al afirmar que
"los planos tienen el tamaño natural sugerido por la arquitectura del entorno de ejecución"
en 3.9.1.
En otras palabras,
int
(o su contraparte sin signo) es con lo que el procesador se siente más cómodo.
Ahora, por supuesto, no sabes de qué tamaño podría ser
unsigned int
.
Solo sabe que es
al menos
tan grande como
short
(y parece recordar que
short
debe tener al menos 16 bits, ¡aunque no puedo encontrarlo en el estándar ahora!).
Por lo general, es simplemente simple de 4 bytes, pero en teoría podría ser más grande, o en casos extremos, incluso más pequeño (
aunque personalmente nunca he encontrado una arquitectura donde este fuera el caso, ni siquiera en computadoras de 8 bits en la década de 1980. .. tal vez algunos microcontroladores, quién sabe
resulta que sufro de demencia,
int
era claramente 16 bits en ese entonces).
El estándar C ++ no se molesta en especificar cuáles son los tipos
<cstdint>
o qué garantizan, simplemente menciona "igual que en C".
uint32_t
, según el estándar C,
garantiza
que obtenga exactamente 32 bits.
No hay nada diferente, nada menos y sin partes de relleno.
A veces esto es exactamente lo que necesita y, por lo tanto, es muy valioso.
uint_least32_t
garantiza que cualquiera que sea el tamaño, no puede ser menor que 32 bits (pero podría muy bien ser mayor).
A veces, pero mucho más raramente que un witdh exacto o "no me importa", esto es lo que quieres.
Por último,
uint_fast32_t
es algo superfluo en mi opinión, excepto para fines de documentación de intenciones.
El estándar C establece
"designa un tipo entero que generalmente es el más rápido"
(tenga en cuenta la palabra "generalmente") y menciona explícitamente que no necesita ser más rápido para todos los propósitos.
En otras palabras,
uint_fast32_t
es casi lo mismo que
uint_least32_t
, que
generalmente
también es el más rápido, solo que no se ofrece ninguna garantía (pero no hay garantía de ninguna manera).
Como la mayoría de las veces no le importa el tamaño exacto o desea
exactamente
32 (o 64, a veces 16) bits, y dado que el tipo
unsigned int
"no importa" es más rápido de todos modos, esto explica por qué
uint_fast32_t
No se usa con tanta frecuencia.