Hallo zusammen,
ich will eine feste Zeichenkette per Funktionsaufruf ans Display
ausgeben. Also ungefähr so:
1
testprint(PSTR("Fcn:lala"));
Der Aufruf-String soll also im Flash liegen. Jetzt gibt es ein Problem
mit den Parametern von "fprintf_P": Die Funktion erlaubt nur eine
Zeichenkette im Flash.
Auf anderen Plattformen als AVR wird die Funktion einfach zu "fprintf".
Das funktioniert auf einem STM32 oder dem PC. Allerdings gilt der Aufruf
1
printf(string);
heutzutage als schlechter Stil, da für zahlreiche Sicherheitslücken
verantwortlich. Stattdessen ist
1
printf("%s",string);
empfohlen.
Allerdings läßt sich das nicht 1:1 auf den AVR übertragen:
fprintf_P(stdout,PSTR("%s"),PSTR("Out3:lalala"));// Funktioniert nicht
8
9
// Diese Variante von fprintf gilt als unzeitgemaess, weil unsicher:
10
fprintf_P(stderr,e);// Funktioniert
11
12
// Das ist das Ziel:
13
fprintf_P(stderr,PSTR("%s"),e);// Funktioniert nicht
14
15
16
// Displayinhalt darstellen
17
fprintf_P(stderr,PSTR(" \a"));
18
}
Also bleiben mir mehrere Möglichkeiten
1. "alte" Variante nutzen, mit den Kompilerwarnungen auf anderen
Plattformen leben lernen.
2. eine Kompilerweiche einbauen
3. Oder kennt jemand aus dem Forum eine saubere
Implementierungsvariante?
Viele Grüße
W.T.
4. ein fprintf_PP selber schreiben, das sowohl den Formatstring als auch
die Argumentenliste ja dem Flash liest.
Das sollte nicht so kompliziert sein, da du ja nur die Bersion für %s
mit einem Parameter implementieren musst.
Oliver
Axel S. schrieb:> Bitte was? Wo hast du das denn her
Errm, das ist doch Allgemeinwissen. Was wenn "string" von außen, übers
Netzwerk, kommt und ein "%s" enthält, was passiert dann? Dann sieht der
Aufruf äquivalent so aus:
1
printf("%s");
und dann wird irgendwas aus dem Speicher ausgegeben. Im besten Fall
stürzt das Programm einfach nur ab, im schlimmsten gibt es
möglicherweise geheime Informationen raus...
Dr. Sommer schrieb:> Was sich heute Programmierer nennt und dennoch solche Grundlagen nicht> kennt...
Axel ist genausowenig Programmierer wie ich. Also paßt schon.
Walter T. schrieb:> Dr. Sommer schrieb:>> Was sich heute Programmierer nennt und dennoch solche Grundlagen nicht>> kennt...>> Axel ist genausowenig Programmierer wie ich. Also paßt schon.
Nein, paßt nicht. Wenn er kein Programmierer ist, sollte er vielleicht
seine Klappe zu dem Thema nicht so weit aufreißen.
Wenn Du blind einen String ausgibst, kann viel Blödsinn passieren.
Wenn Du aber mit Deiner Funktion (im FLASH), eine unveränderbare
Zeichenkette (aus dem FLASH) ausgibst, sollte das sicher sein.
Kommt jemand an den Flash ran, so ist sowieso Hopfen und Schmalz
verrieben.
Sebastian S. schrieb:> Wenn Du aber mit Deiner Funktion (im FLASH), eine unveränderbare> Zeichenkette (aus dem FLASH) ausgibst, sollte das sicher sein.
Das ist wohl wahr. Allerdings weiß ich die Warnungen eines modernen
Compilers (allen voran Clang/LLVM) sehr zu schätzen. Und diese Compiler
warnen vor Anti-Pattern wie "uncontrolled format string". Es wäre
unklug, sich die echten, berechtigten Warnungen durch solche für die
konkrete Plattform unnötigen Warnungen zu überdecken. Und irgendwie ist
mein Ziel auch bei einem AVR-Programm immer noch 0 Errors, 0 Warnings, 0
Badboxes. Oder so ähnlich.
Axel S. schrieb:> Tatsächlich die ist zweite Form deutlich teurer.
Für teurer halte ich die zweite Form allerdings nicht. Wenn ich die Muße
habe, in das Assembler-Listing zu schauen, entdecke ich immer wieder,
daß der GCC die Aufrufe aus der printf-Familie munter so
durcheinandermischt, daß jeder Aufruf durch den billigsten Kandidaten
aus der Familie ersetzt wird.
(Disclaimer: Entdeckt beim Debuggen auf einem STM32, wo man die
putc-Funktionen selbst schreiben muß.)
Walter T. schrieb:> Axel S. schrieb:>> Tatsächlich die ist zweite Form deutlich teurer.>> Für teurer halte ich die zweite Form allerdings nicht.
Ich wüßte auch nicht, warum sie das sein sollte. Ich würde dann eher
noch mit einer Ersparnis rechnen, denn der Format-String muss ja im
Gegensatz zu dem anderen geparst werden und ist jetzt viel kürzer.
Dr. Sommer schrieb:> Axel S. schrieb:>> Bitte was? Wo hast du das denn her> Errm, das ist doch Allgemeinwissen. Was wenn "string" von außen, übers> Netzwerk, kommt und ein "%s" enthält, was passiert dann?
Wo war denn bitte davon die Rede, daß das ein String "von außen" ist?
Im Gegenteil, dem TE ging es ja gerade darum, daß der String im Flash
steht, also schon zur Compilezeit bekannt ist.
Dr. Sommer schrieb:> Was sich heute Programmierer nennt und dennoch solche Grundlagen nicht> kennt...Du mich auch
Wirklich, so Typen wie du sind das eigentliche Problem. Nicht verstanden
worum es geht, aber den Obermacker raushängen lassen.
Rolf M. schrieb:> Nein, paßt nicht. Wenn er kein Programmierer ist, sollte er vielleicht> seine Klappe zu dem Thema nicht so weit aufreißen.
Herr Nuhr, übernehmen Sie!
Walter T. schrieb:> Axel S. schrieb:>> Tatsächlich die ist zweite Form deutlich teurer.>> Für teurer halte ich die zweite Form allerdings nicht. Wenn ich die Muße> habe, in das Assembler-Listing zu schauen, entdecke ich immer wieder,> daß der GCC die Aufrufe aus der printf-Familie munter so> durcheinandermischt, daß jeder Aufruf durch den billigsten Kandidaten> aus der Familie ersetzt wird.
Wenn der Compiler das wegoptimiert, dann nicht. Allerdings grenzt das
Auswerten des Formatstrings zur Compilezeit schon an schwarze Magie. Ich
würde mich da keineswegs drauf verlassen.
Daß die variadische Funktion andererseits mit dem Parsen des Format-
strings und der verschachtelten Ausgabe von Formatstring und Argument
dann aufwendiger ist, muß ich ja hoffentlich nicht noch erklären.
Axel S. schrieb:> Wo war denn bitte davon die Rede, daß das ein String "von außen" ist?> Im Gegenteil, dem TE ging es ja gerade darum, daß der String im Flash> steht, also schon zur Compilezeit bekannt ist.
Na, hoffentlich wird der Programmteil niemals bearbeitet/kopiert, oder
viel schlimmer, hoffentlich gewöhnt sich keiner den Stil printf(string)
an. Nur weil ein mieser Code an einer ganz bestimmten Stelle "ok" ist,
sollte man das noch lange nicht so schreiben. Und die richtige Lösung
ist natürlich puts.
Axel S. schrieb:> Daß die variadische Funktion andererseits mit dem Parsen des Format-> strings und der verschachtelten Ausgabe von Formatstring und Argument> dann aufwendiger ist, muß ich ja hoffentlich nicht noch erklären.
Achja? In diesem Code:
1
printf("%d",string);
muss sie nur ein harmloses %s auswerten. In
1
printf(string);
muss sie einen möglicherweise langen komplizierten string auswerten und
nach "%" suchen, auch wenn gar keine drin sind.
Du meinst also, ich hätte besser schreiben sollen:
1
fprintf_P(stdout,PSTR("%s"),PSTR("Out3:lalala"));// Funktioniert nicht und das ist auch voellig konform mit dem dokumentierten Verhalten, aber ich suche eine Variante, die sinngemaess das macht
Ich merke es mir für's nächste Mal, daß die Freunde des horizontalen
Scrollbalkens auch etwas davon haben sollen.
Nichts für ungut. Es hat mich vorgestern ein Weilchen Zeit gekostet,
einen Fehler in neuem Quelltext auf einen Fehler in anderem Quelltext,
der seit ca. einem Jahr (auf anderen Plattformen) fehlerfrei läuft (da
PROGMEM dort keine Rolle spielt) auf den genau diese eine Zeile
herunterzubrechen, und da hatte ich tatsächlich die einfache Variante
mit fputs_P übersehen. Eben der Wald und die Bäume. Aber den Baum hat ja
Johann schon im zweiten Post herausgeholt.
Der Rest ist ja eh nur eine "wir haben das aber so gelernt!" -
"heutzutage macht man das aber so!"-Diskussion. Inhaltlich vermutlich
nutzlos, aber zwei Leute können ihren Frust dort wegschreiben, wo sie
keinen stören.
Alles in Butter.
P.S.: Edit: Jetzt bin ich aber enttäuscht, daß überlanger Code keinen
Scrollbalken mehr macht. Menno. Nichts funktioniert am Montagmorgen.
Walter T. schrieb:> Du meinst also
Wen meinst du mit „Du“? Mich? Nein, ich würde das nicht. Ich schrieb
dir, dass der Konvertierungsbuchstabe Groß-s genau dafür da ist, was
du da wolltest. Dass für den konkreten Fall fputs_P() besser ist, ist
eine andere Frage. Aber es kann ja auch die Situation geben, da man
das Argument für %S zuvor aus irgendeiner Tabelle nehmen möchte und
außer dem reinen String noch weitere Dinge formatieren möchte.
Der Formatbuchstabe "%S" ist allerdings für plattformübergreifenden
Quelltext eine echte Falle. Auf dem AVR steht er für String im PROGMEM,
unter MinGW und MSVC für wchar_t.
Walter T. schrieb:> Der Formatbuchstabe "%S" ist allerdings für plattformübergreifenden> Quelltext eine echte Falle. Auf dem AVR steht er für String im PROGMEM,> unter MinGW und MSVC für wchar_t.
Plattformübergreifende Strings im Flash gibt es sowieso nicht. ;-) Du
musst also in solchen Fällen ohnehin eine Abstraktion dafür schaffen,
und in dieser kannst du auch gleich das %S mit unterbringen.
Ansonsten normiert der Standard für wchar_t die Formatierung %ls,
während Großbuchstaben explizit als “may be used in extensions”
benannt werden. Damit ist die avr-libc-Implementierung in dieser
Hinsicht sogar standardkonform.
Eigentlich ist diese Abstraktion relativ primitiv realisierbar. Im
Anhang ist mal meine Variante für den begrenzten Funktionumfang von
avr/stdio, den ich wirklich nutze.
Wozu diese Abstraktion natürlich überhaupt nicht taugt, ist Fehler im
Bezug auf SRAM/Flash schon am PC zu finden. Sonst wäre dieser Thread nie
gemacht worden :-).
Für die pgm_read/pgm_write-Befehle gibt es dann noch ein paar kleine
Dummy-Befehle.
Ja, so ähnlich haben wir das auch schon vor Jahren getan, damals
um GCC vs. IAR auf dem AVR zu abstrahieren, später kam dann ARM
hinzu.
Wenn du dort jetzt noch einfügst:
1
#ifdef AVR
2
# define FMT_CONSTSTRING "S"
3
#else
4
# define FMT_CONSTSTRING "s"
5
#endif
dann hast du halt auch den format specifier noch abstrahiert.
Benutzt wird das dann wie mit den diversen Abstraktionen in
<inttypes.h>:
1
myprintf("The result for %"FMT_CONSTSTRING" is %d\n",