Suchen

C++ in der Embedded-Entwicklung: Umgang mit Heap-Daten

Autor / Redakteur: Dr. Hartmut Schorrig * / Sebastian Gerstl

Der zweite Artikel dieser Serie zu C++ in der Embedded-Entwicklung geht auf das Thema der Datenhaltung und Dynamische Daten zur Runtime ein. Es wird dabei vorausgesetzt, dass die Nutzung von C++ bereits angekommen ist. Jedoch wird auf Unterschiede in der Anwendung hingewiesen. Die Aussagen gelten häufig gleichartig für C und C++ – nicht die Sprachversion macht den Unterschied.

Firmen zum Thema

Anders als in einem sortierten, geordneten Stack wie links dargestellt liegen in einem Heap (rechts) Daten in einem unsortierten, ungeordneten Zustand vor. Entsprechend herausfordernd kann sich die Embedded-Programmierung mit Heap-Daten gestalten.
Anders als in einem sortierten, geordneten Stack wie links dargestellt liegen in einem Heap (rechts) Daten in einem unsortierten, ungeordneten Zustand vor. Entsprechend herausfordernd kann sich die Embedded-Programmierung mit Heap-Daten gestalten.
(Bild: Clipdealer)

Was bei PC-Applikationen „Gang und Gäbe“ oder neudeutsch „state of the art“ ist, wird im Embedded Bereich anders gesehen. Eine Umfrage mit drei Antwortmöglichkeiten

  • Auch im Embedded Bereich sollte new und delete verwendet werden, ...C++-Libraries nutzen, Defragmentierung ist nicht relevant
  • Dynamischer Speicher zur Runtime sollte nur für Speziallösungen verwendet werden, zu Startup allokieren ist aber ok
  • Man sollte im Embedded-Bereich nur mit statischen Daten hantieren, das ist ausreichend, man weiß genau wo die Daten liegen.

ergab ein eindeutiges Ergebnis für den mittleren Punkt. Die Umfrage wurde in einer Xing-Gruppe ausgeführt mit Mitgliedern aus dem Embedded Bereich, die dem Autor nicht in jedem Fall persönlich bekannt waren, also mit Streubreite. Sie muss zwar nicht als repräsentativ angesehen werden, spiegelt aber ein wohl bekanntes Bild wieder. Einleitung zu der betreffenden Umfrage war dieser Artikel [2].

Warum ist das so, worin liegen die Probleme der dynamischen Allokation?

Die beiden entscheidenden Unterschied zwischen PC-Applikationen und Embedded sind:

  • Verfügbarer Speicherplatz: Es stehen hier Größenordnungen der üblichen 4-16 GByte gegenüber einer Zahl von ca. 160 kByte RAM in einer Embedded Control Lösung beispielsweise mit einem Prozessor TMS320F28379D, der immerhin mit zwei Cores und zwei weiteren Spezial-Cores „Control Law Accelerator“ eine hohe Rechenleistung für schnelle Regelung bis in den kleinen Mikrosekundenbereich kombiniert mit komplexen Datenauswertungen im Target mit einem Chip 2 x 2 cm ohne Kühlkörper ermöglicht. Es gibt auch kleinere Prozessoren.
  • Embedded Control Lösungen müssen oft lange (Monate, Jahre) ununterbrochen bedienfrei laufen, Applikationen auf dem PC dagegen werden regelmäßig beendet, bei Problemen sitzt ein Bediener davor der einen Neustart der Applikation erzwingen kann.

Es gibt dazwischen einen Bereich, der ebenfalls dem Embedded Control zugeordnet wird, etwa mit Industrie-PC-Lösungen oder einem Raspberry-Pi als Kommunikations- und Datenauswerter. Häufig gilt dort zwar ebenfalls das Paradigma „bedienfrei“. Aber es gibt ein Betriebssystem, das Prozesse terminieren kann und die Prozesse laufen begrenzte Zeit jeweils nach Anforderung. Für diese Embedded Control Anwendungen gelten die folgenden Ausführungen nur bedingt.

Welche Probleme werden von der Dynamischen Allocation zur Runtime verursacht, die offensichtlich bei PC-Applikationen weniger stören oder nicht vorkommen?

Als erstes wird häufig die mögliche Speicherfragmentierung genannt. Diese Gefahr besteht abhängig von der Art von Allocations real oder auch nicht. Das Problem wird provoziert wenn zu beliebigen Zeitpunkten Speicher allokiert wird, der aber für verschieden lange Zeitbereiche stehenbleibt. Die Lücken der wieder freigegebenen erneut allokierbaren Speicherbereiche sind dann verteilt im Gesamtspeicher. Möglicherweise findet sich nach einiger Zeit kein genügend großer zusammenhängender Speicherbereich für eine notwendige Allocation, obwohl die Gesamtbilanz der Speichergröße ausreichend bemessen ist. Es gibt kein Problem der Fragmentierung, wenn alle Allocations nach kürzerer Zeit auch wieder freigegeben werden, also etwa die gleiche Bestandszeit haben. Daher funktionieren allokierende Systeme scheinbar gut auch in Embedded Control, bis man Funktionalität erweitert und sich dem Problem nicht bewusst ist.

Das zweite Problem liegt darin, dass der Speicherbereich nicht ausreichen könnte für aktuelle Anforderungen. Damit muss es einen Entscheider geben, der wichtigen Prozessen den Vorrang gibt. Am PC ist dies der Bediener mit einem Blick auf alle laufenden und unnötig laufenden Programme.

Das dritte Problem sind unentdeckte Speicherfresser. Für Applikationen, die nach einer gewissen Zeit wieder beendet werden, spielt das fast keine Rolle, wenn genügend Speicher vorhanden ist. Denn: Die Prozessverwaltung des Betriebssystems gibt den Gesamtspeicher des Prozesses unabhängig von der internen Programmierung jedenfalls frei. Unentdeckte Speicherfresser sind zwar „Fehler der Programmierung“, liegen oft aber auch an der Natur der Sache. Es ist eben nicht so, wie beispielsweise dieses Doukument [5] uns weismachen will, die Verantwortung für ein allokiertes Objekt läge nur beim Allokierer, einer Instanz im Stack, dessen Destruktor die Freigabe ausführt. Dies trifft zwar für bestimmte Anwendungen zu, ist aber eben nicht verallgemeinerbar. Da die Frage, wann ein Objekt gelöscht werden kann, häufig nicht einfach beantwortbar ist, bleibt es eben stehen.

Es gibt ein viertes Problem, häufig verschwiegen aber real existierend: Mögliche Fehler wenn dynamischer Speicher bereits gelöscht wird, obwohl er noch referenziert wird. Die Gefahr von Softwarefehlern wird größer, wenn dynamischer Speicher im Spiel ist. Am PC gibt es im Ernstfall eine Exception entweder aufgefangen oder mit Dialogbox.

Diese Fragen sind insgesamt etwa im Java-Bereich mit der Einführung des Garbage-Collector-Prinzip gelöst. Der Garbage-Collector in Java kann allerdings auch nicht Speicherfresser verhindern, wenn eigentlich nicht mehr benötigter Speicherplatz weiterhin referenziert bleibt. Alle anderen Probleme einschließlich der Defragmentierung sind behoben. Der Einsatz für oder gegen Java ist allerdings nicht Thema dieses Artikels.

Ohne Dynamische Allokation zur Runtime sind einige C++-Libraries nicht nutzbar?

Dies ist die Konsequenz. Häufig arbeiten Containerklassen in C++ mit dynamischen Speicher, da somit die Anwendungsprogrammierung viel einfacher und übersichtlicher ist. Man kann entsprechende Libraries einsetzen, wenn gesichert ist, dass die jeweiligen Teilanwendungen eine tatsächlich begrenzte Zeit laufen, hier eben das Smart-Pointer-Prinzip verwendet wird, wobei man dabei nicht unbedingt die neueste C++-Version braucht. Diese wird häufig (noch) nicht für Embedded C++ bereitgestellt. Man kann das Smart-Pointer-Prinzip auch durch manuell sorgfältige Programmierung ohne hohen Aufwand realisieren. Smart Pointer sind u.a. hier beschrieben [5]. Wenn dann die Teilanwendungen mit einem spezifischen Heap-Bereich arbeiten, gibt es auch keine Kollosionen mit anderen Teilanwendungen, Thema Fragmentierung des Speichers. Dies ist eine individuelle Entscheidung der Programmierung.

In allgemeinen schnell und / oder lang laufenden Programmteilen wird man auf solche Libraries eher verzichten oder die jeweiligen Container nur im Startup als „festes Mengengerüst“, möglicherweise anhängig von Geräteparametrierungen, anlegen.

Allokation zur Startup-Time

Dieses Paradigma steht in der Opposition zur Nutzung statischer Daten. Letzteres ist klassisch in C durchaus praxisüblich. Daher ist es interessant, dass in der obigen Umfrage niemand diese Option gewählt hat.

Statisch angelegte Datenbereiche sind gut im Mapfile dokumentiert. Man weiß wo die Daten stehen. Viele Monitoring-Tools greifen über die vom Mapfile bekannten Adressen auf die Daten zu. Diese Tools sind weitgehend unbrauchbar wenn mit allokierten Daten zur Runtime gearbeitet wird.

Allerdings ist ein Datenzugriff von außen dann möglich, wenn es statisch root_data Zeiger gibt. Diese sollten vorhanden sein, für Debug-Aspekte, auch aus generellen Überlegungen. Man braucht eigentlich nur einen statischen Zeiger, die das einzige allokierte root-Data-Objekt referenziert, von dem alle anderen Referenzen ausgehen. Man kann für verschiedene Teilapplikationen auch jeweils die eigenen root_dataxy Zeiger haben.

Statische Instanzen sind mischbar mit allokierten Instanzen. Entsprechende Speicherbereiche sind im Embedded Bereich meist sowieso in entsprechenden Link-Cmd-Files zu behandeln. Sprich, für die allokierten Daten muss ein genügend großer Bereich reserviert werden, dessen Größe dann für bestimmte Ausführungen der Gesamtapplikation verifiziert wird.

Mit Allokation zur Startup-Time kann man über Geräteparameter verschiedene Konfigurationen laufen lassen. Bestimmte Konfigurationen die zu hohe Speicheranforderungen benötigen, können per Beschreibung oder Vertrag (Zusicherung) ausgeschlossen werden. Dies lässt sich einfach verifizieren. Dagegen würde eine statische Speicherverteilung bei verschiedenen Konfigurationen jeweils eine Programmänderung erzwingen. Man denke an Geräte, die eigentlich die voll umfängliche Software enthalten, aber per „Freischaltung“ je nachdem was der Kunde haben oder zahlen will, parametriert werden.

Keine Instanziierung in unterlagerten Modulen

Die Zusammenfassung von Einzeldaten entweder in struct-Definitionen für C oder in C++ classes sollte vorausgesetzt werden. Diese Datenobjekte lassen sich sowohl statisch als auch allokiert instanziieren.

Unterlagerte Module wie etwa ein PID-Regler oder auch eine komplexen Datenauswertung sollten immer mit Referenzen arbeiten. Die Daten werden diesen Modulen referenziert übergeben. Für die Anlage sorgt das Rahmenprogramm. Dort wird entschieden ob allokiert oder statisch definiert wird. Im Rahmenprogramm wird damit auch über den Typ der Instanz entschieden, betrifft das Thema Nutzung der Ableitung (derived classes) in C++. Das ist kein Widerspruch, das Submodul soll möglicherweise insbesondere damit zurechtkommen.

Unerwünscht oder nicht empfehlenswert ist also

  • die Verteilung von statischen Einzeldaten in C-Files, klassischer C-Style;
  • die statische Definition auch von struct-Daten in den Modulen der Programmierung. Das verhindert mehrfache Nutzung der Module oder Spezialnutzungen beispielsweise für den Modultest. Das Wort „statisch“ bezieht sich hier auf die Speichernutzung, nicht auf die Sichtbarkeit. Die Daten sollen also in Modulen mit oder ohne „static“ nicht definiert sein;
  • die Allokation von Daten in den Modulen der Programmierung. Damit würde sonst die Entscheidung über Nutzung der Allokation bereits feststehen; und
  • Vom letzten Punkt ausgenommen sind Spezialfälle. Die anderen Punkte sollten immer beachtet werden, dies ist auch einfach realisierbar.

Den Modulen müssen folglich alle Instanzdaten von außen referenziert übergeben werden. Das Thema „Rechenzeit bei Nutzung von Referenzen vs. direkter Adressierung“ ist in Teil 1 dieser Serie [1] behandelt worden, es sollte kein Ausschlussfaktor sein.

Man kann ein Factory-Pattern anbieten, das im Modul allokiert. Die Entscheidung ob diese Factory außen gerufen wird oder eine statische Instanziierung erfolgt, ist damit frei. Wichtig ist dass Allokationen nicht zwangsläufig im Modul ausgeführt werden.

Damit ist die Frage der Instanziierung immer außen im Rahmen der Applikation zu beantworten. Dies sei ein wichtiges Grundprinzip moderner Programmierung in C oder C++ für Embedded Control.

Initialisierung der Instanzen außerhalb oder im Modul?

Mit den Ausführungen des obigen Abschnitts wird allerdings das Prinzip RAII = „Resource Acquisition Is Initialization“ berührt, wie es für die Zusammenfassung von new und Constructor in C++ realisiert ist, aber auch für die statische Instanziierung von class-Objekten gilt:

MyClass* refdata = new MyClass ( initializationArguments ); MyClass staticdata( initializationArguments );

In beiden Fallen wird der Constructor gerufen, bei der statischen Initialisierung sogar innerhalb des Startup vor Erreichen der main()-Routine. Wenn die Initialisierungsdaten wesentliche Bedeutung für die Modul-Funktionalität haben, etwa vorher zusammengestellt werden, dann wird hiermit dieser Teil der Zuständigkeit aus dem Modul entfernt und in den Aufrufrahmen verlagert. Das ist nicht wünschenswert.

Es gibt ein weiteres Argument gegen die strenge Anwendung des RAII-Paradigmas: In Tools der Grafischen Programmierung werden Instanzen (Daten der Funktionsblöcke) häufig zunächst unabhängig von deren Verdrahtung des Grafischen Modells angelegt. Die Verdrahtung kann aber initial maßgebende Dinge enthalten, also nur beim Startup auszuführen sein. Das können sowohl im Modell berechnete Konstantwerte sein, als auch Aggregationen wenn man an UML-Class-Diagramme denkt. Insbesondere bei den Aggregationen gibt es ein Problem das systematisch im RAII-Prinzip ungelöst ist, die gegenseitige Aggregation. Man kann diese nicht mit akademischer Forderung nach baumartigem Datenaufbau wegdiskutieren. Eine Aggregation sollte eigentlich mit const in C++ realisierbar sein, die damit im Constructor festgelegt werden muss. Das geht aber nicht bei gegenseitigen Aggregationen, mindestens für die eine Seite. Mir ist immer schon im Kollegenkreis das Wort „Nachinitialisierung“ aufgefallen, mit dem das Problem gelöst wird.

Konsequent heißt dies:

  • Beim Constructor werden nur die Daten übergeben, die nicht funktionsgebend sondern instanzkennzeichnend sind. Das kann ein Identifikator sein, numerisch oder als String, eine Typ-Beschreibung und dergleichen. Für diese Dinge steht die Instanziierungsumgebung, also das Rahmenprogramm.
  • Aggregationen oder konstante Parameter können leider nicht als const gekennzeichnet werden. Sie dürfen aber als private geschützt und nur mit einer get-Operation zugänglich sein. Damit ist ebenfalls compilerseitig der Schreibschutz gewährleistet.
  • Es gibt immer eine Nachinitialisierungsroutine pro Klasse oder Modul, die man praktisch init...(...) nennen könnte oder sollte. Diese Routine wird im Modul ausgeführt und in der Startup-Phase eben im dessen init...(...) gerufen. Mit der init-Routine werden alle Aggregationen und Startparameter übergeben. bzw. im Modul verknüpft. Die zu aggregierenden Instanzen sind komplett vorhanden, da die Instanziierung zeitlich davor bereits erfolgt ist.
  • Man muss beachten, dass bestimmte Parameter erst von anderen init-Routinen berechnet und bereitgestellt werden. Das können auch Daten in einer aggregierten Instanz sein, also referenziert. In der Grafischen Programmierung mit Funktionsblöcken ist gut sichtbar wenn Verbindungen mit der Markierung „init“ zwischen den FBlocks bestehen. Man kann diese in Simulink beispielsweise einer bestimmten Abtastzeit zuordnen, die dann im Target nur als Initialisierungsschleife gerufen wird. Nutzbar ist dies bei Simulink mit den sogenannten S-Functions. Wenn man die Aufrufreihenfolge der init-Routinen frei halten möchte, hilft eine Kennzeichnung, welche Instanz bereits fertig initialisiert ist. Deren Daten werden also erst dann übernommen. Es erfolgt ein Aufruf aller init-Routinen in einer Schleife, bis alle „fertig“ melden. Diese Schleife läuft allerdings auch dauerhaft, wenn es eine ungünstige Konstellation der gegenseitigen Abhängigkeit gibt, die faktisch ein Programmfehler ist. Das kann einfach erkannt werden mit einem Schleifenzähler.

Damit wird das RAII „Resource Acquisition Is Initialization“-Paradigma etwas aufgeweicht, aber mit einer weiteren Systematik ergänzt. Diese Systematik ist nun passfähig für die Anforderungen der Embedded Programmierung nicht nur im Hinblick auf die Allocationfrage, sondern auch mit Blick auf Grafische Programmierung.

const-Daten im Flash-Speicher

Es gibt eine weitere Besonderheit in der Embedded Programmierung: const Daten auf dem Flash, das Gegenteil von dynamischen Daten: Wogegen auf dem PC const Daten nicht besonders unterschieden werden von nicht const-Daten, das const hat nur Schreibschutzfunktion auf Compilerebene, gibt es für Embedded die const-Daten wirklich. Am PC kann man const Daten berechnen und als C++-class-Instanz definieren. Man übergibt dem Constructor berechnete Werte aus Programmteilen davor. Die Definition der C++-const-Daten erfolgt im Constructor, also zum Startup für einmal anfänglich angelegte const-Daten. Die Daten auf dem Flash müssen aber vom Compiler bestimmbar sein, vor dem Flashen. Da helfen nur die altbekannten C struct, die mit der { Initializerlist ,...} definiert werden. Dabei könnten Makros vom Autor jeweils als INIZ_...() bezeichnet helfen, wenn es eine gewisse Verschachtelung gibt. Die Anwendung wird dann entlastet von Klammern- und Kommas zählen. Man kann die entsprechenden Sources auch mit C++ compilieren, könnte aber zielsystemspezifisch doch auf C ausweichen müssen. Die Mischbarkeit stellt kein Problem dar. Die Deklaration von Instanzen muss mit

extern_C Type const myConstData;

erfolgen. extern_C ist extern "C" für C++, siehe auch [1].

Braucht es dynamischen Speicher zur Runtime für Embedded?

In den Vorkapiteln wurde dargestellt, dass für die Instanziierung der maßgeblichen Arbeitsdaten zur Runtime kein dynamischer Speicher mehr genutzt wird, sondern dies beim Startup erfolgt. Die Bezeichnung „dynamischer Speicher“ ist dann falsch, wohl aber ist es allokierter Speicher im Heap.

An zwei Beispielen soll nun gezeigt werden, dass vermeintlich notwendige dynamische Daten mit etwas Überlegung auch spezifisch gestaltet oder vermieden werden können: Strings und Events.

Strings für Logmeldungen

Strings treten in geschlossenen numerisch orientierten Embedded Applikationen etwa für Log-Meldungen auf. Man kann einen Speicherbereich dafür statisch vorsehen, wobei sich hier das Wort statisch auf „feststehend“ bezieht, möglicherweise zur Startup-Zeit allokiert, nicht unbedingt statisch im RAM. Kennzeichnend für solche Strings ist, dass sie nur kurzlebig sind. Die Meldung wird zusammengestellt, dann auf kurzem Weg einem Log-System übergeben. Das kann ein Sender einer angeschlossenen Kommunikation sein (Netzwerk), ein Laufwerk oder dergleichen. Man muss damit rechnen, dass zeitgleich viele Log-Meldungen auftreten eben wegen eines Problems, es darf dann nicht der Speicher ausgehen. Aber man kann die Gesamtmenge des Speichers reduzieren wollen, indem man den Speicher für die kurzzeitige Logmeldungsbearbeitung dynamisch verteilt. Es sind auch Programmkonstrukte denkbar, die die Zusammenstellung der Logmeldung verschieben bis der Speicher wieder verfügbar ist und den Fehlerzustand dafür in einfachen numerischen Datenzellen, die statisch vorhanden sind, speichert. Man denke an eine Fehlernummer, die im Logeintrag zum String wird.

Damit ist für diese Anwendungsklasse ein spezieller dynamischer Speicherbereich (Heap) zweckmäßig, nicht zu groß, bei Ausgehen des Heap-Speichers gibt es passende Behandlungen. Sind alle Logs geschrieben, dann ist der Heap wieder vollkommen frei. Es gibt insoweit nicht das Fragmentierungsproblem, da das Freigeben des Speichers genau von einem Prozess vorgenommen wird. Wenn dieser hängt, dann funktioniert das Logsystem nicht mehr, womit man auch keine Diagnose vom Gerät bekommt. Man muss also bei bedienfreiem Betrieb sich darauf konzentrieren, dass mindestens genau dieses immer funktioniert, oder muss den Watchdog-Reset beauftragen.

Speicherplatz für Eventdaten

Man meint, Events wie sie etwa für Statemachines verwendet werden oder eigentlich auch Bestandteil einer guten Objektorientierten Programmierung sein könnten (Datenaustausch zwischen den Objekten über Events), seien unerwartet auftretende Dinge, eben Events. Demgemäß bräuchten diese beim Auftreten dynamischen Speicher für die zugehörigen Daten. Systeme, die etwa Codegenerierung für Eventverarbeitung beinhalten, arbeiten daher gern mit dynamischen Speicher.

Das ist eine Fehlinterpretation. Die Anzahl der Event-Instanzen ist sehr wohl definiert und nicht allzu groß. Wenn eine Statemaschine in einem Ablauf nur ein Event eines bestimmten Typs verarbeiten kann, das Vorliegen mehrerer gleicher Events eher zu unklaren Situationen führt, dann braucht es für dieses Event von A nach B vom Typ T genau eine Instanz. Diese kann statisch vorhanden sein. Siehe auch den Beitrag „EventQueue Lösungen für Embedded Control“, vom Verfasser dieses Artikels. [3]

Ob die betreffende Event-Instanz belegt ist (in der Queue, in der Verarbeitung) ist in der Instanz selbst gekennzeichnet. Der Emitter des Events kann entscheiden, ob das Event in der Queue nur mit neuen Daten versorgt wird, ob es aus der Queue entfernt und erneut eingeschrieben wird oder ob jetzt kein neuen Event erzeugt wird, weil das alte noch nicht verarbeitet ist. Das kann als Ergebnis der Abfrage auch zu bestimmten Stateabläufen beim Emitter führen, selbst soweit, dass ein Messageempfänger über Kommunikation einem bestimmten Partner mitteilt „Die Message kann nicht verarbeitet werden“.

Bei dynamischer Allokation von Events kann es leicht zum gegenteiligen Fall kommen: Die Verarbeitung hängt aufgrund einer Fehlersituation, der Emitter allokiert immer neue Events, die Queue läuft voll, der Heap leer und die Situation ist nicht mehr debugbar, Ressourcen sind aus.

Standardlösungen für Embedded fehlen

Beide Beispiele zeigen, dass gängige PC-passfähigen C++ Libraries und Paradigmen im Embedded Bereich weniger gut angewendet werden können. Ein Embedded Programmierer bekommt passfähige Lösungen aus sich heraus schon hin. Die entstehenden immer leicht unterschiedlichen Lösungsansätze sind aber schlecht austauschbar.

Der Verfasser versucht mit der eigenen „embedded multiplattform C/++“ (emC) diese Lücke zu füllen, aus der eigenen Erfahrung [2], [3] und [4], jedoch ist dies freilich bislang auch nur Stückwerk. So ist die nicht allokierende Eventverarbeitung dort zwar (seit Februar 2020) programmiert, aber lange nicht etabliert.

Ein Problem kann sein, dass die Existenz der vielen scheinbar ausgereiften C++-Lösungen, die aber doch nur eher PC-tauglich sind, die Sicht versperren. Es bleibt also etwas zu tun für die Zukunft.

Literaturverzeichnis

[1] C++ in der Embedded-Entwicklung, Teil 1: Embedded-Code am PC testen“

[2] „Nutzung von dynamischen Speicher zur Runtime für Embedded Control“, vom Verfasser erstellt.

[3] „EventQueue Lösungen für Embedded Control“, vom Verfasser

[4] Website emC des Verfassers

[5] Online-Dokumentation Smart Pointer in C++ Microsoft-Doku.

* Hartmut Schorrig war in den vergangenen zwei Jahrzehnten Entwicklungsingenieur bei Siemens. In den Jahren zuvor wurden in verschiedenen Forschungsinstituten und in der Wirtschaft Erfahrungen gesammelt, anfänglich in den 80-ger Jahren mit der Entwicklung eines Industrie-PCs „MC80“, damals selbstverständlich noch in Assembler. Schon mit dem Studium „Technische Kybernetik und Automatisierungstechnik“ an der TH Ilmenau wurde der Blick auf den Zusammenhang von Elektronik, Regelungstechnik und Software gerichtet. Aktuell betreibt er die IT-Plattform vishia.org.

(ID:46752739)