¿Hay alguna razón para usar C en lugar de C ++ para el desarrollo integrado?

81

Pregunta

Tengo dos compiladores en mi hardware C ++ y C89

Estoy pensando en usar C ++ con clases pero sin polimorfismo (para evitar vtables). Las principales razones por las que me gustaría usar C ++ son:

  • Prefiero usar funciones "en línea" en lugar de definiciones de macros.
  • Me gustaría usar espacios de nombres ya que prefijos desordenan el código.
  • Veo que C ++ es un tipo de bit más seguro principalmente debido a las plantillas y la conversión detallada.
  • Realmente me gustan las funciones y los constructores sobrecargados (usados ​​para la conversión automática).

¿Ve alguna razón para seguir con C89 al desarrollar para hardware muy limitado (4kb de RAM)?

Conclusión

Gracias por sus respuestas, ¡fueron de gran ayuda!

Pensé en el tema y me quedaré con C principalmente porque:

  1. Es más fácil predecir el código real en C y esto es realmente importante si solo tiene 4 KB de RAM.
  2. Mi equipo está formado principalmente por desarrolladores de C, por lo que las funciones avanzadas de C ++ no se utilizarán con frecuencia.
  3. Encontré una forma de funciones en línea en mi compilador de C (C89).

Es difícil aceptar una respuesta, ya que proporcionó tantas buenas respuestas. Desafortunadamente, no puedo crear una wiki y aceptarla, así que elegiré una respuesta que me hizo pensar más.

Piotr Czapla
fuente
11
Una cosa: tenga siempre perfectamente claro en qué idioma está escribiendo. No intente escribir un programa en "C / C ++". Escriba en C o escriba en C ++ sabiendo qué características del lenguaje usará y cuáles no.
David Thornley
Definir "desarrollo integrado"
Marco van de Voort
@DavidThornley, es posible que tenga razón para los casos integrados, pero me ha sorprendido gratamente lo bien que se combinan los códigos C y C ++ en los que estoy buscando extender las aplicaciones de código abierto comunes de la industria como Kamailio con STL. Oficialmente estoy fomentando este uso del código STL y C, ya que ofrece una funcionalidad tremenda y facilidad de mantenimiento en el futuro, mientras crea casi cero problemas (la falta de estructuras integradas en C ++ es un crimen horrible contra C ++, y debe corregirse lo antes posible ).
user2548100
Para reflexionar, aquí hay un gran artículo en el que el diseñador y escritor de ZeroMQ analiza por qué se arrepiente de escribir el código base en C ++ en lugar de C. No es lo que esperaba en absoluto, y por razones que no se encuentran en ninguna otra parte de esta página. 250bpm.com/blog:4
user2548100

Respuestas:

48

Dos razones para usar C sobre C ++:

  1. Para muchos procesadores integrados, o no hay un compilador de C ++ o hay que pagar más por él.
  2. Mi experiencia es que una proporción significativa de ingenieros de software embebido tiene poca o ninguna experiencia en C ++, ya sea por (1), o porque tiende a no ser enseñado en títulos de ingeniería electrónica, por lo que sería mejor seguir con lo que saben.

Además, la pregunta original y una serie de comentarios mencionan los 4 Kb de RAM . Para un procesador integrado típico, la cantidad de RAM (en su mayoría) no está relacionada con el tamaño del código, ya que el código se almacena y se ejecuta desde flash.

Ciertamente, la cantidad de espacio de almacenamiento de código es algo a tener en cuenta, pero a medida que aparecen nuevos procesadores con más capacidad en el mercado, es un problema menor de lo que solía ser para todos los proyectos, excepto los más costosos.

Sobre el uso de un subconjunto de C ++ para su uso con sistemas integrados: ahora existe un estándar MISRA C ++ , que puede valer la pena echarle un vistazo.

EDITAR: Consulte también esta pregunta , que llevó a un debate sobre C vs C ++ para sistemas integrados.

Steve Melnikoff
fuente
2
Vea mi respuesta más larga a continuación: C ++ tiende a hacer que poner datos constantes en FLASH sea muy difícil.
jakobengblom2
3
Una razón potencialmente buena para usar C en lugar de C ++ es la ABI estándar de C. Solo para completar.
Chris Lutz
66

Para un objetivo con recursos muy limitados, como 4 KB de RAM, probaría las aguas con algunas muestras antes de realizar un gran esfuerzo que no se puede transferir fácilmente a una implementación de ANSI C pura.

El grupo de trabajo Embedded C ++ propuso un subconjunto estándar del lenguaje y un subconjunto estándar de la biblioteca estándar para acompañarlo. Perdí la pista de ese esfuerzo cuando murió el C User's Journal, desafortunadamente. Parece que hay un artículo en Wikipedia y que el comité todavía existe.

En un entorno integrado, realmente debe tener cuidado con la asignación de memoria. Para hacer cumplir ese cuidado, es posible que deba definir lo global operator new()y sus amigos como algo que ni siquiera se puede vincular para que sepa que no se usa. Por newotro lado, es probable que la ubicación sea ​​su amiga, cuando se usa con prudencia junto con un esquema de asignación estable, seguro para subprocesos y con latencia garantizada.

Las funciones en línea no causarán muchos problemas, a menos que sean lo suficientemente grandes como para haber sido funciones reales en primer lugar. Por supuesto, las macros que reemplazaron tenían el mismo problema.

Las plantillas tampoco pueden causar un problema a menos que su instanciación se vuelva loca. Para cualquier plantilla que use, audite su código generado (el mapa de enlaces puede tener suficientes pistas) para asegurarse de que solo ocurran las instancias que pretendía usar.

Otro problema que puede surgir es la compatibilidad con su depurador. No es inusual que un depurador de hardware que de otro modo se pueda utilizar tenga un soporte muy limitado para la interacción con el código fuente original. Si efectivamente debe depurar en ensamblador, entonces la interesante modificación de nombres de C ++ puede agregar confusión adicional a la tarea.

RTTI, conversiones dinámicas, herencia múltiple, polimorfismo pesado y excepciones vienen con una cierta cantidad de costo de tiempo de ejecución para su uso. Algunas de esas funciones nivelan el costo de todo el programa si se usan, otras simplemente aumentan el peso de las clases que las necesitan. Conozca la diferencia y elija las funciones avanzadas sabiamente con pleno conocimiento de al menos un análisis superficial de costo / beneficio.

En un entorno integrado pequeño, se vinculará directamente a un kernel en tiempo real o se ejecutará directamente en el hardware. De cualquier manera, deberá asegurarse de que su código de inicio en tiempo de ejecución maneje correctamente las tareas de inicio específicas de C ++. Esto puede ser tan simple como asegurarse de usar las opciones correctas del enlazador, pero dado que es común tener control directo sobre la fuente hasta el punto de entrada de reinicio, es posible que deba auditarlo para asegurarse de que hace todo. Por ejemplo, en una plataforma ColdFire en la que trabajé, las herramientas de desarrollo se enviaron con un módulo CRT0.S que tenía los inicializadores C ++ presentes pero comentados. Si lo hubiera usado directamente de la caja, me habrían desconcertado los objetos globales cuyos constructores nunca se habían ejecutado.

Además, en un entorno integrado, a menudo es necesario inicializar los dispositivos de hardware antes de que puedan usarse, y si no hay un sistema operativo ni un cargador de arranque, entonces es su código el que hace eso. Deberá recordar que los constructores para objetos globales se ejecutan antes de que main() se llamen, por lo que deberá modificar su CRT0.S local (o su equivalente) para realizar la inicialización del hardware antes de que se llamen a los propios constructores globales. Obviamente, la cima main()es demasiado tarde.

RBerteig
fuente
1
¡Este necesita más votos a favor de los que puedo darle! Gran respuesta.
Harper Shelby
+1, gran respuesta. Pero creo que la única instanciación de plantilla de la que realmente debe preocuparse es la del tipo recursivo (relativamente raro); para el tipo "regular" no recursivo, la instanciación equivale al código que habría escrito manualmente de todos modos.
j_random_hacker
2
@j_random_hacker, cierto. Pero el hábito de las plantillas puede dar lugar a sorpresas ocasionales cuando aparece una segunda (o tercera) instanciación donde la coerción de tipo adecuada en el punto de uso podría haberlo impedido. Es algo de lo que hay que tener cuidado.
RBerteig
@RBerteig: Buen punto, las plantillas permiten menos posibilidades de coerción de tipos => posiblemente se producen más instancias distintas que con código sin plantilla.
j_random_hacker
26

No. Cualquiera de las características del lenguaje C ++ que podrían causar problemas (polimorfismo en tiempo de ejecución, RTTI, etc.) se puede evitar mientras se realiza el desarrollo integrado. Hay una comunidad de desarrolladores de C ++ incrustado (recuerdo haber leído columnas de desarrolladores incrustados que usaban C ++ en el antiguo Diario de usuarios de C / C ++), y no puedo imaginar que hablarían mucho si la elección fuera tan mala.

Harper Shelby
fuente
20

El Informe técnico sobre el rendimiento de C ++ es una gran guía para este tipo de cosas. Tenga en cuenta que tiene una sección sobre cuestiones de programación integrada.

Además, ++ sobre la mención de Embedded C ++ en las respuestas. El estándar no es 100% para mis gustos, pero es una buena referencia a la hora de decidir qué partes de C ++ podrías eliminar.

Al programar para plataformas pequeñas, deshabilitamos las excepciones y RTTI, evitamos la herencia virtual y prestamos mucha atención a la cantidad de funciones virtuales que tenemos por ahí.

Sin embargo, su amigo es el mapa del enlazador: revíselo con frecuencia y detectará rápidamente las fuentes de código y la memoria estática.

Después de eso, se aplican las consideraciones de uso de memoria dinámica estándar: en un entorno tan restringido como el que menciona, es posible que no desee utilizar asignaciones dinámicas en absoluto. A veces puede salirse con la suya con grupos de memoria para pequeñas asignaciones dinámicas, o asignación "basada en marcos" en la que preasigna un bloque y desecha todo más tarde.

leander
fuente
16

Recomiendo usar el compilador de C ++, pero limitando el uso de características específicas de C ++. Puede programar como C en C ++ (el tiempo de ejecución de C se incluye al hacer C ++, aunque en la mayoría de las aplicaciones integradas no hace uso de la biblioteca estándar de todos modos).

Puede seguir adelante y usar clases de C ++, etc., solo

  • Limite su uso de funciones virtuales (como ha dicho)
  • Limite el uso de plantillas
  • Para una plataforma integrada, querrá anular el operador nuevo y / o usar la ubicación nueva para la asignación de memoria.
arca
fuente
8
Por supuesto, si ya está escribiendo básicamente C, también puede hacerlo oficial.
Chuck
6
¿Por qué limita el uso de plantillas? Pensé que las funciones de plantilla podrían ser realmente útiles en sistemas integrados, por ejemplo, para desenrollar bucles.
Piotr Czapla
1
Aún puede usar plantillas, pero tendría mucho cuidado con ellas, ya que pueden aumentar rápidamente el tamaño del binario de salida. Por supuesto, si su código se ejecuta directamente desde la ROM o similar y tiene espacio en la ROM de sobra, entonces claro, pero aparte de eso, debe tener cuidado con lo que hace con las plantillas (cada instancia de plantilla es básicamente todo el código de plantilla duplicado nuevamente en el ejecutable final en el peor de los casos).
arke
14

Como ingeniero de firmware / sistemas embebidos, puedo decirles algunas de las razones por las que C sigue siendo la opción número 1 sobre C ++ y sí, los domino ambos.

1) Algunos objetivos en los que desarrollamos tienen 64kB de RAM para código y datos, por lo que debe asegurarse de que cada byte cuente, y sí, me he ocupado de la optimización del código para ahorrar 4 bytes que me costaron 2 horas, y eso es en 2008.

2) Cada función de la biblioteca C se revisa antes de dejarlas en el código final, debido a la limitación de tamaño, por lo que preferimos que las personas no usen dividir (sin divisor de hardware, por lo que se necesita una biblioteca grande), malloc (porque no tenemos montón , toda la memoria se asigna desde el búfer de datos en un bloque de 512 bytes y debe revisarse el código) u otra práctica orientada a objetos que conlleva una gran penalización. Recuerde, cada función de biblioteca que use cuenta.

3) ¿Has oído hablar del término superposición? tiene tan poco espacio de código que a veces tiene que cambiar cosas con otro conjunto de código. Si llama a una función de biblioteca, la función de biblioteca debe ser residente. Si solo lo usa en una función de superposición, está desperdiciando mucho espacio confiando en demasiados métodos orientados a objetos. Por lo tanto, no asuma que se aceptará ninguna función de biblioteca C, y mucho menos C ++.

4) La conversión y el empaquetado uniforme (donde la estructura de datos no alineada cruza el límite de la palabra) son necesarios debido al diseño de hardware limitado (es decir, un motor ECC que está cableado de cierta manera) o para hacer frente a un error de hardware. No puedes asumir demasiado implícitamente, entonces, ¿por qué orientarlo demasiado?

5) El peor de los casos: la eliminación de algunos de los métodos orientados a objetos obligará a los desarrolladores a pensar antes de usar recursos que pueden explotar (es decir, asignar 512 bytes en una pila en lugar de un búfer de datos) y evitar algunos de los posibles peores escenarios que no se prueban ni eliminan toda la ruta del código por completo.

6) Usamos mucha abstracción para mantener el hardware lejos del software y hacer que el código sea lo más portátil posible y amigable con la simulación. El acceso al hardware debe estar envuelto en una función macro o en línea que se compila condicionalmente entre diferentes plataformas, el tipo de datos debe ser convertido en tamaño de byte en lugar de específico de destino, el uso de puntero directo no está permitido (porque algunas plataformas asumen que la E / S mapeada en memoria es igual que la memoria de datos), etc.

Puedo pensar en más, pero entiendes la idea. Nosotros, los de firmware, tenemos entrenamiento orientado a objetos, pero la tarea del sistema embebido puede estar tan orientada al hardware y de bajo nivel, que no es de alto nivel o abstraíble por naturaleza.

Por cierto, cada trabajo de firmware en el que he estado usa control de fuente, no sé de dónde sacas esa idea.

-Un tipo de firmware de SanDisk.

Shing Wong
fuente
a principios de los 90, la superposición era una técnica muy popular (al menos en el mundo de DOS)
psihodelia
Buenos puntos Shing. C ++ se siente como un luchador de sumo en una cabina telefónica en proyectos donde la funcionalidad es limitada y los recursos son aún más limitados.
4
Creo que esta respuesta es muy subjetiva y no proporciona un razonamiento concreto.
Venemo
1
C ++ no significa necesariamente "orientado a objetos".
Martin Bonner apoya a Monica el
1
Simplemente no es cierto que la tarea del sistema integrado no se pueda abstraer por naturaleza. Usted mismo lo dijo en el punto 6): "usamos mucha abstracción para mantener hw de sw y hacer que el código sea lo más portátil posible" :-) Por cierto: "abstracción" no implica necesariamente "polimorfismo".
Daniele Pallastrelli
9

Mi preferencia personal es C porque:

  • Sé lo que hace cada línea de código (y los costos)
  • No conozco C ++ lo suficientemente bien como para saber qué está haciendo cada línea de código (y qué cuesta)

¿Por qué la gente dice esto? Usted no sabe lo que cada línea de C está haciendo a menos que marque la salida asm. Lo mismo ocurre con C ++.

Por ejemplo, qué asm produce esta declaración inocente:

a[i] = b[j] * c[k];

Parece bastante inocente, pero un compilador basado en gcc produce este conjunto para un micro de 8 bits

CLRF 0x1f, ACCESS
RLCF 0xfdb, W, ACCESS
ANDLW 0xfe
RLCF 0x1f, F, ACCESS
MOVWF 0x1e, ACCESS
MOVLW 0xf9
MOVF 0xfdb, W, ACCESS
ADDWF 0x1e, W, ACCESS
MOVWF 0xfe9, ACCESS
MOVLW 0xfa
MOVF 0xfdb, W, ACCESS
ADDWFC 0x1f, W, ACCESS
MOVWF 0xfea, ACCESS
MOVFF 0xfee, 0x1c
NOP
MOVFF 0xfef, 0x1d
NOP
MOVLW 0x1
CLRF 0x1b, ACCESS
RLCF 0xfdb, W, ACCESS
ANDLW 0xfe
RLCF 0x1b, F, ACCESS
MOVWF 0x1a, ACCESS
MOVLW 0xfb
MOVF 0xfdb, W, ACCESS
ADDWF 0x1a, W, ACCESS
MOVWF 0xfe9, ACCESS
MOVLW 0xfc
MOVF 0xfdb, W, ACCESS
ADDWFC 0x1b, W, ACCESS
MOVWF 0xfea, ACCESS
MOVFF 0xfee, 0x18
NOP
MOVFF 0xfef, 0x19
NOP
MOVFF 0x18, 0x8
NOP
MOVFF 0x19, 0x9
NOP
MOVFF 0x1c, 0xd
NOP
MOVFF 0x1d, 0xe
NOP
CALL 0x2142, 0
NOP
MOVFF 0x6, 0x16
NOP
MOVFF 0x7, 0x17
NOP
CLRF 0x15, ACCESS
RLCF 0xfdf, W, ACCESS
ANDLW 0xfe
RLCF 0x15, F, ACCESS
MOVWF 0x14, ACCESS
MOVLW 0xfd
MOVF 0xfdb, W, ACCESS
ADDWF 0x14, W, ACCESS
MOVWF 0xfe9, ACCESS
MOVLW 0xfe
MOVF 0xfdb, W, ACCESS
ADDWFC 0x15, W, ACCESS
MOVWF 0xfea, ACCESS
MOVFF 0x16, 0xfee
NOP
MOVFF 0x17, 0xfed
NOP

El número de instrucciones producidas depende enormemente de:

  • Los tamaños de a, by c.
  • si esos punteros están almacenados en la pila o son globales
  • si i, j y k están en la pila o son globales

Esto es especialmente cierto en el pequeño mundo integrado, donde los procesadores simplemente no están configurados para manejar C.Entonces mi respuesta sería que C y C ++ son tan malos como el otro, a menos que siempre examines la salida de asm, en cuyo caso son tan buenos como los demás.

Hugo

Cohetemagnet
fuente
2
Observe también que hay una instrucción de llamada en medio de todo lo que realmente llama a la función de multiplicación. ¡Todo ese código ni siquiera es una instrucción multiplicada!
Rocketmagnet
Alguien familiarizado con un micro normalmente conocería una forma sencilla de procesar cada parte del código C de forma aislada, y un compilador decente no debería producir un código peor que eso. La única forma en que la expresión anterior podría procesarse de manera eficiente sería si se hicieran suposiciones que podrían no ser apropiadas para un compilador de C.
supercat
8

He escuchado que algunas personas prefieren C para el trabajo integrado debido al hecho de que es más simple y, por lo tanto, más fácil de predecir el código real que se generará.

Personalmente, creo que escribir C ++ al estilo C (usando plantillas para la seguridad de tipos) le daría muchas ventajas y no veo ninguna razón real para no hacerlo.

user21714
fuente
+1, la transparencia es siempre importante, y probablemente más para un entorno restringido con (presumiblemente) herramientas de depuración restringidas.
j_random_hacker
7

No veo ninguna razón para usar C en lugar de C ++. Cualquier cosa que pueda hacer en C, también puede hacerlo en C ++. Si desea evitar los gastos generales de VMT, no utilice métodos virtuales ni polimorfismo.

Sin embargo, C ++ puede proporcionar algunos modismos muy útiles sin gastos generales. Uno de mis favoritos es RAII. Las clases no son necesariamente caras en términos de memoria o rendimiento ...

Cătălin Pitiș
fuente
6

Escribí un código para la plataforma integrada ARM7 en IAR Workbench. Recomiendo encarecidamente confiar en las plantillas para realizar la optimización en tiempo de compilación y la predicción de rutas. Evite el lanzamiento dinámico como una plaga. Utilice características / políticas a su favor, como se indica en el libro de Andrei Alexandrescu, Diseño moderno en C ++ .

Lo sé, puede ser difícil de aprender, pero también estoy seguro de que su producto se beneficiará de este enfoque.

GregC
fuente
5

Una buena razón, y a veces la única, es que todavía no existe un compilador C ++ para el sistema integrado específico. Este es el caso, por ejemplo, de los microcontroladores PIC de Microchip . Son muy fáciles de escribir y tienen un compilador C gratuito (en realidad, una ligera variante de C) pero no hay un compilador C ++ a la vista.

shoosh
fuente
1
Comeau Computing ( comeaucomputing.com ) vende un compilador de C ++ que compila a C.
Thomas L Holaday
3
Eww. Ese sitio dio ganas de vomitar.
Shoosh
@shoosh: Sí, el diseño del sitio es terrible. Sin embargo, el compilador en sí se considera un líder en el campo, al menos en términos de cumplimiento estándar (no tengo información sobre el rendimiento).
j_random_hacker
Ese sitio web me hace sentir como si estuviera atrapado dentro de una ensalada de frutas viva, respirando y MUY enojada.
Tim Post
5

Para un sistema restringido a 4K de RAM, usaría C, no C ++, solo para que pueda estar seguro de ver todo lo que está sucediendo. Lo que pasa con C ++ es que es muy fácil usar muchos más recursos (tanto CPU como memoria) de lo que parece al mirar el código. (Oh, solo crearé otro BlerfObject para hacer eso ... ¡Ups! ¡Sin memoria!)

Puede hacerlo en C ++, como ya se mencionó (sin RTTI, sin vtables, etc., etc.), pero dedicará tanto tiempo a asegurarse de que su uso de C ++ no se le escape como lo haría con el equivalente en C .

Michael Kohne
fuente
2
Su última oración es correcta pero irrelevante ya que C ++ ofrece otras ventajas sobre C que (podrían) inclinar la balanza. Piotr ya ha mencionado algunas de estas ventajas (de coste cero).
Konrad Rudolph
5

La mente humana se ocupa de la complejidad evaluando tanto como sea posible y luego decidiendo en qué es importante concentrarse y descartando o depreciando el resto. Este es todo el fundamento detrás de la marca en marketing y, en gran medida, los iconos.

Para combatir esta tendencia, prefiero C a C ++, porque te obliga a pensar en tu código y en cómo interactúa con el hardware más de cerca, de manera implacable.

Por mi larga experiencia, creo que C lo obliga a encontrar mejores soluciones a los problemas, en parte, saliéndose de su camino y no obligándolo a perder mucho tiempo satisfaciendo una restricción que algún compilador-escritor pensó que era una buena idea. , o averiguar qué está pasando "debajo de las sábanas".

En ese sentido, los lenguajes de bajo nivel como C te hacen pasar mucho tiempo concentrado en el hardware y construyendo buenos paquetes de algoritmos / estructura de datos, mientras que los lenguajes de alto nivel te hacen pasar mucho tiempo rascándote la cabeza preguntándote qué está pasando allí. y por qué no puede hacer algo perfectamente razonable en su contexto y entorno específicos. Hacer que su compilador se someta (el tipo fuerte es el peor infractor) NO es un uso productivo del tiempo.

Probablemente encajo bien en el molde del programador, me gusta el control. En mi opinión, eso no es un defecto de personalidad para un programador. El control es lo que nos pagan por hacer. Más específicamente, control impecable. C te da mucho más control que C ++.

Deshacer
fuente
Martin Sistrik, el autor de ZeroMQ hizo casi el mismo punto en su discusión de por qué deseaba ahora haber escrito ZeroMQ en C, en lugar de C ++. Échale un vistazo a 250bpm.com/blog:8
user2548100
3

Personalmente, con 4kb de memoria, diría que no está obteniendo mucho más rendimiento de C ++, así que elija la que parezca la mejor combinación de compilador / tiempo de ejecución para el trabajo, ya que el lenguaje probablemente no va a importar mucho.

Tenga en cuenta que tampoco se trata solo del idioma, ya que también importa la biblioteca. A menudo, las bibliotecas de C tienen un tamaño mínimo un poco más pequeño, pero me imagino que una biblioteca de C ++ destinada al desarrollo integrado está cortada, así que asegúrese de probar.

Marco van de Voort
fuente
2

C gana en portabilidad, porque es menos ambiguo en las especificaciones del lenguaje; por lo tanto, ofrece una portabilidad y flexibilidad mucho mejores entre diferentes compiladores, etc. (menos dolores de cabeza).

Si no va a aprovechar las funciones de C ++ para satisfacer una necesidad, elija C.

Oliver
fuente
Que el lenguaje sea inequívoco depende de si se considera que especifica cosas que solían considerarse de sentido común, pero que hoy en día no lo son [por ejemplo, que un compilador para hardware de complemento a dos silencioso envolvente de 32 bits debería procesar algo unsigned mul(unsigned short x, unsigned short y) { return x*y;}como que no tiene efectos secundarios incluso si el producto excede 2147483647, o que se considere void get_float_bits(float *fp, uint32_t n) { *(uint32_t)fp = n; }que posiblemente altere el valor de a float].
supercat
2

¿Ve alguna razón para seguir con C89 al desarrollar para hardware muy limitado (4kb de RAM)?

Personalmente, cuando se trata de aplicaciones integradas (cuando digo incrustadas, no me refiero a winCE, iPhone, etc., dispositivos integrados inflados en la actualidad). Me refiero a dispositivos con recursos limitados. Prefiero C, aunque también he trabajado bastante con C ++.

Por ejemplo, el dispositivo del que estás hablando tiene 4kb de RAM, bueno, solo por esa razón no consideraría C ++. Claro, es posible que pueda diseñar algo pequeño usando C ++ y limitar su uso en su aplicación como han sugerido otras publicaciones, pero C ++ "podría" potencialmente terminar complicando / hinchando su aplicación bajo las sábanas.

¿Vas a enlazar estáticamente? Es posible que desee comparar estática con una aplicación ficticia usando c ++ vs c. Eso puede llevarlo a considerar C en su lugar. Por otro lado, si puede construir una aplicación C ++ dentro de sus requisitos de memoria, hágalo.

En mi humilde opinión, en general, en las aplicaciones integradas me gusta saber todo lo que está sucediendo. ¿Quién usa los recursos de memoria / sistema, cuánto y por qué? ¿Cuándo los liberan?

Al desarrollar para un objetivo con X cantidad de recursos, CPU, memoria, etc., trato de mantenerme en el lado inferior del uso de esos recursos porque nunca se sabe qué requisitos futuros vendrán, por lo que agregará más código al proyecto se "suponía" que era una aplicación pequeña y sencilla, pero acaba volviéndose mucho más grande.

Steve Lazaridis
fuente
1
Definitivamente compararé los dos compiladores. (por cierto. No puedo vincular dinámicamente porque no hay sistema operativo)
Piotr Czapla
2

Mi elección suele estar determinada por la biblioteca C que decidimos usar, que se selecciona en función de lo que el dispositivo necesita hacer. Entonces, 9/10 veces ... termina siendo uclibc o newlib y C. El kernel que usamos es una gran influencia en esto también, o si estamos escribiendo nuestro propio kernel.

También es una elección de terreno común. La mayoría de los buenos programadores de C no tienen problemas para usar C ++ (aunque muchos se quejan todo el tiempo de que lo usan) ... pero no he encontrado que lo contrario sea cierto (en mi experiencia).

En un proyecto en el que estamos trabajando (que involucra un kernel básico), la mayoría de las cosas se hacen en C, sin embargo, se implementó una pequeña pila de red en C ++, porque era más fácil y menos problemático implementar redes usando C ++.

El resultado final es que el dispositivo funcionará y pasará las pruebas de aceptación o no lo hará. Si puede implementar foo en las restricciones xx stack y yy heap usando el lenguaje z, hágalo, use lo que lo haga más productivo.

Mi preferencia personal es C porque:

  • Sé lo que hace cada línea de código (y los costos)
  • No conozco C ++ lo suficientemente bien como para saber qué está haciendo cada línea de código (y qué cuesta)

Sí, me siento cómodo con C ++, pero no lo conozco tan bien como el estándar C.

Ahora bien, si puedes decir lo contrario, usa lo que sabes :) Si funciona, pasa pruebas, etc. ¿Cuál es el problema?

Tim Post
fuente
2
> # Sé lo que hace cada línea de código (y los costos) Habiendo escrito compiladores, no estaría tan seguro de eso ... un buen compilador de C puede hacer cosas bastante sorprendentes con su código, ya que tiene una buena descripción global de cosas. No compila línea por línea.
jakobengblom2
@ jakobengblom2: Para el desarrollo integrado, tener un rendimiento constante es a menudo más importante que tener el máximo rendimiento. Si uno está tratando de determinar si un fragmento de código cumplirá con los requisitos de tiempo, hacer que un compilador emplee optimizaciones que serán utilizables en firmware de "prueba" que no funcionará en firmware real probablemente no será de mucha ayuda.
supercat
2

¿Cuánta ROM / FLASH tienes?

4kB de RAM aún puede significar que hay cientos de kilobytes de FLASH para almacenar el código real y los datos estáticos. La RAM de este tamaño tiende a estar destinada solo a variables, y si tiene cuidado con ellas, puede colocar un programa bastante grande en términos de líneas de código en la memoria.

Sin embargo, C ++ tiende a dificultar la colocación de código y datos en FLASH, debido a las reglas de construcción en tiempo de ejecución de los objetos. En C, una estructura constante se puede colocar fácilmente en la memoria FLASH y acceder a ella como un objeto constante de hardware. En C ++, un objeto constante requeriría que el compilador evalúe el constructor en tiempo de compilación, lo que creo que aún está más allá de lo que puede hacer un compilador de C ++ (teóricamente, podría hacerlo, pero es muy, muy difícil de hacer en la práctica) .

Entonces, en un entorno de "RAM pequeña", "FLASH grande", optaría por C cualquier día. Tenga en cuenta que una buena opción intermedia es C99, que tiene la mayoría de las características agradables de C ++ para código no basado en clases.

jakobengblom2
fuente
3
¿Hay alguna razón por la que la misma estructura que se colocaría en la memoria Flash en C no terminaría también en Flash en C ++? No tiene que agregar un constructor a su estructura en C ++.
jalf
1

En general no. C ++ es un superconjunto de C. Esto sería especialmente cierto para nuevos proyectos.

Está en el camino correcto para evitar construcciones de C ++ que pueden ser costosas en términos de tiempo de CPU y espacio de memoria.

Tenga en cuenta que algunas cosas, como el polimorfismo, pueden ser muy valiosas: son esencialmente punteros de función. Si encuentra que los necesita, utilícelos sabiamente.

Además, un buen manejo de excepciones (bien diseñado) puede hacer que su aplicación incorporada sea más confiable que una aplicación que maneja cosas con códigos de error tradicionales.

Foredecker
fuente
2
C ++ no es, estrictamente hablando, un superconjunto estricto de C, pero ese detalle particular no es particularmente sustancial en este contexto.
Arafangion
1

Para problemas de asignación de memoria, puedo recomendar el uso de Quantum Platform y su enfoque de máquina de estado, ya que asigna todo lo que necesita en el momento de la inicialización. También ayuda a aliviar los problemas de contención.

Este producto se ejecuta tanto en C como en C ++.

GregC
fuente
1

Algunos dicen que los compiladores de C pueden generar código mucho más eficiente porque no tienen que admitir las funciones avanzadas de C ++ y, por lo tanto, pueden ser más agresivos en sus optimizaciones.

Por supuesto, en este caso es posible que desee poner a prueba los dos compiladores específicos.

Yishai
fuente
1
Relacionado: Hasta donde yo sé, la palabra clave restrict es la única construcción de C relacionada con la optimización que falta en C ++ (también C ++ 11).
Johan Lundberg
1

La única razón para preferir C en mi humilde opinión sería si el compilador de C ++ para su plataforma no está en buena forma (con errores, optimización deficiente, etc.).

Nemanja Trifunovic
fuente
¿Qué pasa con la utilización de memoria / recursos?
Steve Lazaridis
¿Qué pasa con eso? No hay razón para que un compilador de C ++ produzca un código menos eficiente que uno de C, excepto si el código usa RTTI, lo que nadie hace en los sistemas integrados.
Nemanja Trifunovic
1

Tienes en línea en C99. Tal vez te gusten los dtors, pero el asunto de hacer los dtors bien puede ser complicado. Si la única razón restante para no usar C son los espacios de nombres, realmente me quedaría con C89. Esto se debe a que es posible que desee portarlo a una plataforma integrada ligeramente diferente. Más tarde, puede comenzar a escribir en C ++ en ese mismo código. Pero tenga cuidado con lo siguiente, donde C ++ NO es un superconjunto de C. Sé que dijo que tiene un compilador de C89, pero hace esta comparación de C ++ con C99 de todos modos, ya que el primer elemento, por ejemplo, es válido para cualquier C desde K&R.

sizeof 'a' > 1 en C, no en C ++. En C tiene matrices de longitud variable VLA. Ejemplo: func (int i) {int a [i] . En C tiene miembros de matriz de variables VAM. Ejemplo: struct {int b; int m [];} .

hept
fuente
1
No. Quiero mencionar que en C tienes (sizeof 'a') == sizeof (int). Mientras que en C ++ tiene ese 1 == tamaño de 'a'
hept
1
Sin mencionar "int * a; ...; a = (int *) malloc (size * sizeof (int));" es la forma de asignar memoria que funciona en C y C ++, y no debe usarse en ninguno. Utilice "a = malloc (tamaño * tamaño de (int));" o "vector <int> a (tamaño)"; o incluso "int * a = new int [size];" en lugar.
David Thornley
1
No entiendo tu punto sobre los dtors. El punto sobre ellos es que hacen que el resto de su código sea mucho menos complicado.
jalf
1
+1, no estoy seguro de por qué esta publicación tuvo tan mala reputación. Pero estoy de acuerdo con jalf, los destructores simplifican enormemente el código cuando se usan de la manera correcta (RAII). (Se podría decir que "trabajan entre bastidores", pero solo hacen cosas que el código correcto haría manualmente de todos modos).
j_random_hacker
1
Creo que las cosas que señalo son muy relevantes para la pregunta. También me atengo a mi afirmación de que los dtors pueden ser difíciles, y la razón es exactamente que ocurre automáticamente. Obtuve puntos negativos, eso es realmente difícil. Supongo que es porque no digo "SÍ, IR C ++ es genial".
hept
1

Depende del compilador.

No todos los compiladores integrados implementan todo C ++, e incluso si lo hacen, es posible que no sean buenos para evitar el exceso de código (que siempre es un riesgo con las plantillas). Pruébelo con algunos programas más pequeños, vea si tiene algún problema.

Pero dado un buen compilador, no, no hay razón para no usar C ++.

jalf
fuente
1

Solo quiero decir que no hay ningún sistema con recursos "ILIMITADOS". Todo en este mundo es limitado y CADA aplicación debe considerar el uso de recursos sin importar si es ASM, C, JAVA o JavaScript. Los maniquíes que asignan unos pocos Mbs "solo para estar seguros" hacen que el iPhone 7, Pixel y otros dispositivos sean extremadamente inestables. No importa si tiene 4kb o 40 Gb.

Pero desde otro lado, oponerse al desperdicio de recursos, es un tiempo que lleva ahorrar esos recursos. Si se necesita 1 semana extra para escribir algo simple en C para ahorrar algunos tics y algunos bytes en lugar de usar C ++ ya implementado, probado y distribuido. ¿Por qué molestarse? Es como comprar un concentrador USB. sí, puedes hacerlo tú mismo, pero ¿será mejor? ¿más confiable? más barato si cuentas tu tiempo?

Solo un pensamiento lateral: incluso la energía de su tomacorriente no es ilimitada. Trate de investigar de dónde viene y verá principalmente que se quema algo. La ley de la energía y la materia sigue vigente: ningún material o energía aparece ni desaparece, sino que se transforma.

Alex Paniutin
fuente
0

Acabo de encontrar un ejemplo de cómo usar ISO C ++ para el desarrollo integrado, que podría ser interesante para alguien que está tomando la decisión cada vez que usa C ++ o C.

Fue proporcionado por Bjarne Stroustrup en su página de inicio :

Para ver cómo se puede utilizar ISO C ++ para la programación de sistemas integrados serios, consulte los estándares de codificación C ++ de vehículos aéreos JSF .

Piotr Czapla
fuente
Bueno, las cosas voladoras tienden a tener procesadores PPC con gigabytes de RAM. No es su sistema integrado promedio con recursos limitados.
jakobengblom2
0

Publicación de respuesta diferente a un aspecto diferente de la pregunta:

"malloc"

Algunas respuestas anteriores hablan bastante sobre esto. ¿Por qué crees que existe esa llamada? Para una plataforma realmente pequeña, malloc tiende a no estar disponible, o definitivamente es opcional. La implementación de la asignación de memoria dinámica tiende a ser significativa cuando tiene un RTOS en la parte inferior de su sistema, pero hasta entonces, es puramente peligroso.

Puedes llegar muy lejos sin él. Solo piense en todos los programas antiguos de FORTRAN que ni siquiera tenían una pila adecuada para las variables locales ...

jakobengblom2
fuente
0

Hay varios fabricantes de controladores diferentes en todo el mundo y cuando echas un vistazo a sus diseños y los conjuntos de instrucciones que deben usarse para configurar, puedes terminar en muchos problemas. La principal desventaja del lenguaje ensamblador es que depende de la máquina / arquitectura. Es realmente importante pedirle a un desarrollador que se memorice todas las instrucciones que se establecen allí para lograr la codificación para diferentes controladores. Es por eso que C se hizo más popular en el desarrollo integrado porque C es lo suficientemente alto como para abstraer los algoritmos y las estructuras de datos de los detalles que dependen del hardware, lo que hace que el código fuente sea portátil en una amplia variedad de hardware de destino, lenguaje independiente de la arquitectura y muy fácil de usar. convertir y mantener el código. Pero vemos algunos lenguajes de alto nivel (orientados a objetos) como C, C ++, Python, Java, etc.

mohammed_thoyyib_tk
fuente
0

Recomiendo C ++ con limitaciones y notas.

  1. Tiempo de comercialización y mantenibilidad. El desarrollo de C ++ es más fácil y rápido. Entonces, si está en la fase de diseño, elija un controlador lo suficientemente bueno como para usar C ++. (Tenga en cuenta que algunos mercados de alto volumen requieren el menor costo posible, donde no puede tomar esta decisión).

  2. Velocidad. C puede ser más rápido que C ++, pero asegúrese de que la ganancia de velocidad no sea grande. Entonces puedes ir con C ++. Desarrolle sus algoritmos, pruébelos y hágalos más rápidos solo si es necesario (!). Utilice perfiladores para señalar los cuellos de botella y reescribirlos en forma de "C" externa , para lograr la velocidad C. (Si aún es lento, implemente esa parte en ASM)

  3. Tamaño binario. Los códigos de C ++ son más grandes, pero aquí hay una gran respuesta que cuenta los detalles. El tamaño del binario compilado de un código C dado será el mismo si se compiló utilizando el compilador C o C ++. "El tamaño del ejecutable no está relacionado con el lenguaje, sino con las bibliotecas que incluye en su proyecto". Ir con C ++, pero evitar funcionalidades avanzadas, como streams, string, new, virtualfunciones, etc. Revisar toda la función de la biblioteca antes de dejarlos en el código final, debido a la limitación de tamaño (en base a esta respuesta)

betontalpfa
fuente