sirven que programar programacion para lenguaje desde comandos cero aprender c

que - typedef c



¿Qué ocurrió con la sopa typedef histórica para los enteros en los programas de C? (6)

¡Recuerdo ese período y soy culpable de hacer lo mismo!

Un problema era el tamaño de int , podría ser el mismo que el short , el long o el intermedio. Por ejemplo, si estaba trabajando con formatos de archivo binarios, era imperativo que todo se alineara. Byte ordenando cosas complicadas también. Muchos desarrolladores tomaron la ruta perezosa y simplemente fwrite lo que fuera, en lugar de separar los números byte por byte. Cuando las máquinas se actualizaron a longitudes de palabra más largas, todo el infierno se desató. Así que typedef fue un truco fácil de arreglar eso.

Si el rendimiento era un problema, como solía ser en aquel entonces, se garantizaba que int era el tamaño natural más rápido de la máquina, pero si necesitaba 32 bits e int era más corto que eso, corría el riesgo de un vuelco.

En el lenguaje C, sizeof() no debe resolverse en la etapa de preprocesador, lo que complica las cosas porque no se puede hacer #if sizeof(int) == 4 por ejemplo.

Personalmente, algunos de los fundamentos también fueron simplemente el de una mentalidad de ensamblador de lenguaje y no estar dispuestos a abstraer la noción de lo que es short , int y long . En aquel entonces, el ensamblador se usaba en C con bastante frecuencia.

Hoy en día, hay muchos formatos de archivos no binarios, JSON, XML, etc. donde no importa cuál sea la representación binaria. Además, muchas plataformas populares se han basado en un int 32 bits o más, lo que suele ser suficiente para la mayoría de los propósitos, por lo que hay menos problemas con la reinversión.

Esta es una pregunta posiblemente inana cuya respuesta probablemente debería saber.

Hace aproximadamente quince años, una gran cantidad de código C que vería tenía toneladas de #ifdef de enteros en la plataforma específica #ifdef s. Parecía que cada programa o biblioteca que miraba tenía su propia sopa typedef incompatible entre sí. No sabía mucho acerca de la programación en ese momento y me pareció un montón de aros extraños con los que saltar para decirle al compilador qué tipo de entero querías usar.

He reunido una historia en mi mente para explicar de qué se trataban esos typedefs, pero en realidad no sé si es verdad. Básicamente, supongo que cuando C se desarrolló y estandarizó por primera vez, no se dio cuenta de lo importante que era poder plataforma-independientemente obtener un tipo entero de un tamaño determinado, y por lo tanto, todos los tipos de enteros C originales pueden ser de diferentes Tamaños en diferentes plataformas. Así, todos los que intentaban escribir un código C portátil tenían que hacerlo ellos mismos.

¿Es esto correcto? Si es así, ¿cómo se esperaba que los programadores usaran los tipos enteros C? Quiero decir, en un lenguaje de bajo nivel con muchas modificaciones de bits, ¿no es importante poder decir "este es un entero de 32 bits"? ¿Y como el lenguaje se estandarizó en 1989, seguramente se pensó que la gente estaría tratando de escribir código portátil?


C es un producto de principios de la década de 1970, cuando el ecosistema informático era muy diferente. En lugar de millones de computadoras que se comunicaban entre sí a través de una red extendida, teníamos unos cien mil sistemas en todo el mundo, cada uno ejecutando algunas aplicaciones monolíticas, casi sin comunicación entre sistemas. No se puede suponer que dos arquitecturas tengan el mismo tamaño de palabra o los enteros con signo representados de la misma manera. El mercado todavía era lo suficientemente pequeño como para que no hubiera ninguna necesidad percibida de estandarizar, las computadoras no hablaran entre sí (mucho), y nadie pensaba mucho en la portabilidad.

Si es así, ¿cómo se esperaba que los programadores usaran los tipos enteros C?

Si deseaba escribir el código máximo portátil, entonces no asumió nada más allá de lo garantizado por el Estándar. En el caso de int , eso significaba que no asumió que podría representar nada fuera del rango [-32767,32767] , ni que asumió que estaría representado en el complemento de 2, ni que asumió que era una ancho específico (podría ser más ancho que 16 bits, pero aún así solo representa un rango de 16 bits si contiene algún bit de relleno).

Si no le importaba la portabilidad, o estaba haciendo cosas que no eran portables (lo que generalmente es la combinación de bits), entonces utilizó cualquier tipo (s) que cumpliera sus requisitos.

Realicé la mayoría de las aplicaciones de programación de alto nivel, por lo que estaba menos preocupado por la representación que por el alcance. Aun así, ocasionalmente necesitaba sumergirme en representaciones binarias, y siempre me mordía el culo. Recuerdo haber escrito un código a principios de los años 90 que tenía que ejecutarse en MacOS clásico, Windows 3.1 y Solaris. Creé un montón de constantes de enumeración para máscaras de 32 bits, que funcionaron bien en los cuadros de Mac y Unix, pero no compilé en el cuadro de Windows porque en Windows un int solo tenía 16 bits de ancho.


C se diseñó como un lenguaje que podría ser portado a la mayor cantidad posible de máquinas, en lugar de un lenguaje que permitiría que la mayoría de los programas se ejecuten sin modificaciones en una gama de máquinas de este tipo. Para la mayoría de los propósitos prácticos, los tipos de C fueron:

  • Un tipo de 8 bits si hay uno disponible, o el tipo más pequeño es al menos 8 bits.

  • Un tipo de 16 bits, si hay uno disponible, o el tipo más pequeño que tiene al menos 16 bits.

  • Un tipo de 32 bits, si hay uno disponible, o algún otro tipo que tenga al menos 32 bits.

  • Un tipo que será de 32 bits si los sistemas pueden manejar cosas tan eficientemente como los tipos de 16 bits, o 16 bits de lo contrario.

Si el código necesitaba tipos de 8, 16 o 32 bits y es poco probable que se pueda utilizar en máquinas que no los admiten, no hubo ningún problema en particular con dicho código en relación con el char , short y long como 8, 16 y 32 bits, respectivamente. Los únicos sistemas que no asignaron esos nombres a esos tipos serían aquellos que no podrían admitir esos tipos y no serían capaces de manejar el código que los requería. Dichos sistemas se limitarían a escribir código que se haya escrito para que sea compatible con los tipos que utilizan.

Creo que C podría ser mejor visto como una receta para convertir las especificaciones del sistema en dialectos lingüísticos. Un sistema que utiliza memoria de 36 bits no podrá procesar de manera eficiente el mismo dialecto de lenguaje que un sistema que usa memoria basada en octetos, pero un programador que aprende un dialecto podría aprender otro simplemente aprendiendo qué representaciones enteras el último lo utiliza. Es mucho más útil decirle a un programador que necesita escribir código para un sistema de 36 bits, "Esta máquina es como las otras máquinas, excepto que el char es de 9 bits, el short es de 18 bits y el long es de 36 bits", que decir "Debe usar lenguaje ensamblador porque todos los demás idiomas requerirían tipos enteros que este sistema no puede procesar de manera eficiente".


Cuando C comenzó, las computadoras eran menos homogéneas y mucho menos conectadas que en la actualidad. Se consideró más importante para la portabilidad que los tipos int sean los tamaños naturales de la computadora. Pedir un tipo entero de exactamente 32 bits en un sistema de 36 bits probablemente dará como resultado un código ineficiente.

Y luego surgió una red generalizada en la que está trabajando con campos específicos de tamaño en el cable. Ahora la interoperabilidad se ve muy diferente. Y el ''octeto'' se convierte en la cantidad de facto de los tipos de datos.

Ahora necesita una cantidad exacta de múltiplos de 8 bits, así que ahora obtiene typedef sopa y, finalmente, el estándar se pone al día y tenemos nombres estándar para ellos y la sopa no es como se necesita.


El éxito anterior de C se debió a su flexibilidad para adaptarse a casi todas las arquitecturas variantes existentes @John Hascall con:
1) tamaños enteros nativos de 8, 16, 18, 24, 32, 36, etc. bits,
2) modelos enteros con signo de variante: complemento a 2, complemento a 1, entero con signo y
3) varios endian, big, little y others .

A medida que se desarrollaba la codificación, los algoritmos y el intercambio de datos exigían una mayor uniformidad y, por lo tanto, la necesidad de tipos que se reunían 1 y 2 en todas las plataformas. Los codificadores rodaron los suyos como typedef int int32 dentro de un #if ... Las muchas variaciones de eso crearon la sopa según lo señalado por OP.

C99 introdujo (u)int_leastN_t, (u)int_fastN_t, (u)intmax_t para hacer que sean portátiles, aunque sean de los tipos de ancho de bits mínimos portátiles. Estos tipos son necesarios para N = 8,16,32,64.

También se presentan los tipos semi opcionales (ver más abajo **) como (u)intN_t que tiene los atributos adicionales de los que deben ser el complemento de 2 y no el relleno. Estos tipos populares son los que más se desean y se usan para diluir la sopa de enteros .

¿Cómo se esperaba que los programadores usaran los tipos enteros de C?

Al escribir código flexible que no dependía mucho del ancho de bits. Es bastante fácil codificar strtol() utilizando solo LONG_MIN, LONG_MAX sin tener en cuenta la codificación de ancho de bit / endian / entero.

Sin embargo, muchas tareas de codificación obligan a los tipos de ancho precisos y el complemento de 2 para una codificación fácil y de alto rendimiento. En ese caso, es mejor renunciar a la portabilidad a máquinas de 36 bits y de magnitudes de signo de 32 bits y atenerse a enteros de 2 N de ancho (complemento de 2 para signo). Varios algoritmos CRC y criptografía y formatos de archivo vienen a la mente. Por lo tanto, la necesidad de tipos de ancho fijo y una forma específica (C99) de hacerlo.

Hoy en día todavía hay gotchas que todavía deben ser gestionados. Ejemplo: las promociones habituales int/unsigned pierden cierto control, ya que esos tipos pueden ser 16, 32 o 64.

**

Estos tipos son opcionales. Sin embargo, si una implementación proporciona tipos de enteros con anchos de 8, 16, 32 o 64 bits, sin bits de relleno, y (para los tipos con signo) que tienen una representación de complemento a dos, de fi nirá los nombres typedef correspondientes. C11 7.20.1.1 tipos enteros de ancho exacto 3


No todas las máquinas tienen el mismo tamaño de palabra nativa. Si bien puede sentirse tentado a pensar que un tamaño variable más pequeño será más eficiente, simplemente no es así. De hecho, usar una variable que es del mismo tamaño que el tamaño de palabra nativo de la CPU es mucho más rápido para operaciones aritméticas, lógicas y de manipulación de bits.

Pero, ¿cuál es exactamente el "tamaño de la palabra nativa"? Casi siempre, esto significa el tamaño de registro de la CPU, que es el mismo con el que puede trabajar la Unidad de lógica aritmética (ALU).

En los entornos integrados, todavía hay cosas como las CPU de 8 y 16 bits (¿existen todavía controladores PIC de 4 bits?). Todavía hay montañas de procesadores de 32 bits. Así que el concepto de "tamaño de palabra nativo" está vivo y bien para los desarrolladores de C.

Con procesadores de 64 bits, a menudo hay un buen soporte para operandos de 32 bits. En la práctica, el uso de números enteros de 32 bits y valores de punto flotante a menudo puede ser más rápido que el tamaño de palabra completo.

Además, hay diferencias entre la alineación de palabras nativas y el consumo de memoria general al diseñar estructuras en C.

Pero los dos patrones de uso comunes permanecen: el tamaño del código independiente para mejorar la velocidad (int, corto, largo), o el tamaño fijo (int32_t, int16_t, int64_t) para la corrección o interoperabilidad donde sea necesario.