varias superponer studio lineas graficos graficas c unsigned signed

studio - superponer graficas en r



¿Cuándo debería usar "int" versus tipos más específicos de signos o tamaños específicos? (5)

Aquí hay algunas cosas que hago. No estoy seguro de que sean para todos, pero funcionan para mí.

  1. Nunca use int o unsigned int directamente. Siempre parece haber un tipo con un nombre más apropiado para el trabajo.
  2. Si una variable debe tener un ancho específico (por ejemplo, para un registro de hardware o para que coincida con un protocolo), use un tipo específico de ancho (por ejemplo, uint32_t ).
  3. Para los iteradores de matriz, donde quiero acceder a los elementos de matriz de 0 a n, esto también debe estar sin signo (no hay razón para acceder a ningún índice menor que 0) y utilizo uno de los tipos rápidos (por ejemplo, uint_fast16_t ), seleccionando el tipo basado en tamaño mínimo requerido para acceder a todos los elementos de la matriz. Por ejemplo, si tengo un ciclo for que iterará a través de un máximo de 24 elementos, uint_fast8_t y dejaré que el compilador (o stdint.h, dependiendo de lo pedante que queramos obtener) decida cuál es el tipo más rápido para esa operación.
  4. Utilice siempre variables sin signo a menos que haya una razón específica para que se firmen.
  5. Si sus variables sin signo y las variables con signo necesitan jugar juntas, use modelos explícitos y tenga en cuenta las consecuencias. (Afortunadamente, esto se minimizará si evita usar variables con signo, excepto donde sea absolutamente necesario).

Si no está de acuerdo con alguno de ellos o tiene alternativas recomendadas, ¡hágamelo saber en los comentarios! Esa es la vida de un desarrollador de software ... seguimos aprendiendo o nos volvemos irrelevantes.

Tengo una pequeña VM para un lenguaje de programación implementado en C. Admite la compilación en arquitecturas de 32 bits y 64 bits, así como en C y C ++.

Estoy tratando de hacer una compilación limpia con tantas advertencias habilitadas como sea posible. Cuando CLANG_WARN_IMPLICIT_SIGN_CONVERSION , recibo una cascada de nuevas advertencias.

Me gustaría tener una buena estrategia para cuándo usar int versus tipos explícitamente sin signo y / o de tamaño explícito. Hasta ahora, tengo problemas para decidir cuál debería ser esa estrategia.

Ciertamente es cierto que mezclarlos, usando principalmente int para cosas como variables y parámetros locales y usando tipos más estrechos para campos en estructuras, causa muchos problemas de conversión implícita.

Me gusta usar tipos de tamaños más específicos para los campos de estructura porque me gusta la idea de controlar explícitamente el uso de memoria para los objetos en el montón. Además, para las tablas hash, confío en el desbordamiento sin signo cuando se realiza el hashing, por lo que es bueno si el tamaño de la tabla hash se almacena como uint32_t .

Pero, si trato de usar tipos más específicos en todas partes , me encuentro en un laberinto de moldes retorcidos por todas partes.

¿Qué hacen otros proyectos de C?


El uso de int todas partes puede parecer tentador, ya que minimiza la necesidad de lanzar, pero hay varias dificultades potenciales que debe tener en cuenta:

  • Un int puede ser más corto de lo que esperas. Aunque, en la mayoría de las plataformas de escritorio, un int es típicamente de 32 bits, el estándar C solo garantiza una longitud mínima de 16 bits . ¿Podría su código necesitar números mayores que 2 16 −1 = 32,767, incluso para valores temporales? Si es así, no use un int . (Es posible que desee utilizar un long ; se garantiza que un long sea ​​de al menos 32 bits).

  • Incluso un long no siempre es lo suficientemente largo. En particular, no hay garantía de que la longitud de una matriz (o de una cadena, que es una matriz de caracteres) se ajuste en un long . Use size_t (o ptrdiff_t , si necesita una diferencia con signo) para esos.

    En particular, un size_t se define para ser lo suficientemente grande como para contener cualquier índice de matriz válido , mientras que un int o incluso un long podría no serlo. Por lo tanto, por ejemplo, al iterar sobre una matriz, su contador de bucle (y sus valores iniciales / finales) generalmente debe ser un size_t , al menos a menos que sepa con certeza que la matriz es lo suficientemente corta como para que funcione un tipo más pequeño. (¡Pero tenga cuidado al iterar hacia atrás: size_t no está firmado, for(size_t i = n-1; i >= 0; i--) lo que for(size_t i = n-1; i >= 0; i--) es un bucle infinito! Usar i != SIZE_MAX o i != (size_t) -1 sin embargo, debería funcionar; o usar un bucle do / while, ¡pero cuidado con el caso n == 0 !)

  • Se firma un int . En particular, esto significa que el desbordamiento int es un comportamiento indefinido. Si alguna vez existe el riesgo de que sus valores puedan desbordarse legítimamente, no use un int ; use un unsigned int (o un unsigned long , o uint NN _t ) en su lugar.

  • A veces, solo necesitas una longitud de bit fija. Si está interactuando con un ABI, o leyendo / escribiendo un formato de archivo, eso requiere enteros de una longitud específica, entonces esa es la longitud que necesita usar. (Por supuesto, en tales situaciones, es posible que también deba preocuparse por cosas como la endianness, por lo que a veces puede tener que recurrir al empaquetado manual de datos byte a byte de todos modos).

Dicho todo esto, también hay razones para evitar el uso de los tipos de longitud fija todo el tiempo: no solo es difícil de escribir int32_t todo el tiempo, sino que obligar al compilador a usar siempre números enteros de 32 bits no siempre es óptimo, especialmente en plataformas donde el tamaño int nativo podría ser, digamos, 64 bits. Podría usar, por ejemplo, C99 int_fast32_t , pero eso es aún más incómodo de escribir.

Por lo tanto, aquí están mis sugerencias personales para la máxima seguridad y portabilidad:

  • Defina sus propios tipos enteros para uso casual en un archivo de encabezado común, algo como esto:

    #include <limits.h> typedef int i16; typedef unsigned int u16; #if UINT_MAX >= 4294967295U typedef int i32; typedef unsigned int u32; #else typedef long i32; typedef unsigned long i32; #endif

    Use estos tipos para cualquier cosa donde el tamaño exacto del tipo no importe, siempre que sean lo suficientemente grandes. Los nombres de tipo que he sugerido son breves y autodocumentados, por lo que deberían ser fáciles de usar en los moldes donde sea necesario y minimizar el riesgo de errores debido al uso de un tipo demasiado estrecho.

    Convenientemente, se garantiza que los tipos u32 y u16 definidos anteriormente son al menos tan anchos como unsigned int y, por lo tanto, se pueden usar de manera segura sin tener que preocuparse de que sean promovidos a int y causen un comportamiento de desbordamiento indefinido.

  • Use size_t para todos los tamaños de matriz e indexación, pero tenga cuidado al convertir entre él y cualquier otro tipo de entero. Opcionalmente, si no le gusta escribir tantos guiones bajos, escriba también un alias más conveniente.

  • Para los cálculos que suponen un desbordamiento en un número específico de bits, use uint NN _t , o simplemente use u16 / u32 como se definió anteriormente y una máscara de bits explícita con & . Si elige usar uint NN _t , asegúrese de protegerse contra una promoción inesperada a int ; Una forma de hacerlo es con una macro como:

    #define u(x) (0U + (x))

    que debería permitirte escribir de forma segura, por ejemplo:

    uint32_t a = foo(), b = bar(); uint32_t c = u(a) * u(b); /* this is always unsigned multiply */

  • Para ABI externos que requieren una longitud entera específica, defina nuevamente un tipo específico, por ejemplo:

    typedef int32_t fooint32; /* foo ABI needs 32-bit ints */

    Una vez más, este nombre de tipo es autodocumentado, tanto con respecto a su tamaño como a su propósito.

    Si la ABI realmente puede requerir, digamos, entradas de 16 o 64 bits, dependiendo de la plataforma y / o las opciones de tiempo de compilación, puede cambiar la definición de tipo para que coincida (y cambiar el nombre del tipo a simple), pero luego Realmente debes tener cuidado cada vez que lanzas algo hacia o desde ese tipo, ya que puede desbordarse inesperadamente.

  • Si su código tiene sus propias estructuras o formatos de archivo que requieren longitudes de bits específicas, considere definir tipos personalizados para ellos también, exactamente como si fuera un ABI externo. O simplemente puede usar uint NN _t en uint NN _t lugar, pero perderá un poco de autodocumentación de esa manera.

  • Para todos estos tipos, no olvides definir también las constantes _MIN y _MAX correspondientes para verificar fácilmente los límites. Esto puede parecer mucho trabajo, pero en realidad son solo un par de líneas en un solo archivo de encabezado.

Finalmente, recuerde tener cuidado con las matemáticas de enteros, especialmente los desbordamientos. Por ejemplo, tenga en cuenta que la diferencia de dos enteros con signo de n bits puede no encajar en un int n de bits. (Encajará en un int sin signo de n bits, si sabe que no es negativo; ¡pero recuerde que necesita convertir las entradas a un tipo sin signo antes de tomar su diferencia para evitar un comportamiento indefinido!) Del mismo modo, para encontrar el promedio de dos enteros (por ejemplo, para una búsqueda binaria), no use avg = (lo + hi) / 2 , sino más bien, avg = lo + (hi + 0U - lo) / 2 ; el primero se romperá si la suma se desborda.


Mantenga grandes números que se utilizan para acceder a los miembros de las matrices, o controle las memorias intermedias como size_t .

Para ver un ejemplo de un proyecto que utiliza size_t , consulte GNU''s dd.c, línea 155 .


Pareces saber lo que estás haciendo, a juzgar por el código fuente vinculado, que eché un vistazo.

Lo dijiste tú mismo: usar tipos "específicos" te hace tener más lanzamientos. Esa no es una ruta óptima para tomar de todos modos. Use int tanto como pueda, para cosas que no requieren un tipo más especializado.

La belleza de int es que se abstrae sobre los tipos de los que hablas. Es óptimo en todos los casos en los que no necesita exponer la construcción a un sistema que no tenga conocimiento de int . Es su propia herramienta para abstraer la plataforma de sus programas. También puede brindarle velocidad, tamaño y ventaja de alineación, dependiendo.

En todos los demás casos, por ejemplo, donde desea permanecer deliberadamente cerca de las especificaciones de la máquina, int puede y, a veces, debe abandonarse. Los casos típicos incluyen protocolos de red en los que los datos van por cable e instalaciones de interoperabilidad: puentes de tipo entre C y otros lenguajes, rutinas de ensamblaje de kernel que acceden a estructuras C. Pero no olvide que a veces desearía usar int incluso en estos casos, ya que sigue el tamaño de palabra "nativo" o preferido de las plataformas, y es posible que desee confiar en esa misma propiedad.

Con tipos de plataforma como uint32_t , un kernel puede querer usarlos (aunque puede que no sea necesario) en sus estructuras de datos si se accede a ellos desde C y ensamblador, ya que este último generalmente no sabe qué se supone que es int .

En resumen, use int tanto como sea posible y recurra a pasar de tipos más abstractos a tipos de "máquina" (bytes / octetos, palabras, etc.) en cualquier situación que lo requiera.

En cuanto a size_t y otros tipos "sugestivos de uso", siempre que la sintaxis siga la semántica inherente al tipo, digamos, usando size_t para valores de tamaño de todo tipo, no contestaría. Pero no lo aplicaría liberalmente a nada solo porque está garantizado que es el tipo más grande (independientemente de si es realmente cierto). Esa es una piedra submarina que no querrás pisar más tarde. El código tiene que explicarse por sí mismo en la medida de lo posible, diría: tener un size_t donde no se espera ninguno naturalmente, levantaría las cejas, por una buena razón. Use size_t para tamaños. Use offset_t para desplazamientos. Use [u]intN_t para octetos, palabras y esas cosas. Y así.

Se trata de aplicar la semántica inherente a un tipo de C en particular, a su código fuente, y sobre las implicaciones en el programa en ejecución.

Además, como otros han ilustrado, no typedef , ya que le da el poder de definir eficientemente sus propios tipos, una instalación de abstracción que personalmente valoro. Un buen código fuente del programa puede que ni siquiera exponga un solo int , sin embargo, confía en int alias detrás de una multitud de tipos definidos con un propósito. No voy a cubrir typedef aquí, espero que las otras respuestas lo hagan.


Siempre.

A menos que tenga razones específicas para usar un tipo más específico, incluso que se encuentre en una plataforma de 16 bits y necesite números enteros mayores que 32767, o que necesite asegurar el orden de bytes y la señalización adecuados para el intercambio de datos a través de una red o en un archivo ( y a menos que tenga recursos limitados, considere transferir datos en "texto plano", es decir, ASCII o UTF8 si lo prefiere).

Mi experiencia ha demostrado que "solo usar ''int''" es una buena máxima para vivir y hace posible que el código funcione, se mantenga fácilmente y se corrija rápidamente cada vez. Pero su situación específica puede ser diferente, así que tome este consejo con un poco de escrutinio bien merecido.