Guten Abend,
im AVR Beispielcode für Elm Chan FatFS gibt es die Dateien xitoa.s und
xitoa.h.
Die werden dafür benutzt, um Daten über Uart zu schicken. Darin gibt es
unter Anderem die Funktion:
void xprintf(const prog_char *format, ...); /* Send formatted string to
the registered device */
Im Beispiel übergibt er den Formatstring mit PSTR(), was den String
irgendwo in den PROGMEM schreibt.
Wenn man jetzt versucht, dort einen char* String zu übergeben, der nicht
explizit im Flash gespeichert wurde, kommt bei mir in der Ausgabe nicht
das raus, was im String steht, sondern eine Variante von dem, was in
einem der früheren PROGMEM Strings steht.
Beispiel:
In ff.c hatte ich ein paar Variablen per Uart anzeigen lassen:
In der Main habe ich ebenfalls ein paar Ausgaben mit PSTR und auch ein
paar mit einem normalen String machen lassen:
1
intvariable=44;
2
xprintf("zeile %d \n",1);//=1
3
xprintf("%s \n","String");//332
4
xprintf("%d \n",1);//_mount: vol=1
5
xprintf("%d \n",variable);//_mount: vol=44
6
xprintf("%s %d \n","Hallo",i);//_Test_stat=344
Als Kommentar daneben steht was die Zeile ausgibt.
Wie man sieht, sind es teilweise Fragmente von dem, was zuvor aus dem
PROGMEM heraus ausgegeben wurde. ("_mount: vol=" und "_Test_stat=")
Wenn ich jetzt sowas machen wie:
1
constchareintest[]={"%s \n"};
2
xprintf(eintest,"String");
Dann kommen für diese und die nachfolgende Zeile nur noch wirre Zeichen
raus, als ob er irgendwo in den Speicher greift.
([01]âè[0E]ñ[1C]÷[01]Î[01] usw.)
Mit einem globalen
1
constchareintest[]PROGMEM={"%s \n"};
und einem
1
xprintf(eintest,"String");
funktioniert es hingegen wie gewünscht, da der String jetzt im Progmem
steht.
Kann mir jemand erklären, wieso sich die Funktion so verhält? Scheinbar
kann sie ja mit dem Zeiger in den normalen Ram nicht viel anfangen.
Und wieso kommt bei der Variante mit dem separaten String so etwas
komisches raus?
Schöne Grüße
Karl schrieb:> Dann kommen für diese und die nachfolgende Zeile nur noch wirre Zeichen> raus, als ob er irgendwo in den Speicher greift.
Du hast es ja selbst schon gemerkt. Wenn eine Funktion einen Zeiger ins
PROGMEM erwartet, du aber einen Zeiger ins RAM übergibst kann alles
mögliche passieren. Von irgendwelchen Fragmenten aus Strings die
irgendwo im Flash liegen bis zu kompletten Müll.
Karl schrieb:> Kann mir jemand erklären, wieso sich die Funktion so verhält? Scheinbar> kann sie ja mit dem Zeiger in den normalen Ram nicht viel anfangen.> Und wieso kommt bei der Variante mit dem separaten String so etwas> komisches raus?
Weil der AVR eine Harvard-Architektur hat, d.h. getrennte Adressbereiche
für Flash und RAM. Wenn du Daten an Adresse 17 lesen willst, musst du
dazu sagen, ob der Rechner im RAM oder im Flash lesen soll - indem du
den richtigen Befehl verwendest (in Assembler LD und co. für RAM und LPM
und co. für Flash, in der libc für Flash Funktionen mit _P am Ende, bei
anderen Projekten musst du eben nachsehen...).
C braucht ein paar Krücken, um das Prinzip zu verstehen, und erlaubt
generell recht freie Typkonvertierungen, daher gibt es keine
Fehlermeldung, wenn du einen String im RAM anlegst (zum Beispiel an
Adresse 42) und dessen Adresse an eine Funktion übergibst, die aus dem
Flash liest. Was sie da liest, ist Zufall - vielleicht steht an der
Adresse 42 im Flash ein anderer String, vielleicht auch Programmcode.
Wenn ich das richtig im Kopf habe, werden explizit deklarierte Strings
(const char [] = "abcde") und implizit deklarierte Strings ("abcde") in
verschiedenen Speichersektionen im RAM zusammengefasst, daher werden bei
deinem ersten Test die RAM-Zeiger bei deinem Programm zufällig alle an
eine Stelle im Flash zeigen, in dem die _PSTRs liegen, daher kommen mehr
oder weniger sinnvolle "Fragmente" zusammen, im zweiten Test zeigen die
RAM-Zeiger dagegen in den Programmcode, deswegen kommt Zeichensalat. Und
vermutlich erst sehr spät eine '\0' für String-Ende, weshalb die
Funktion wahrscheinlich auch noch viel zu viele Zeichen aus dem Flash
ins RAM kopiert und damit das RAM überläuft.
MfG, Arno
Sebastian V. schrieb:> Wenn eine Funktion einen Zeiger ins> PROGMEM erwartet, du aber einen Zeiger ins RAM übergibst kann alles> mögliche passieren.
Sollte der Compiler da nicht mindestens warnen oder es gar nicht
zulassen?
MfG Klaus
Scott Meyers schrieb:> Die einzig sinnvolle Antwort, die man auf so etwas geben kann,> ist:> Stop using C!
Das ist kompletter Unsinn. Dieses konkrete Problem hast Du nur bei µCs
mit Harvard-Architektur - und nicht nur in C: auch in Assembler musst Du
verschieden auf die unterschiedlichen Speicherbereiche Flash und RAM
zugreifen.
Bei Neumann Prozessoren gibt es dieses Problem schlichtweg nicht. Schon
von daher ist Deine pauschale Aussage obsolet - um es mal höflich
auszudrücken.
Deine Antwort ist leider völliger non-sense!
Es geht um Typ-Sicherheit, damit solche Fehler erst gar nicht passieren.
Das Problem hätte man mit den entsprechenden DT in C++ gar nicht. Das
Programm würde schlicht nicht kompilieren!
Scott Meyers schrieb:> Es geht um Typ-Sicherheit, damit solche Fehler erst gar nicht passieren.
Sag das doch gleich. Du hattest ohne jegliches Zitat Deiner Vorredner
einfach nur diesen Satz "Stop using C" pauschal in den Raum geworfen.
Das war so allein für sich hingerotzt leider nicht verständlich.
Klaus schrieb:> Sebastian V. schrieb:>> Wenn eine Funktion einen Zeiger ins>> PROGMEM erwartet, du aber einen Zeiger ins RAM übergibst kann alles>> mögliche passieren.>> Sollte der Compiler da nicht mindestens warnen oder es gar nicht> zulassen?
Es gar nicht zuzulassen ist bei der hardwarenahen Programmierung mMn
grundsätzlich falsch, denn es wird immer Anwendungsfälle geben, in denen
man es doch aus irgendeinem Grund braucht. Das ist das schöne an C: Du
kannst damit unglaublich viel anstellen, weil niemand meint "das kann
man ja nur benutzen, um sich in den Fuß zu schießen, also verbieten wir
es".
Dass keine Warnung kommt, wundert mich allerdings auch ein wenig. Teste
ich heute Abend auf meinem Entwicklungsrechner mal, hier habe ich keinen
avr-C-Compiler.
Scott Meyers schrieb:> Was sollte denn sonst gemeint sein! Denn genau das (keine> Typsicherheit)> ist das Ursprungsproblem!!!
Schau mal hier im Forum, dann wirst du feststellen, dass die meisten
Leute mit "Stop Using C" sowas wie "ich bin so geil, ich programmier
mein Betriebssystem in Assembler" meinen. Dann wird dir sicherlich auch
Franks Antwort verständlich.
MfG, Arno
Scott Meyers schrieb:> Was sollte denn sonst gemeint sein!
Lies Dir diesen Teil des Ursprungspostings nochmal durch:
Karl schrieb:> Wenn man jetzt versucht, dort einen char* String zu übergeben, der nicht> explizit im Flash gespeichert wurde, kommt bei mir in der Ausgabe nicht> das raus, was im String steht, sondern eine Variante von dem, was in> einem der früheren PROGMEM Strings steht.
Der TO fragte also, wieso er nicht einfach einen "char* String"
heruntergeben kann. Wenn er nun C++ mit entsprechendem DT verwendet
hätte, dann hätte er halt einen Error bekommen und hier gefragt, warum
er diesen Error bekommen hätte. Sein Problem hätte er also mit Deiner
"Lösung" "Stop using C" auch nicht gelöst bekommen.
Scott Meyers schrieb:> Denn genau das (keine Typsicherheit) ist das Ursprungsproblem!!!
Das Ursprungsproblem schon, aber nicht die Ursprungsfrage.
P.S.
Google mal nach "Pratchett exclamation marks". Multiple
Ausrufungszeichen nerven nicht nur, sondern können ein Bild abgeben,
welches Du nicht unbedingt beabsichtigst.
Arno schrieb:> Schau mal hier im Forum, dann wirst du feststellen, dass die meisten> Leute mit "Stop Using C" sowas wie "ich bin so geil, ich programmier> mein Betriebssystem in Assembler" meinen. Dann wird dir sicherlich auch> Franks Antwort verständlich.
Das mag ja sein, gilt aber nicht für mich.
Frank M. schrieb:> Der TO fragte also, wieso er nicht einfach einen "char* String"> heruntergeben kann. Wenn er nun C++ mit entsprechendem DT verwendet> hätte, dann hätte er halt einen Error bekommen und hier gefragt, warum> er diesen Error bekommen hätte.
Falsch: sein eigentliches Problem entsteht ja erst dadurch, dass man ihm
die Möglichkeit gibt, die Schnittstelle falsch zu benutzen.
> Sein Problem hätte er also mit Deiner> "Lösung" "Stop using C" auch nicht gelöst bekommen.
Doch: sinnvoller weise hätte es eine überladene Funktion xprintf()
gegeben, einmal für die PGM-Strings und einmal für andere (etwa
std::string, ..., you name it), und das Problem wäre gar nicht erst
aufgetaucht.
Scott Meyers schrieb:> Arno schrieb:>> Schau mal hier im Forum, dann wirst du feststellen, dass die meisten>> Leute mit "Stop Using C" sowas wie "ich bin so geil, ich programmier>> mein Betriebssystem in Assembler" meinen. Dann wird dir sicherlich auch>> Franks Antwort verständlich.>> Das mag ja sein, gilt aber nicht für mich.
Und wenn du nur "Stop Using C" schreibst, wie sollte Frank das erkennen?
Scott Meyers schrieb:>> Sein Problem hätte er also mit Deiner>> "Lösung" "Stop using C" auch nicht gelöst bekommen.>> Doch: sinnvoller weise hätte es eine überladene Funktion xprintf()> gegeben, einmal für die PGM-Strings und einmal für andere (etwa> std::string, ..., you name it), und das Problem wäre gar nicht erst> aufgetaucht.
Das ist in manchen Kontexten sinnvoll und tatsächlich eine
Funktionalität von C++ oder Java, die ich in C oft vermisse.
In anderen Kontexten ist es sinnvoller, wenn die PGM-Strings eine
.toString-Methode haben, die implizit aufgerufen wird, wenn die Funktion
einen "normalen" String erwartet. In vielen Anwendungen ist der Aufwand
für beides zu groß (vor allem auf Seiten der Bibliotheksentwickler, die
sich hier ja sogar das _P-Suffix gespart haben - in Extremfällen auch an
Rechenzeit und/oder Speicherplatz)
Und in wieder anderen Kontexten möchte der Anwender Pointerarithmetik
verwenden und muss u.U. Pointer verschiedener Typen sogar miteinander
verrechnen statt "nur" einander zuzuweisen.
MfG, Arno
Arno schrieb:> Scott Meyers schrieb:>> Arno schrieb:>>> Schau mal hier im Forum, dann wirst du feststellen, dass die meisten>>> Leute mit "Stop Using C" sowas wie "ich bin so geil, ich programmier>>> mein Betriebssystem in Assembler" meinen. Dann wird dir sicherlich auch>>> Franks Antwort verständlich.>>>> Das mag ja sein, gilt aber nicht für mich.>> Und wenn du nur "Stop Using C" schreibst, wie sollte Frank das erkennen?
Das gilt auch umgekehrt: warum nimmt er das ohne Grund an?
>> Scott Meyers schrieb:>>> Sein Problem hätte er also mit Deiner>>> "Lösung" "Stop using C" auch nicht gelöst bekommen.>>>> Doch: sinnvoller weise hätte es eine überladene Funktion xprintf()>> gegeben, einmal für die PGM-Strings und einmal für andere (etwa>> std::string, ..., you name it), und das Problem wäre gar nicht erst>> aufgetaucht.>> Das ist in manchen Kontexten sinnvoll und tatsächlich eine> Funktionalität von C++ oder Java, die ich in C oft vermisse.>> In anderen Kontexten ist es sinnvoller, wenn die PGM-Strings eine> .toString-Methode haben, die implizit aufgerufen wird, wenn die Funktion> einen "normalen" String erwartet.
Nennt man Typumwandlungsoperator in C++.
Möglich, ist aber ggf. die schlechtere der Alternativen (nötige Kopien).
> In vielen Anwendungen ist der Aufwand> für beides zu groß (vor allem auf Seiten der Bibliotheksentwickler, die> sich hier ja sogar das _P-Suffix gespart haben - in Extremfällen auch an> Rechenzeit und/oder Speicherplatz)
Nein, denn die speziellen Instruktion, um die Daten aus dem Flash zu
holen, brauche ich eh!
> Und in wieder anderen Kontexten möchte der Anwender Pointerarithmetik> verwenden und muss u.U. Pointer verschiedener Typen sogar miteinander> verrechnen statt "nur" einander zuzuweisen.
Man sollte aber im eigenen Interesse nur das erlauben, was sinnvoll ist.
Bzw. den Anwender dazu zwingen, stark über das nachzudenkne, was er da
an fragwürden Sachen vor hat.
Wie in vielen Kontexten liegt hier der Fehler schon beim Ersteller der
Bib, nicht beim TO. Der begeht "nur" den Folge-Fehler, dann eben auch C
zu benutzen ...
Auch bei modernen Harvard Rechner ist es kein Problem da man i.d.R
sowohl über den Instruktion als auch über den Datenpfad auf Flash und
RAM zugreifen kann. Alles hängt am selben Bus oder Crossbar
Sonst wird es auch mühsam mal einen CRC über den eigenen Code zu
rechnen, oder viele Daten im Flash abzulegen,um das eigene Flash zu
Programmieren muss man auch mal Code aus dem RAM ausführen können.
Alles nicht so einfach mit den Dogmas.
"Jedem Tierchen sein Pläsierchen!"
Wer das Disziplin und das Wissen hat, kann mit C sehr gut fahren und
wird diese Freiheit auch lieben. Sicherlich schwirrt dann die Umwelt
voller Torpedos die den Unaufmerksamen oder den Schlampigen erwischen.
Aber das ist ja das Schöne daran...
The enemy lurks in the dark; to get you!
Sonst verläßt man sich halt bei größeren Projekten je nach Anforderungen
auf die voraussehenden Schutzvorkehrungen alternativer Sprachen die den
Einsatz von traditionellen C möglicherweise sonst zu riskant machen
würden.
Jeder muß selber wissen was ihm liegt und was notwendig ist.
Johann L. schrieb:> Klaus schrieb:>> Sebastian V. schrieb:>>> Wenn eine Funktion einen Zeiger ins>>> PROGMEM erwartet, du aber einen Zeiger ins RAM übergibst kann alles>>> mögliche passieren.>>>> Sollte der Compiler da nicht mindestens warnen oder es gar nicht>> zulassen?>> Verwende Address Spaces wie __flash zusammen mit> -Werror=addr-space-convert.>> https://gcc.gnu.org/onlinedocs/gcc/Named-Address-Spaces.html> https://gcc.gnu.org/onlinedocs/gcc/AVR-Options.htm...
Was aber nicht bei __attribute__((_progmem_)) bzw. eben prog_char
funktioniert, d.h. es kommt keine Warnung bzw. Fehler.
Scott Meyers schrieb:> Was aber nicht bei __attribute__((progmem)) bzw. eben prog_char> funktioniert, d.h. es kommt keine Warnung bzw. Fehler.
Betrachte PROGMEM als veraltet. Der Ersatz dafür ist __flash.
Bei prog_char kommt auch die entsprechende Warnung:
1
main.c:20:1: warning: 'prog_char' is deprecated: prog_char type is deprecated. [-Wdeprecated-declarations]
PROGMEM ist offiziell noch nicht veraltet, aber ich danke dir für den
Hinweis auf __flash - werde mal sehen, ob/wie ich meine Projekte darauf
umstricke.
MfG, Arno
Frank M. schrieb:> Scott Meyers schrieb:>> Was aber nicht bei __attribute__((progmem)) bzw. eben prog_char>> funktioniert, d.h. es kommt keine Warnung bzw. Fehler.>> Betrachte PROGMEM als veraltet. Der Ersatz dafür ist __flash.
Warum soll PROGMEM (__attribute__((progmem))) veraltet sein?
Scott Meyers schrieb:> Johann L. schrieb:>> Klaus schrieb:>>> Sollte der Compiler da nicht mindestens warnen oder es gar nicht>>> zulassen?>>>> Verwende Address Spaces wie __flash zusammen mit>> -Werror=addr-space-convert.>>>> https://gcc.gnu.org/onlinedocs/gcc/Named-Address-Spaces.html>> https://gcc.gnu.org/onlinedocs/gcc/AVR-Options.htm...>> Was aber nicht bei __attribute__((_progmem_)) bzw. eben prog_char> funktioniert, d.h. es kommt keine Warnung bzw. Fehler.
Wass auch nicht funktionieren kann weil progmem kein Qualifier ist
sondern ein Attribut. Diese regelt nur die ABlage des Codes wie ein
Section-Attribut, nicht aber den Zugriff.
prog_char* war immer sinnlos, da Attribute nicht Teil des Typs sind, im
Gegensatz zu Qualifiern.
__flash ist der einzige Weg, per Hochsprache auf den Flash zuzugreifen
und wird nur als C-Erweiterung unterstützt. In C++ muss man weiterhin
auf (Inline-)Assembler zurückgreifen.