Forum: Compiler & IDEs AVR: prog_char* == char* ?


von Sebastian Fahrner (Gast)


Lesenswert?

Hallo,

mal eine Frage zu Pointern ins Flash beim AVR:
Gibt es überhaupt einen Unterschied zwischen Pointern, in ins RAM
zeigen und Pointer, die ins FLASH zeigen?

Es gibt zwar in AVRGCC die Pointer PGM_P und PGM_VOID_P, aber mir ist
aufgefallen, daß man statt diesen auch "normale" Pointer benutzen
kann. Der eigentliche Unterschied ob RAM oder FLASH liegt dann nur noch
in der Routine (z.B. ob memcpy() oder memcpy_P() ).

Grüße,

Sebastian

von Rufus T. Firefly (Gast)


Lesenswert?

Beim AVR ist Programmspeicher nicht das gleiche wie der Datenspeicher -
daher sind Pointer auf RAM in der Tat was anderes als Pointer auf
FLASH.

Das ist ziemlich heimtückisch.

von mthomas (Gast)


Lesenswert?

Beim aktuellen avr-gcc sind Pointer 'auf Flash' und Pointer 'auf
RAM' eben nicht wirklich "was anderes", denn dann gaebe es die ganze
"progmem"-Verwirrungen nicht, die hier und in anderen Foren immer
wieder auftreten, und man bzw. der Compiler und die
Laufzeitbibliotheksfunktionen koennten einem Pointer irgendwie
"ansehen" worauf er zeigt und ensprechenden Code generieren/nutzen.

von Sebastian Fahrner (Gast)


Lesenswert?

Ich erinnere mich dunkel, daß es beim Keil C51-Compiler einen "Generic
Pointer" gab, der 3 Bytes belegt. Dabei waren 2 Bytes für die Adresse
und das 3.Byte gab den Speicher-Typ an (IDATA, XDATA und CODE).

Aber wenn ich es richtig verstehe, gibt es beim AVR-GCC keinen
Unterschied zwischen RAM- und ROM-Pointern, da beide nur 2 Bytes für
die Adresse haben, oder?

Grüße,

Sebastian

von Rufus T. Firefly (Gast)


Lesenswert?

Wenn RAM- und ROM-Pointer nur zwei Bytes belegen, muss man bei deren
Gebrauch aber höllisch aufpassen, daß man die nicht verwechselt - sonst
geht's in die Hose, da sie nicht auswechselbar verwendet werden
dürfen.

Damit sind die Pointer definitiv was unterschiedliches - auch wenn's
der Compiler nicht auseinanderhalten kann.
Die Unterscheidung muss also durch den Programmierer aufrechterhalten
werden; schafft der's nicht, geht so einiges in die Hose.

Also braucht man entweder von allen Funktionen, denen Pointer übergeben
werden können zwei Ausführungen (mit RAM- und mit ROM-Pointer) oder man
muss ineffiziente Umkopieraktionen durchführen.

Was für eine Grütze. Aber verständlich; das 3-Byte-Pointer-Konzept ist
in der Tat ineffizient, da ja bei Laufzeit bei jedem Pointerzugriff
erst einmal nachgesehen werden muss, worum es sich dabei handelt ...

Diese Architektur mit getrenntem Code/Datenspeicher ist vielleicht doch
nicht die allerpraktischste, oder?

von Jörg Wunsch (Gast)


Lesenswert?

Genauso ist es.

Es gibt wohl einen C-Standard-Vorschlag für distinct memory spaces,
und einen Willigen, der dies im längeren Zeitrahmen im GCC
implementieren möchte (Svein Seldal).  Damit sollte der Compiler dann
auch in der Lage sein, die Zeiger zumindest auseinanderzuhalten, d. h.
eine Warnung zu generieren, wenn man einen Zeiger in den falschen
memory space benutzt.

Bis dahin ist die Unterscheidung ausschließlich im Kopf des
Programmierers.  Manche Compiler (wie IAR) helfen dem Programmierer
mit einigen Automatismen mehr (z. B. beim Dereferenzieren), aber die
Auswahl der korrekten Routine obliegt dennoch dem Programmierer.

von mthomas (Gast)


Lesenswert?

"Damit sind die Pointer definitiv was unterschiedliches - auch
wenn's der Compiler nicht auseinanderhalten kann."

"Genaus so ist es"

Hmm, was ist nach so definitiven Aussagen denn genau der Unterschied.
Ich frage nicht, wie mit den Addressangaben umzugehen ist oder was man
im Kopf haben muss, nur wo der definitive Unterschied ist, bezogen auf
die Pointer selbst.

von Rufus T. Firefly (Gast)


Lesenswert?

Ein Pointer ist für den Compiler ein 16-Bit-Wert. Um diesen korrekt
nutzen zu können, muss der Compiler wissen, ob dieser Pointer auf RAM
oder ROM zeigt - die nämlich liegen nicht im gleichen Adressraum.

Damit ist ein ROM-Pointer mit dem Wert 0x1234 was anderes als ein
RAM-Pointer mit dem gleichen Wert.
Das heimtückische ist, daß man das dem Wert nicht ansehen kann.

Bevor man einen Pointer verwendet, muss man also sehr genau wissen,
worauf er zeigt bzw. worauf er zeigen soll.
Eine Funktion wie puts() erwartet als Argument einen Pointer - und zwar
aller Wahrscheinlichkeit nach einen RAM-Pointer.
Also muss man einen String aus dem ROM erstmal ins RAM kopieren, damit
man puts() aufrufen kann - oder man muss eine puts()-Variante
implementieren, die ROM-Pointer verarbeitet.

Würde der Compiler wenigstens zur Übersetzungszeit RAM- und ROM-Pointer
unterscheiden können, dann würden Aufrufe von Funktionen wie puts() mit
ROM-Pointern wenigstens mit entsprechenden Fehlermeldungen quittiert -
das aber ist, wenn ich J. Wunsch korrekt interpretiere, in GCC noch
lange nicht implementiert.

Damit wird den C-Anfängern hier im Forum noch ein ganz besonderer
Knüppel zwischen die Beine geworfen, vor allem, wenn C-Fragen von
Leuten beantwortet werden, die C zwar mit der Muttermilch aufgesogen
haben, aber eben "normale" Prozessoren damit programmieren ...

von Jörg Wunsch (Gast)


Lesenswert?

Martin, es gibt derzeit nur einen Unterschied, der liegt in der
Definition dieser Objekte: all diese PGMSPACE usw. Makros expandieren
letztlich zu einem __attribute__((_progmem_)), was den Compiler
veranlasst, die derart definierten Objekte in eine separate section im
ELF-File einzusortieren.

Ich glaube, es gibt noch einen zweiten Unterschied, dass für deren zur
Compilezeit erfolgendes Dereferenzieren der pm-Operator im
Assemblercode benutzt wird.  Dadurch funktionieren eben solche Dinge
wie menu[2], obwohl menu[i] nicht mehr funktioniert (mit i != const).

Ansonsten sind damit definierte Zeiger aber in der Tat hernach für den
Compiler ,,ganz normale'' Zeiger.

von mthomas (Gast)


Lesenswert?

Merci fuer die Infromation Jörg

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.