C vs C++ – Mikrocontroller.net (2024)

Speziell bei der Verwendung höherer Programmiersprachen (HLLs) im Bereich der Programmierung von Mikrocontrollern taucht häufig die Frage auf, ob eine Sprache wie C++ für "kleinere" Mikroprozessoren nicht einen zu großen Overhead darstelle und ob man nicht besser bei C (oder gar Assembler) bleiben solle.

Inhaltsverzeichnis

  • 1 Beziehung zwischen C und C++
  • 2 C++-Performance-Fußangeln
    • 2.1 Fußangel-Beispiel 1
    • 2.2 Fußangel-Beispiel 2
  • 3 Vererbung und Aggregation
  • 4 Virtuelle Methoden
  • 5 Interfaces (Abstrakte Basisklassen)
  • 6 Exceptions (Ausnahmebehandlung)
  • 7 Laufzeit-Typinformation
  • 8 Templates (Schablonen)

Beziehung zwischen C und C++

Ursprünglich entstand C++ als Erweiterung zu C (mit minimalen Ausnahmen - Leitlinie: "As close to C as possible, but no closer"). Damit ist C als Teilmenge in C++ enthalten und jedes C-Programm ist zugleich ein C++-Programm, denn es kann auch mit einem C++-Compiler übersetzt werden (nicht aber umgekehrt!). Da ein weiterer Grundsatz bei der C++-Entwicklung lautete "You only pay for what you use", entsteht in einem Programm, das ausschließlich C-Features benutzt aber mit einem C++-Compiler übersetzt wird, kein prinzipiell bedingter Overhead.

So weit die Theorie.

Der Artikel wurde ursprünglich im Jahr 2004 verfasst. Einige der im weiteren Verlauf genannten Punkte, insbesondere bzgl. Optimierungen, Performance und unnötige Funktionsaufrufe, treffen auf aktuelle Compiler nicht mehr zu. Es gibt verschiedene (Web-basierte) Tools, um das Compiler-Ergebnis von C++ Code zu untersuchen:

  • Compiler Explorer (https://godbolt.org) - Hilft zu zeigen, wie der vom C++ Compiler erzeugte Assembler Code aussieht.
  • C++ Insights (https://cppinsights.io) - Hiermit lässt sich nachvollziehen, was ein C++ Konstrukt auf Code Ebene erzielt.

C++-Performance-Fußangeln

Allerdings waren die Zielsysteme bei der Weiterentwicklung von C++ in den letzten 20 Jahren in der Regel PCs und leistungsfähige Workstations, was in der Praxis zwei wichtige Konsequenzen hatte:

  • Die Sprachsyntax von C++ verschleiert mehr als die von C, womit man sich in einem Programm möglicherweise einen hohen, in der Programmierung von Mikrocontrollern unerwünschten Overhead ohne konkreten Nutzen einhandelt.
  • Die tatsächlichen Implementierungen von C++ bemühen sich weniger als die von C, ausführbare Programme (Executables) in der sparsamsten möglichen Form zu erzeugen.

Fußangel-Beispiel 1

Mitunter werden beim Einsatz von C++ Programmierrichtlinien angewendet, die im Bereich der Mikrocontrollern-Programmierung unsinnig sind oder zumindest nach anderen Kriterien bewertet werden müssen als im PC- und Workstation-Umfeld.So führen etwa "vorsorglich angelegte", aber leere Konstruktoren und Destruktoren in der Regel zu vielen Unterprogramm-Sprungbefehlen, an deren Ziel sofort wieder ein Rücksprung steht.

Oder es werden - dem Beispiel von Java oder bestimmten Entwurfsmustern folgend - Objekte in jedem Fall dynamisch angelegt, was das Einbinden komplexer Unterstützungsroutinen für die Speicherverwaltung auslöst und in keinem Fall zu Ergebnissen führt, deren Performance vergleichbar zu einfachen Variablen auf dem Stack oder an festen Adressen ist.

Fußangel-Beispiel 2

Manchmal wird ein einfaches "hello, world"-Programm in C und eines in C++ auf einem PC kompiliert und die Größe des erzeugten Executables als Kriterium für den Ressourcen-Bedarf der jeweiligen Programmiersprache herangezogen. Dieser Vergleich kann sehr in die Irre führen, da die typischen Ausgabe-Anweisungen der Sprachen C und C++ von Bibliotheksfunktionen abgewickelt werden, die aber mit Sicherheit bei der Kompilierung für einen Mikrocontroller ganz anders aussehen (wenn sie dort überhaupt vorhanden sind).

Bei der Strukturierung von unterstützenden Bibliotheksfunktionen wird im PC- und Workstationbereich kaum darauf geachtet, ob einem Minimal-Programm möglicherweise unnötiger Overhead mit hinzugebunden wird. So könnte beispielsweise - obwohl nur eine einfache Zeichenkette ausgegeben wird - durch eine Ausgabeanweisung die Unterstützung für die Ausgabe aller Datentypen inklusive Gleitpunktzahlen angesprochen werden, dies könnte wiederum das Hinzubinden der mathematischen Bibliothek auslösen und - bei Kompilierung für einen Prozessor ohne Hardware-Support für Gleitpunkt-Operationen - einer weiteren Bibliothek zu deren Emulation. Das alles, wie gesagt, obwohl überhaupt keine Gleitpunktzahl in der konkreten Ausgabe vorkommt.

Vererbung und Aggregation

Beide Mechanismen sind aus Sicht der Umsetzung als geschachtelte Strukturen zu verstehen. Eventuelle kleinere Performance-Einbußen könnten durch die fehlende Möglichkeit der Register-Optimierung entstehen, die ein Compiler bei mehreren, nicht zu einer Struktur zusammengefassten Variablen prinzipiell hat. Allerdings sind die Vor- und Nachteile hier in C und C++ die selben, und insofern ist das Thema für einen Vergleich "C vs. C++" nicht relevant.

In größeren Programmen überwiegt in aller Regel der Vorteil der besseren Strukturierung durch die Zusammenfassung von Einzelvariablen, insbesondere wenn auch die in C++ vorhandenen Mechanismen zum Zugriffsschutz eingesetzt werden. In kleinen Programmabschnitten mit extrem kritischer Performance kann ggf. eine "Hand-Optimierung" und Auflösung von Strukturen in Einzelvariablen erfolgen (oder gleich eine Re-Implementierung in Assembler).

Virtuelle Methoden

Virtuelle Methoden sind im Grunde genommen Funktionszeiger und insofern überall dort sinnvoll, wo man in einem C-Programm zur Lösung des Problems einen Funktionszeiger eingesetzt hätte (oder in Assembler eine Einsprung-Adresse in Maschinencode hinterlegt oder übergeben hätte).

Werden Methoden in C++ unnötigerweise "virtual" deklariert (mit anderen Worten: steht die Einsprungadresse immer fest und hätte insofern auch der Compiler oder Linker sie bestimmen können), so ist der Overhead natürlich unnötig - aber auch leicht vermeidbar, indem man solche Methoden eben nicht als "virtual" deklariert.

Dort, wo sinnvoll, also wo virtuelle Methoden tatsächlich ein Problem lösen helfen, sind sie in der Regel eleganter und programmiertechnisch einfacher zu handhaben als explizite Funktionszeiger.

Interfaces (Abstrakte Basisklassen)

Abstrakte Basisklassen sind im Grunde genommen Bündel von Funktionszeiger und ebenso wie virtuelle Methoden genau dort sinnvoll, wo sie ein Problem lösen helfen. Hierzu ein Beispiel: Ein Embedded-System hat eine Verbindung zu einem Host-Rechner zwecks Datenaustausch. Es besteht entweder ein LAN-Zugang oder an der seriellen Schnittstelle hängt ein Modem. Möglicherweise kommt es im laufenden Betrieb auch zu einer Umkonfiguration oder die Hostverbindung fällt komplett aus, dann sollen die Daten in einem lokalen, nicht-flüchtigen Speicher abgelegt werden.

Damit die Applikation nicht alle Eventualitäten an zahlreichen Stellen des Programms verteilt berücksichtigen muss, abstrahiert man den Datenaustausch z.B.

  • Verbindung initiieren
  • Daten übergeben
  • Übernahme bestätigen
  • Verbindung abbauen

in einem Interface. Jede der genannten Operationen entspricht dabei einem Zeiger auf ein entsprechendes Unterprogramm (also dessen Einsprungadresse). Tatsächlich implementiert werden diese Unterprogramme mehrfach (einmal für LAN, einmal für Modem, einmal für lokalen Speicher) und der eigentlichen Applikation wird - mehr oder weniger transparent - einfach das passende Funktionszeigerbündel übergeben, je nachdem, welche Art von Host-Verbindung derzeit möglich ist.

Auch hier ist es wieder so, dass ein Interface - realisiert durch eine abstrakte Basisklasse - im Vergleich zu expliziten Funktionszeigern eleganter und programmiertechnisch einfacher ausfällt (sofern man einmal das Prinzip verstanden hat). Ebenso gilt, dass abstrakte Basisklassen zu unnötigem Overhead führen, falls man sie "nur mal so" einsetzt, also ohne dass sie, wie oben dargestellt, ein konkretes Problem lösen.

Exceptions (Ausnahmebehandlung)

Mit Exceptions lässt sich ein "alternativer Kontrollfluss" in einem Programm definieren, vorzugsweise zur Handhabung von Fehlersituationen oder anderer "außergewöhnlicher Ereignisse". Mit der Einführung von Exceptions musste in C++ aber auch die Leitlinie "You only pay for what you use" verletzt werden. Das heißt es entsteht stets etwas Overhead, wenn der C++-Compiler Exceptions unterstützt, auch für Programme, die Execptions überhaupt nicht benutzen. Aus diesem Grund lassen sich Execptions über Kommandozeilen-Argumente des Compilers oder in den Projekt-Optionen einer Tool-Chain häufig deaktivieren.

Laufzeit-Typinformation

Laufzeit-Typinformation (auch Runtime Type Information oder RTTI genannt), verursacht unter Umständen ebenfalls einen geringen Overhead in C++, auch wenn ein Programm RTTI nicht benutzt. Insofern gilt wie für Exceptions, dass man ggf. nach einer Möglichkeit suchen sollte, RTTI ganz abzuschalten, wenn man das Feature nicht braucht. Auf der anderen Seite ist der RTTI-Overhead im Vergleich zu Exceptions sehr gering und entsteht vor allem im Hinblick auf ein wenig zusätzlich benötigten Speicherplatz, nicht in Form unnötig ausgeführter Anweisungen. (Nach Abschalten der RTTI-Unterstützung darf man also kein "schnelleres" Programm erwarten.)

Templates (Schablonen)

Templates sind ein Mechanismus, der zwischen Implementierungs- und Kompilierungs-Zeitpunkt greift. Im Grunde genommen steht mit einer C++-Template "ein bisschen Programmcode" stellvertretend für "möglicherweise sehr viel Programmcode", der aber noch in einem bestimmten Aspekt - einem Datentyp oder eine Konstante - variieren kann. Der allgemeine Teil (Template) wird nur einmalig implementiert und später in mehr oder weniger vielen Varianten kompiliert. (Das am ehesten vergleichbare äquivalent zu C++-Templates sind in C die Präprozessor-Makros.)

Unter diesem Aspekt betrachtet sind Templates keine Verschwendung (wie manchmal behauptet) sondern sehr sparsam und ideal für Mikrocontroller, da sie im Vergleich zu einer Bibliotheksfunktionen, die quasi "auf Vorrat" viele Varianten berücksichtigen, von denen konkret aber nicht alle genutzt werden, einem Programm nur das hinzufügt, was wirklich benötigt wird.

Auf der anderen Seite sind Templates ein neueres Feature von C++ zu dem bis vor wenigen Jahren auf Seite der Compiler-Lieferanten kaum Erfahrung hinsichtlich der optimalen Umsetzung bestand. Auch in der Fachliteratur werden Templates meist mit viel geringerer Tiefe und weniger Praxisbezug behandelt als diejenigen Features, die schon sehr lange von C++ unterstützt werden (Vererbung usw.). Eine generelle Empfehlung, Templates bei der Verwendung von C++ im Bereich der Mikrocontroller-Programmierung nicht einzusetzen, ist daraus aber keinesfalls abzuleiten - ganz im Gegenteil: Zur Lösung vieler Probleme sind Templates besser geeignet als etwa Präprozessor-Makros oder gar Copy-und-Paste-Programmierung.

C vs C++ – Mikrocontroller.net (2024)

FAQs

Should I use C or C++ for STM32? ›

C++ can be bulkier, and has more demands on dynamic memory allocation. C++ hides complexity, C forces you to account for it and own it. C facilitates a lot of bad programmers, with poor habits, C++ forces a more structured and object orientated approach.

Which is better for embedded C or C++? ›

C's simplicity, small footprint, predictable performance, low-level capabilities, and wide platform support make it an ideal choice for embedded systems programming. While C++ has its advantages, the specific requirements of embedded applications make C a better fit for these types of applications.

Why is C used over C++ in embedded systems? ›

However, with the rise of modern programming practices, C++ has entered the arena, sparking a debate: Which is better for embedded systems, C or C++? The Case for C: Simplicity and Predictability: C offers a straightforward approach, making it easier to predict system behavior, especially in time-critical applications.

Which is better, C or C++? ›

C language seems to be more suitable for low-level programming applications, and it is also a foundational language for beginners, whereas C++ is an extension of C programming language with the OOP's concept, so it is more feasible for complex applications also it is faster, secure.

Can I program STM32 with C++? ›

You can leverage the STM32 toolchain to set up the project for you. You can then start to add your own application modules in C++. Underneath the hood, you can still use the STM32 C libraries to accelerate your software development.

Why should I use C instead of C++? ›

If you are interested in low-level programming tasks such as operating systems and device drivers, C may be your better choice. If you are more interested in developing higher-level applications such as web browsers and graphics software, C++ may be a better fit.

Why C is widely used in embedded systems? ›

Although not originally designed for embedded software development, the C language allows a range of programming styles from high-level application code down to direct low-level manipulation of hardware registers. As a result, C has become the most popular programming language for embedded systems today.

What is the major difference between C and embedded C? ›

The primary distinction between C and Embedded C is what each is used for. C language is a general-purpose language that is used for developing a wide range of software applications, while Embedded C is a variant of the C language that is specifically designed for developing embedded systems.

Is embedded C in demand? ›

High Demand

The need for experts proficient in embedded systems is steadily growing. The emergence of the IoT (Internet of Things) has sparked a surge in smart devices, heightening the requirement for developers specializing in embedded systems.

Which programming language is best for embedded systems? ›

Top 5 Best Embedded Systems Programming Languages
  • C. The most widely used programming language for embedded devices is C. ...
  • C++ C++ is a high-level programming language that is used in a wide range of applications, including embedded systems programming. ...
  • Assembly Language. ...
  • Python.

Why is C used for operating systems instead of C++? ›

C has a very small runtime. And the memory footprint for its code is smaller than for most other languages. When compared to C++, for example, a C-generated binary that goes to an embedded device is about half the size of a binary generated by similar C++ code. One of the main causes for that is exceptions support.

Why do people use C when C++ exists? ›

C is much easier to use in kernels, on bare metal, and in snippets of code that will be used inside the runtime of other languages. C++ compilers make a lot of assumptions that what is calling C++ code is in fact C++ code itself.

Is C C++ outdated? ›

Learning C and C++ remains relevant, and it's quite likely that their relevance persists in 2024.

What does C++ have that C does not? ›

C has no support for the OOPs concept. Thus, it does not support encapsulation, polymorphism, and inheritance. The C++ language supports encapsulation, inheritance, and polymorphism because it is an object-oriented programming language. C has no support for functions and operator overloading.

Which is the hardest C or C++? ›

C does not have so many rules and restrictins as C++ it is not so difficult to learn it because it is more direct to code. On other hand the C++ is more powefull to use. The conclusion is C is easier to learn, C++ is easier to use.

Which compiler is best for STM32? ›

While there are a variety of IDEs available that support the STM32, the most used are ST's STM32CubeIDE, IAR's EWARM and Keil's uVision (MDK).

What language does STM32 use? ›

STMicroelectronics' STM32 family of 32-bit ARM Cortex-M core-based microcontrollers is supported by a wide range of software integrated development environments (IDEs) with C, C++, Pascal and JAVA support and debuggers from STMicroelectronics and major 3rd-parties (free versions are available) that are complemented by ...

What is the best way to learn STM32? ›

Steps to Start with STM32
  1. Learn C Programming: Get comfortable with data types, functions, pointers, etc.
  2. Choose a Basic STM32 Microcontroller: Start with the STM32F0 or C0 series. ...
  3. Install STM32CubeIDE: This integrated development environment simplifies development.
Jun 7, 2024

What is needed to program STM32? ›

Prerequisites
  • An STM32 microcontroller board (e.g., one of the STM32 Discovery or Nucleo boards).
  • A computer with a development environment set up. You can use STM32CubeIDE or STM32CubeMX, both of which are free and powerful tools for STM32 development.
  • Basic knowledge of C programming.
Sep 27, 2023

Top Articles
Latest Posts
Article information

Author: Gregorio Kreiger

Last Updated:

Views: 6201

Rating: 4.7 / 5 (77 voted)

Reviews: 84% of readers found this page helpful

Author information

Name: Gregorio Kreiger

Birthday: 1994-12-18

Address: 89212 Tracey Ramp, Sunside, MT 08453-0951

Phone: +9014805370218

Job: Customer Designer

Hobby: Mountain biking, Orienteering, Hiking, Sewing, Backpacking, Mushroom hunting, Backpacking

Introduction: My name is Gregorio Kreiger, I am a tender, brainy, enthusiastic, combative, agreeable, gentle, gentle person who loves writing and wants to share my knowledge and understanding with you.