Go Down

Topic: Alternative zu Strings (Read 2463 times) previous topic - next topic

LeRoi

Da ich jetzt mehrfach schon gelesen habe, dass Strings nicht zu empfehlen sind insbesondere wenn man nur 2kB RAM hat, würde ich gern wissen, was man als Alternative nehmen könnte.
Wenn ich zum Beispiel den readString, der alle kommenten Chars aufnimmt und einen writeString, den ich aus Strings zusammenbaue, ersetzen möchte ohne den Heap zu fragmentieren, was wäre zu empfehlen? Kann ich einfach einen char readBuffer[feste bytes Anzahl] und einen char writeBuffer[feste byte Anzahl] global anlegen, den ich dann einfach fülle und auslese? Würden da die Daten immer an einem festen Speicherbereich bleiben? Ich möchte auf jeden Fall die Strings nicht in den Flash legen mt PROGMEM.

Serenifly

#1
May 16, 2016, 06:54 pm Last Edit: May 16, 2016, 06:54 pm by Serenifly
In C sind Strings Null-terminierte char Arrays. Die kann man aber z.B. nicht direkt konkatenieren, da Arrays wiederum nur Zeiger auf das erste Element sind. Es gibt für den Umgang damit aber zig Jahrzehnte-alte Standardfunktionen fürs Zusammenfügen, Splitten, Suchen oder Konvertieren (nach Zahlen und zurück).

LeRoi

In C sind Strings Null-terminierte char Arrays. Die kann man aber z.B. nicht direkt konkatenieren, da Arrays wiederum nur Zeiger auf das erste Element sind. Es gibt für den Umgang damit aber zig Jahrzehnte-alte Standardfunktionen fürs Zusammenfügen, Splitten, Suchen oder Konvertieren (nach Zahlen und zurück).
Das weiß ich, habe schon einiges mit C Strings gemacht bin aber durch Java etwas gemütlich geworden (da nutze ich aber StringBuilder zum zusammenfügen). Deshalb war mir die String Klasse von Arduino willkommen. Jetzt werd ich wohl aber wieder mit den C String Befehlen arbeiten, die ich ja auch schon teilweise verwendet habe zum Zerlegen oder Umwandeln. Meine Frage war ja nur, ob dieser Array-Speicherbereich dann sauber immer wieder verwendet wird, wenn ich die chars da durch die C String Befehle reinpacke (die machen ja alles byte-weise). Ich werd mal alles mit einem Char Buffer umbauen.

jurs

#3
May 16, 2016, 07:38 pm Last Edit: May 16, 2016, 07:46 pm by jurs
Das weiß ich, habe schon einiges mit C Strings gemacht bin aber durch Java etwas gemütlich geworden (da nutze ich aber StringBuilder zum zusammenfügen).
Und Java nutzt Du auf Geräten von mindestens dem Typ Smartphone mit 256 MB RAM oder mehr, also mindestens ca. ca. hundertachtundzwanzigtausend mal so viel RAM wie ein UNO hat. Gar nicht zu reden von PCs mit Gigabytes an RAM, also x-millionenfachem an RAM wie ein UNO board.

Deshalb war mir die String Klasse von Arduino willkommen. Jetzt werd ich wohl aber wieder mit den C String Befehlen arbeiten, die ich ja auch schon teilweise verwendet habe zum Zerlegen oder Umwandeln. Meine Frage war ja nur, ob dieser Array-Speicherbereich dann sauber immer wieder verwendet wird, wenn ich die chars da durch die C String Befehle reinpacke (die machen ja alles byte-weise). Ich werd mal alles mit einem Char Buffer umbauen.
Erstmal musst Du Dich von dem Gedanken frei machen "alle ankommenden Zeichen in eine Variable" packen zu wollen. Bei extrem wenig RAM brauchst Du ganz andere Programmiertechniken.

Mit C-Strings definierst Du Dir Deine Zeichenkettenpuffer üblicherweise einmalig und global in der maximal benötigten Größe (bei einem GPS-Programm, das Zeichen per NMEA-Datenzeilen empängt oder einem seriellen Kommandointerpreter beispielsweise  für einen String mit maximal 80 Zeichen (eine Zeile) und dieser eine Zeichenpuffer kann in Programm immer wiederverwendet werden. Allenfalls legst Du kleine Zeichenkettenvariablen noch dynamisch auf dem Stack an, indem Du sie als lokale Variable innerhalb einer Funktion anlegst, z.B.
Code: [Select]

 void printFormattedTime (int hour, int minute, int second)
 {
  char timeStr[9];
  snprintf(timeStr, sizeof(timeStr), "%02d:%02d:%02d", hour,minute,second)
  Serial.println(timeStr);
 } // hier ist zur Laufzeit die Funktion und damit die Gültigkeit der Variablen timeStr beendet, und die auf dem Strack zu Funktionsbeginn reservierten 9 Bytes werden vollständig und ohne irgendwelche Fragmentierung wieder dem Stack zugeschlagen, bis zum nächsten Funktionsaufruf einer Funktion.


Wenn Du extrem wenig RAM hast, darfst Du nicht "alles", sondern nur das "absolut notwendige" gleichzeitig im RAM halten. So gesehen ist schon mein Funktionsbeispiel völlig überzogen im RAM-Verbrau ch, weil man dasselbe mit etwas anderen Befehlen auch ohne diesen 9-Zeichen-Ausgabepuffer erreichen könnte. Wobei allerdings meistens gilt: Um mehr RAM zu sparen, mußt Du auch mehr zusätzlichen Programmcode schreiben, d.h. die Größe des C-Codes in der INO-Datei wird unter ungünstigenUmständen größer als bei Anwendung von Programmiertechniken, mit denen RAM verschwendet wird.

michael_x

Quote
Meine Frage war ja nur, ob dieser Array-Speicherbereich dann sauber immer wieder verwendet wird
Das liegt ganz an dir.
Im Gegensatz zu Java gibt es in C/C++ erstmal kein automatisches Speichermanagement oder Müllabfuhr (garbage collection). Wenn du new verwendest, musst du auch delete machen, oder du machst new einmal nach Reset und weisst, was du tust. Oder du gönnst dir den Luxus von String-Objekten, die das -inzwischen einigermaßen ordentlich- selbst erledigen. (Kriegst du aber hier im Forum immer "Tu's Nicht, Strings sind Böse" - Hinweise ;)

Die "Arduino-Sprache" war schon immer so gedacht, dass sie Java ähnlich sieht, um es dadurch Nicht-Programmierern etwas einfacher zu machen. Daher gab es schon immer String Objekte (anfangs wirklich schrottig). Kostet natürlich etwas an Performance und Speicher.
Einfaches noch einfacher erscheinen zu lassen birgt leider auch das Risiko, dass es umso komplizierter wird, wenn dies an seine Grenzen stößt.

Und - für mich - besteht der Reiz ja grade darin, hübsche Sachen mit wenig Ressourcen zu machen.
Back to the Roots

Puffer mit variablem Inhalt liegen übrigens nie im Flash, da brauchst du dir keine Sorgen zu machen.
Feste Texte sollten zur Speicherplatz-Optimierung jedoch bevorzugt aus dem Flash gelesen werden.

Serenifly

#5
May 16, 2016, 07:52 pm Last Edit: May 16, 2016, 07:55 pm by Serenifly
Und Java nutzt Du auf Geräten von mindestens dem Typ Smartphone mit 256 MB RAM oder mehr, also mindestens ca. ca. hundertachtundzwanzigtausend mal so viel RAM wie ein UNO hat.
Das geht auch mit weit weniger. Es gibt Java VMs für ARM Prozessoren mit ca. 100kB RAM die auch noch vertretbar schnell sind.

/off topic


Wo du bei Arrays halt aufpassen musst ist dass du nicht über das Ende schreibst. Du hast volle Kontrolle darüber wiegenau  der Speicher verwendet wird. Mit allen Vorteilen und Nachteilen.

Wobei String Objekte auch nicht sooo schlimm sind wenn man sie korrekt verwendet. Wozu die meisten Anfänger aber nicht in der Lage sind, weil sie nicht verstehen was dabei gemacht wird. Also so programmieren dass man nicht ständig Objekte kopiert und reserve() verwenden statt ständig den Speicher anzupassen.

LeRoi

Das liegt ganz an dir.
Im Gegensatz zu Java gibt es in C/C++ erstmal kein automatisches Speichermanagement oder Müllabfuhr (garbage collection). Wenn du new verwendest, musst du auch delete machen, oder du machst new einmal nach Reset und weisst, was du tust. Oder du gönnst dir den Luxus von String-Objekten, die das -inzwischen einigermaßen ordentlich- selbst erledigen. (Kriegst du aber hier im Forum immer "Tu's Nicht, Strings sind Böse" - Hinweise ;)

Die "Arduino-Sprache" war schon immer so gedacht, dass sie Java ähnlich sieht, um es dadurch Nicht-Programmierern etwas einfacher zu machen. Daher gab es schon immer String Objekte (anfangs wirklich schrottig). Kostet natürlich etwas an Performance und Speicher.
Einfaches noch einfacher erscheinen zu lassen birgt leider auch das Risiko, dass es umso komplizierter wird, wenn dies an seine Grenzen stößt.

Und - für mich - besteht der Reiz ja grade darin, hübsche Sachen mit wenig Ressourcen zu machen.
Back to the Roots

Puffer mit variablem Inhalt liegen übrigens nie im Flash, da brauchst du dir keine Sorgen zu machen.
Feste Texte sollten zur Speicherplatz-Optimierung jedoch bevorzugt aus dem Flash gelesen werden.
Den Reiz mit wenig Speicher und geringer Taktrate viel zu machen, kann ich nachvollziehen. Ich hab mit Microchips Controllern mit 1kB Flash angefangen (wenigstens gleich nen 20Mhz Quarz angelegt) und viel mit Assembler gecodet, was mir am meisten Spaß macht, da es für mich wie eine Rätselaufgabe ist, die man besonders elegant und clever lösen möchte. Auch wenn sich das kaum noch lohnt, ist es immer wieder spannend, die Machinenbefehle kennenzulernen, zu verwenden und neue Möglichkeiten zu entdecken.

LeRoi

Danke für eure doch recht ausführlichen Antworten. Ich werde jetzt feste Char Arrays (read und write Buffer) anlegen (max nötige Größe kann ich direkt am Anfang berechnen) und mich wieder den C String Befehlen zuwenden. Das fühlt sich dann einfach besser an.

Doc_Arduino

Hallo,

dreht ihr euch nicht irgendwie im Kreis?
Die einen sagen Strings sind böse. Wüßte nicht warum.
Die anderen sagen char arrays wären besser.
Derweile wissen wir doch das Strings auch nur char arrays sind.
Also warum werden diese gegeneinander gestellt?
Ich verstehe die Diskussion nicht.
Im Grunde geht's doch nur darum so wenig und nur so groß wie nötig einen globalen String zu haben und den Rest mit kleinen lokalen Strings zu erledigen.
Tschau
Doc Arduino '\0'

Messschieber auslesen: http://forum.arduino.cc/index.php?topic=273445
EA-DOGM Display - Demos: http://forum.arduino.cc/index.php?topic=378279

michael_x

Quote
Derweile wissen wir doch das Strings auch nur char arrays sind.
Ich weiss, dass sie ein char array zurückliefern können, aber etwas ganz anderes sind.

Ich spare es mir hier aber, die Unterschiede aufzuzählen.

Serenifly

Speicher-Effizienz ist nicht das einzige Problem mit der String Klasse. Sie ist auch in ihrer Funktionalität stark eingeschränkt. An Konvertierungs- und Formatierungs -Funktionen mangelt es fast völlig. Also Dinge wie die Behandlung von Hex-Zahlen oder eine genau einstellbare Formatierung von Float Werten.
split() wurde entfernt, also ist ein Alptraum per Hand Teil-Strings abzutrennen. Wobei wie dabei wieder bei der Speicher Sache sind, da man dauernd neue Objekte bekommt statt Strings einfach in situ bearbeiten zu können.

Doc_Arduino

Hallo,

okay, ich hatte das in meinem Buch nachgelesen das Strings "nur" char Arrays sind. Scheinbar falsch ausgedrückt oder zu kurz erklärt. Ist ein Anfängerbuch. Mittlerweile, auf Grund des Kommentars weis ich es nun etwas besser.
Strings sind Zeiger auf eine Zeichenkette, die können aus char arrays bestehen.
Und char array ist eben ein array bestehend aus Einzelzeichen.
Und string ist auch ein Datentyp.
Soweit so gut.  :)

Mit der Einschränkung meinst du die vorhandenen Befehle auf den Arduino?
Denn Stringfunktionen gibts eigentlich im Überfluss.
Man muß nur wissen welche man davon benötigt, dass ist meistens das größte Problem.
Wegen dem zerteilen. Da kam ich zufällig drauf. String Remove sollte dafür passen.

Tschau
Doc Arduino '\0'

Messschieber auslesen: http://forum.arduino.cc/index.php?topic=273445
EA-DOGM Display - Demos: http://forum.arduino.cc/index.php?topic=378279

Serenifly

Strings sind Zeiger auf eine Zeichenkette, die können aus char arrays bestehen.
Nein. Strings sind Objekte die intern ein char Array verwalten. Man kann sogar mit c_str() inzwischen direkten Lese-Zugriff auf das interne Array bekommen. Das ist eine Art Wrapper Klasse die einfache Methoden enthält um das Array zu manipulieren.

Das Array ist aber immer nur so groß wie es gerade sein muss. Wenn man mit + ein Zeichen einfügt wird jedesmal realloc() gemacht. Das kann man mit reserve() umgehen indem man am Anfang einmal Speicher anlegt. Macht aber kaum jemand.

Und da es Objekte sind werden z.B. bei der einfachen per Value Übergabe oder Zuweisungen Kopien erstellt. Die Ausnahme sind Strings als Rückgabewerte von Funktionen. Das kann der Compiler schön wegoptimieren.
Da muss man wirklich aufpassen und Anfänger haben keine Ahnung was da im Hintergrund eigentlich abläuft.

Whandall

Ich glaube man kann das so einfach niemandem begreiflich machen, man muss es erlebt haben.

Solange man nur innerhalb von loop irgendwelche Kleinigkeiten ausdruckt
- viel mehr machen die teilweise haarsträubenden Beispiele nicht - sind Strings kein Problem.
Ebenso das Fehlen von F(), was konsequent um jede konstante Zeichenkette gehört (alternativ PSTR() oder ähnliches).

Kombiniert man WiFi (non ESP), SD Karte, Wire, RTC und Serial, dann wird es auf einem 328 sehr sehr eng,
auf einmal ist mehr als die Hälte oder noch mehr des Speichers 'verbuffert'.

Dann lernt man die Tücke der Strings kennen, die einfach nicht mehr funktionieren (können).
Wirkliche Fehlerbehandung wie Exceptions gibt es nicht, ich weiss nicht einmal, ob es möglich ist,
nachzusehen, ob eine String Funktion aus Speichermangel fehlschlug.

Die Fehlersuche gestaltet sich in so einem Umfeld... sagen wir mal 'interessant'.
Ah, this is obviously some strange usage of the word 'safe' that I wasn't previously aware of. (D.Adams)

rudirabbit

Selbst wenn die Arduino String Klasse besser geworden ist, wird dies immer mehr Ressourcen verbrauchen wie ein nullterminierter Char Array.
Geht ja nicht anders, jedes String Objekt braucht neben dem Char Array noch Variablen zur Verwaltung etc.  Und das kostet. (unnötig) 

Für die Char Array's bietet der Compiler "mächtige" Funktionen, ich vermisse dort nichts, ist alles da was man braucht.   :)

Arduino UNO,MEGA,DUE 
Dunkel die andere Seite ist. - Klappe, Yoda, und iss deinen Toast :-)

Go Up