Konstanter/n Aufenthalt
« | 16 Nov 2018 | »Bei Protokollen ist man schnell versucht einen konstanten String
“mal einfach so” als Argument zu schreiben.
do_something("Hello", "World");
Der Compiler macht das schon! Er reserviert sich im Datenbereich zwei Blöcke und generiert den Funktionsaufruf mit zwei Pointer auf die Blöcke.
Wer Eindeutigkeit und Übersichtlichkeit bevorzugt, investiert ein paar Zeilen mehr:
… doch dann kam die Portierung auf Mikrocontroller …
Der böse Begriff hier lautet “Datenbereich”. Wo genau ist das?
Die Standards von C und C++ überlassen das den Compiler Herstellern und das ist gut so. Schließlich will man sich hier keine Ketten anlegen.
Was passiert in der Praxis? Gehen wir zurück zu DOS bzw. dem guten alten 8086 Prozessor. Der kannte 3 Segment-Register
- CS: Code-Segment, dort wo Programmcode abgelegt ist
- DS: Daten-Segment, dort wo globale Variablen liegen und der Heap aufgebaut wird.
- SS: Stack-Segment, dort wo Funktion-lokale Variablen und die Rücksprungadressen der Funktionsaufrufe liegen.
- … und noch ein bisschen mehr, aber hier uninteressant
Heutzutage nutzen unsere PCs und Server (zum Glück) das Flat-Memory-Model, wo alles in einem virtuellen Adressraum liegt … so wie es Meister Von-Neumann haben wollte. Trotzdem werden die 3 oben genannten Bereiche weiter unterschiedlich verwaltet.
Mikrocontroller sind jedoch oft nach der Harvard Architektur gebaut und legen Code in einem Speicher, Daten und Stack in einem anderen Speicher ab.
Wenn ich von meinen geschätzten Atmel MCUs ausgehe, so liegt der Code immer in einem größeren Flashspeicher, während die Daten und der Stack auf einem recht kleinen SRAM Bröckchen angesiedelt sind.
Beim Arduino UNO ATmega328p können wir 32 KByte Code einbrennen, aber nur 2 KByte Daten während der Ausführung nutzen. Beim ATtiny85 sind es 8 KByte Code und 512 Bytes RAM … nicht gerade viel.
Zurück zum Problem: Wo landen unsere String-Konstanten? Im 2 KByte Datenspeicher und lassen dort weniger Luft für die Anwendung.
Lösung:
Zum Glück haben die Erfinder der Platform ein paar Erweiterungen
vorgenommen, damit man Konstanten auch in den Codebereich verschieben kann.
PROGMEM
lautet das Zauberwort und schon wandern Texte dorthin, wo mehr Platz ist.
Fazit
Wer also Bibliotheken schreibt, die Datenaustausch-Protokolle implementieren, ist gut beraten sich für seine Konstanten eine Kapselung zu überlegen. Denn so erreichen wir das alte hoch heilige Ziel der Sprache C: Nämlich Portierbarkeit zwischen allen Plattformen.
Hier noch ein sehr einfaches Arduino Beispiel:
Sketch uses 1470 bytes (4%) of program storage space. Maximum is 32256 bytes.
Global variables use 196 bytes (9%) of dynamic memory, leaving 1852 bytes for local variables. Maximum is 2048 bytes.
Sketch uses 1470 bytes (4%) of program storage space. Maximum is 32256 bytes. Global variables use 184 bytes (8%) of dynamic memory, leaving 1864 bytes for local variables. Maximum is 2048 bytes.
Yay! 12 Bytes mehr!
Und jetzt stellen wir uns einfach mal vor, dass wir statt “Hello World” über
1 KByte an Texten im Programm haben! Tja, Kleinvieh macht eben auch Mist.