Me encontré con un punto interesante hoy en una revisión en Code Review . @Veedrac recomendó en esta respuesta que los tipos de tamaño variable (por ejemplo, inty long) sean reemplazados por tipos de tamaño fijo como uint64_ty uint32_t. Cita de los comentarios de esa respuesta:
Los tamaños de int y long (y, por lo tanto, los valores que pueden contener) dependen de la plataforma. Por otro lado, int32_t siempre tiene 32 bits de longitud. Usar int solo significa que su código funciona de manera diferente en diferentes plataformas, que generalmente no es lo que desea.
@Supercat explica en parte aquí el razonamiento detrás del estándar que no fija los tipos comunes . C fue escrito para ser portátil en todas las arquitecturas, a diferencia del ensamblaje que generalmente se usaba para la programación de sistemas en ese momento.
Creo que la intención del diseño fue originalmente que cada tipo que no sea int sea la cosa más pequeña que pueda manejar números de varios tamaños, y que int sea el tamaño "de propósito general" más práctico que pueda manejar +/- 32767.
En cuanto a mí, siempre he usado int y no estoy realmente preocupado por las alternativas. Siempre he pensado que es el tipo más con el mejor rendimiento, final de la historia. El único lugar en el que pensé que el ancho fijo sería útil es cuando se codifican datos para almacenamiento o transferencia a través de una red. Raramente he visto tipos de ancho fijo en el código escrito por otros tampoco.
¿Estoy atrapado en los años 70 o hay realmente una razón para usar inten la era de C99 y más allá?
fuente

Respuestas:
Existe un mito común y peligroso en el que los tipos como
uint32_tsalvar a los programadores de tener que preocuparse por el tamaño deint. Si bien sería útil que el Comité de Normas definiera un medio para declarar enteros con una semántica independiente de la máquina, los tipos sin signouint32_ttienen semántica demasiado floja para permitir que el código se escriba de manera limpia y portátil; Además, los tipos con signoint32tienen semántica que, para muchas aplicaciones, se definen de manera innecesariamente estricta y, por lo tanto, excluyen lo que de otro modo serían optimizaciones útiles.Considere, por ejemplo:
En máquinas donde
intno puede contener 4294967295, o puede contener 18446744065119617025, la primera función se definirá para todos los valores denyexponent, y su comportamiento no se verá afectado por el tamaño deint; Además, el estándar no requerirá que produzca un comportamiento diferente en máquinas con cualquier tamaño deintAlgunos valores denyexponent, sin embargo, hará que invoque Comportamiento indefinido en máquinas donde 4294967295 es representable como unint18446744065119617025 no lo es.La segunda función generará Comportamiento indefinido para algunos valores de
nyexponenten máquinas dondeintno se puede mantener 4611686014132420609, pero generará un comportamiento definido para todos los valores denyexponenten todas las máquinas donde pueda (las especificacionesint32_timplican que el comportamiento de envoltura del complemento a dos en máquinas donde es más pequeño queint).Históricamente, a pesar de que el Estándar no dijo nada acerca de lo que los compiladores deberían hacer con el
intdesbordamientoupow, los compiladores habrían producido consistentemente el mismo comportamiento que siinthubiera sido lo suficientemente grande como para no desbordarse. Desafortunadamente, algunos compiladores más nuevos pueden tratar de "optimizar" los programas mediante la eliminación de comportamientos no obligatorios por el Estándar.fuente
pow, ¡recuerde que este código es solo un ejemplo y no tiene en cuentaexponent=0!exponent=1dará como resultado que n se multiplique por sí mismo una vez, ya que el decremento se realiza después de la verificación, si el incremento se realiza antes de la verificación ( es decir, --exponente), no se realizará ninguna multiplicación y se devolverá n.N^(2^exponent), pero los cálculos de la forma aN^(2^exponent)menudo se usan en el cálculo de funciones de exponenciación, y la exponenciación mod-4294967296 es útil para cosas como calcular el hash de la concatenación de dos cadenas cuya los hashes son conocidos.uint32_tpor 31 nunca producirá UB, pero la eficiencia manera de calcular 31 ^ N implica cálculos de 31 ^ (2 ^ N), lo que será.int32_ta veces tener un desbordamiento definido y otras no, que es lo que parece mencionar, parece tener una importancia mínima en relación con el hecho de que, en primer lugar, me permite razonar sobre la prevención del desbordamiento. Y si desea un desbordamiento definido, es probable que desee que el módulo de resultados tenga un valor fijo, por lo que de todos modos está utilizando tipos de ancho fijo.Para valores estrechamente relacionados con los punteros (y, por lo tanto, con la cantidad de memoria direccionable) como los tamaños de búfer, los índices de matriz y Windows '
lParam, tiene sentido tener un tipo entero con un tamaño dependiente de la arquitectura. Por lo tanto, los tipos de tamaño variable siguen siendo útiles. Esto es por eso que tenemos los typedefssize_t,ptrdiff_t,intptr_t, etc. Ellos tienen que ser typedefs porque ninguno de los incorporados en C número entero tipos tiene por qué ser triple de tamaño.Así que la pregunta es si realmente
char,short,int,long, ylong longsiguen siendo útiles.IME, todavía es común que los programas C y C ++ lo usen
intpara la mayoría de las cosas. Y la mayoría de las veces (es decir, cuando sus números están en el rango ± 32 767 y no tiene requisitos de rendimiento rigurosos), esto funciona bien.Pero, ¿qué sucede si necesita trabajar con números en el rango de 17-32 bits (como las poblaciones de las grandes ciudades)? Podría usar
int, pero eso sería codificar una dependencia de la plataforma. Si desea cumplir estrictamente con el estándar, puede usarlolong, que tiene una garantía de al menos 32 bits.El problema es que el estándar C no especifica ningún tamaño máximo para un tipo entero. Hay implementaciones en las que
longes de 64 bits, lo que duplica el uso de su memoria. Y si estoslongson elementos de una matriz con millones de elementos, agotarás la memoria como un loco.Por lo tanto, ni
inttampocolonges un tipo adecuado para usar aquí si desea que su programa sea multiplataforma y eficiente en memoria. Introduzcaint_least32_t.long, evitando los problemas de truncamiento deintint, evitando el desperdicio de memoria de los 64 bitslong.intOTOH, suponga que no necesita grandes cantidades o grandes matrices, pero necesita velocidad. Y
intpuede ser lo suficientemente grande en todas las plataformas, pero no es necesariamente el tipo más rápido: los sistemas de 64 bits generalmente todavía tienen 32 bitsint. Pero se puede usarint_fast16_ty obtener el tipo “más rápido”, ya seaint,longolong long.Por lo tanto, hay casos de uso prácticos para los tipos de
<stdint.h>. Los tipos enteros estándar no significan nada. Especialmentelong, que puede ser de 32 o 64 bits, y puede o no ser lo suficientemente grande como para contener un puntero, dependiendo del capricho de los escritores del compilador.fuente
uint_least32_tes que sus interacciones con otros tipos están aún más débilmente especificadas que las deuint32_t. En mi humilde opinión, el estándar debe definir tipos comouwrap32_tyunum32_t, con la semántica que cualquier compilador que define el tipouwrap32_t, debe promocionar como un tipo sin signo en esencialmente los mismos casos que se promovería siintfueran 32 bits, y cualquier compilador que defina el tipounum32_tdebe asegurarse de que Las promociones aritméticas básicas siempre lo convierten en un tipo con signo capaz de mantener su valor.intN_tyuintN_t, y cuyos comportamientos definidos serían consistentes conintN_tyuintN_t, pero que otorgaría a los compiladores cierta libertad en los valores asignados de código de caso fuera de su rango [permitiendo semánticas similares a las que fueron tal vez destinado parauint_least32_t, pero sin incertidumbres como si agregar auint_least16_ty anint32_tproduciría un resultado firmado o firmado.