c embedded

¿Cuándo debería usar abstracción de tipo en sistemas integrados?



embedded (9)

He trabajado en una serie de diferentes sistemas integrados. Todos han usado typedef s (o #defines ) para tipos como UINT32 .

Esta es una buena técnica ya que lleva el tamaño del tipo de letra al programador y te hace más consciente de las posibilidades de desbordamiento, etc.

Pero en algunos sistemas, usted sabe que el compilador y el procesador no cambiarán durante la vida del proyecto.

Entonces, ¿qué debería influir en su decisión de crear y aplicar tipos específicos de proyectos?

EDITAR Creo que logré perder la esencia de mi pregunta, y tal vez sean realmente dos.

Con la programación integrada, puede necesitar tipos de tamaño específico para las interfaces y también para hacer frente a los recursos restringidos, como la memoria RAM. Esto no se puede evitar, pero puede optar por utilizar los tipos básicos del compilador.

Para todo lo demás, los tipos tienen menos importancia.
Debe tener cuidado de no causar un desbordamiento y debe tener cuidado con el registro y el uso de la pila. Lo que puede llevarte a UINT16 , UCHAR . Sin embargo, el uso de tipos como UCHAR puede agregar "pelusa" al compilador. Debido a que los registros suelen ser más grandes, algunos compiladores pueden agregar código para forzar el resultado al tipo.

i++; puede llegar a ser

ADD REG,1 AND REG, 0xFF que es innecesario

Entonces creo que mi pregunta debería haber sido:

Dadas las limitaciones del software integrado, ¿cuál es la mejor política para un proyecto que tendrá mucha gente trabajando en él? No todos tendrán el mismo nivel de experiencia.


Consistencia, conveniencia y legibilidad. "UINT32" es mucho más legible y escribible que "unsigned long long", que es el equivalente para algunos sistemas.

Además, el compilador y el procesador se pueden reparar durante la vida de un proyecto, pero el código de ese proyecto puede encontrar una nueva vida en otro proyecto. En este caso, tener tipos de datos consistentes es muy conveniente.


El estándar C99 tiene varios tipos de enteros de tamaño estándar. Si puede usar un compilador que admita C99 (gcc lo hace), encontrará estos en <stdint.h> y puede usarlos en sus proyectos.

Además, puede ser especialmente importante en los proyectos integrados utilizar tipos como una especie de "red de seguridad" para cosas como conversiones de unidades. Si puede usar C ++, entiendo que hay algunas bibliotecas de "unidades" que le permiten trabajar en unidades físicas definidas por el sistema de tipos C ++ (a través de plantillas) que se compilan como operaciones en los tipos escalares subyacentes. Por ejemplo, estas bibliotecas no te permitirán agregar un distance_t a un mass_t porque las unidades no se alinean; en realidad obtendrá un error de compilación.

Incluso si no puede trabajar en C ++ u otro idioma que le permita escribir el código de esa manera, al menos puede usar el sistema de tipo C para detectar estos errores a simple vista. (Esa fue realmente la intención original de la notación húngara de Simonyi). Solo porque el compilador no te grite por agregar un meter_t a un gram_t no significa que no deberías usar tipos como ese. Las revisiones de código serán mucho más productivas al descubrir los errores de la unidad en ese momento.


Mi opinión es que si depende de un tamaño mínimo / máximo / específico , no solo asuma que (digamos) una unsigned int es de 32 bytes; use uint32_t en su lugar (suponiendo que su compilador admita C99).


Uso la abstracción de tipo muy raramente. Aquí están mis argumentos, ordenados en orden creciente de subjetividad:

  1. Las variables locales son diferentes de los miembros de estructura y las matrices en el sentido de que desea que quepan en un registro. En un destino 32b / 64b, un int16_t local puede hacer que el código sea más lento en comparación con un int local, ya que el compilador tendrá que agregar operaciones a / force / overflow de acuerdo con la semántica de int16_t . Mientras C99 define un intfast_t typedef, AFAIK un plain int encajará también en un registro, y seguro que es un nombre más corto.

  2. Las organizaciones que gustan de estos typedefs casi invariablemente terminan con varios de ellos ( INT32, int32_t, INT32_T , ad infinitum). Las organizaciones que usan tipos incorporados están, por lo tanto, mejor, de alguna manera, teniendo solo un conjunto de nombres. Desearía que la gente usara typedefs de stdint.h o windows.h o cualquier cosa existente; y cuando un objetivo no tiene ese archivo .h, ¿qué tan difícil es agregar uno?

  3. Los archivos de tipo teóricamente pueden ayudar a la portabilidad, pero, por mi parte, nunca obtuve nada de ellos. ¿Hay algún sistema útil que pueda transferir de un objetivo 32b a uno 16b? ¿Hay un sistema 16b que no sea trivial para portar a un objetivo 32b? Además, si la mayoría de los vars son enteros, en realidad ganarás algo de los 32 bits en el nuevo objetivo, pero si son int16_t , no lo harás. Y los lugares que son difíciles de virar tienden a requerir una inspección manual; antes de probar un puerto, no sabes dónde están. Ahora, si alguien piensa que es tan fácil portar cosas si tiene typedefs por todas partes: cuando llegue el momento del puerto, que le sucede a algunos sistemas, escriba un script que convierta todos los nombres en la base de código. Esto debería funcionar de acuerdo con la lógica de "no requiere inspección manual", y pospone el esfuerzo hasta el punto en el que realmente da beneficio.

  4. Ahora bien, si la portabilidad puede ser un beneficio teórico de los typedefs, la legibilidad seguramente se irá por el desagüe. Solo mira stdint.h: {int,uint}{max,fast,least}{8,16,32,64}_t . Muchos tipos Un programa tiene muchas variables; ¿es realmente tan fácil de entender qué necesidad de ser int_fast16_t y cuál necesita ser uint_least32_t ? ¿Cuántas veces estamos convirtiendo en silencio entre ellos, haciéndolos completamente sin sentido? (Me gustan particularmente las conversiones BOOL / Bool / eBool / boolean / bool / int. Todos los programas escritos por una organización ordenada que ordena typedefs están llenos de eso).

  5. Por supuesto, en C ++ podríamos hacer que el sistema de tipo sea más estricto, envolviendo números en instancias de clase de plantilla con operadores sobrecargados y demás. Esto significa que ahora obtendrá mensajes de error del formulario "class Number <int, Least, 32> no tiene operator + overload para el argumento de tipo class Number <unsigned long long, Fast, 64>, los candidatos son ..." I tampoco llames a esto "legibilidad". Sus posibilidades de implementar estas clases de contenedor correctamente son microscópicas, y la mayoría de las veces esperará la compilación de innumerables instancias de plantillas.


Me gusta usar tipos stdint.h para definir las API del sistema específicamente porque dicen explícitamente qué tan grandes son los elementos. Ya en los viejos tiempos de Palm OS, las API del sistema se definían usando un montón de tipos poco sólidos como "Word" y "SWord" que se heredaban de Mac OS muy clásico. Hicieron una limpieza para decir Int16 e hicieron que la API fuera más fácil de entender para los recién llegados, especialmente con los extraños problemas del puntero de 16 bits en ese sistema. Cuando estaban diseñando Palm OS Cobalt, cambiaron esos nombres nuevamente para que coincidieran con los nombres de stdint.h, lo que lo hizo aún más claro y reduciendo la cantidad de typedefs que tenían que administrar.


Creo que los estándares MISRA sugieren (¿requieren?) El uso de typedefs.

Desde una perspectiva personal, el uso de typedefs no deja confusión en cuanto al tamaño (en bits / bytes) de ciertos tipos. He visto a desarrolladores líderes intentar ambas maneras de desarrollar utilizando tipos estándar, por ejemplo, int y utilizando tipos personalizados, por ejemplo, UINT32.

Si el código no es portátil, hay poco beneficio real en el uso de typedefs, sin embargo , si como yo, entonces trabajas en ambos tipos de software (entorno portátil y fijo), entonces puede ser útil mantener un estándar y utilizar los tipos cutomised. Por lo menos, como dices, el programador es muy consciente de la cantidad de memoria que está usando. Otro factor a considerar es qué tan ''seguro'' estás de que el código no se transporta a otro entorno. He visto el código específico del procesador tener que ser traducido ya que un ingeniero de hardware ha tenido que cambiar de repente una placa, esta no es una buena situación, ¡pero debido a los tipos de letra personalizados podría haber sido mucho peor!


Tal vez soy raro, pero uso ub, ui, ul, sb, si y sl para mis tipos enteros. Tal vez la "i" de 16 bits parece un poco anticuada, pero me gusta el aspecto de ui / si mejor que uw / sw.


Si sus sistemas integrados son de alguna manera un sistema de seguridad crítica (o similar), se recomienda encarecidamente (si no es necesario) utilizar typedefs sobre tipos simples.

Como TK. ha dicho antes, MISRA-C tiene una regla (de asesoramiento) para hacerlo:

Regla 6.3 (asesoramiento): los tiposdefs que indican el tamaño y la firma deben utilizarse en lugar de los tipos numéricos básicos.

(de MISRA-C 2004; es la Regla # 13 (adv) de MISRA-C 1998)

Lo mismo también se aplica a C ++ en esta área; p.ej. Estándares de codificación JSF C ++ :

Regla AV 209 Se creará un archivo de UniversalTypes para definir todos los tipos estándar que los desarrolladores puedan usar. Los tipos incluyen: [uint16, int16, uint32_t etc.]


El uso de <stdint.h> hace que tu código sea más portátil para probar unidades en una PC.

Puede morderte bastante cuando tienes pruebas para todo, pero aún se rompe en tu sistema objetivo porque un int repente tiene solo 16 bits de largo.