Hallo, mal eine Frage, kann man eine Funktion void fkt(char* a, char* b) { machwas; } so mit Strings füttern: fkt(&("text"), &("nocheintext")) aufrufen? char array[]={"text"}; fkt(&array, ...) funktioniert bekanntermaßen, aber ich würde mir gerne die Schreibarbeit sparen...
String schrieb: > kann man eine Funktion > aufrufen? klar nur viel einfacher
1 | fkt("text", "nocheintext"); |
ok... das heißt ich muss garnicht mitteilen, dass call by reference verwendet werden soll?
String schrieb: > ok... das heißt ich muss garnicht mitteilen, dass call by reference > verwendet werden soll? schau dir doch einfach mal printf an und wie du es aufrufst.
String schrieb: > das heißt ich muss garnicht mitteilen, dass call by reference verwendet > werden soll C übergibt keine Arrays an Funktionen, sondern immer nur Adressen, d.h. der "call by reference" ist implizit. Deswegen ist es auch nicht möglich, den sizeof-Operator in einer Funktion auf ein (vermeintliches) Array-Funktionsargument anzuwenden. Oh, und auf dem AVR solltest Du dran denken, daß die Stringkonstanten kostbares RAM(!) belegen.
:
Bearbeitet durch User
String schrieb: > char array[]={"text"}; > > fkt(&array, ...) funktioniert bekanntermaßen, aber ich würde mir gerne > die Schreibarbeit sparen.. Da übergibst du aber kein char* sondern ein (char*)[5] Einen Zeiger auf ein char-Array mit 5 Elementen. Die Adresse ist identisch, in der Funktion kannst du den Unterschied nicht mehr feststellen. Ein Stringliteral "text" ist schon vom Typ char*
Dirk B. schrieb: > String schrieb: > char array[]={"text"}; > fkt(&array, ...) funktioniert bekanntermaßen, aber ich würde mir gerne > die Schreibarbeit sparen.. > > Da übergibst du aber kein char* sondern ein (char*)[5] Einen Zeiger auf > ein char-Array mit 5 Elementen. Die Adresse ist identisch, in der > Funktion kannst du den Unterschied nicht mehr feststellen. > > Ein Stringliteral "text" ist schon vom Typ char* Genauer const char*! Denn auch nur dann besitzt der Compiler die Möglichkeit, bei Übergabe in eine Funktion foo(const char* Bar) den String im Flash liegen zu lassen. Außer Du willst den String noch ändern, dann habe ich nichts gesagt ;)
Kasus schrieb: > Denn auch nur dann besitzt der Compiler die Möglichkeit, bei Übergabe in > eine Funktion foo(const char* Bar) den String im Flash liegen zu lassen. ... das ist bei von-Neumann-Maschinen so, nicht aber bei der Harvard-Architektur, so wie sie auf den 8-Bit-AVRs umgesetzt wird. Da muss explizit anderer Programmcode verwendet werden, damit eine Stringkonstante im Flash bleibt und nicht ins kostbare RAM kopiert wird.
String schrieb: > kann man eine Funktion > void fkt(char* a, char* b) > { > machwas; > } > > so mit Strings füttern: > > fkt(&("text"), &("nocheintext")) > > aufrufen? Kasus schrieb: >> Ein Stringliteral "text" ist schon vom Typ char* > > Genauer const char*! > Bei manchen Compilern ist ein explizites Casting (char *) nötig, um ein Stringliteral in eine Funktion einzusetzen, die mit char * ohne const deklariert ist. Deshalb sollte man sich bei selbstgeschriebenen Stringfunktionen angewöhnen, sie mit const zu deklarieren, wenn der String nur gelesen und nicht bearbeitet wird.
@ Kasus (Gast) >> Ein Stringliteral "text" ist schon vom Typ char* >Genauer const char*! >Denn auch nur dann besitzt der Compiler die Möglichkeit, bei Übergabe in >eine Funktion foo(const char* Bar) den String im Flash liegen zu lassen. Aber nicht beim avr gcc, dort muss man das zu Fuß über PSTR("") machen.
Falk B. schrieb: Hallo Falk, > Aber nicht beim avr gcc, dort muss man das zu Fuß über PSTR("") machen. Da möchte ich mal einhaken. Ich habe im folgenden mal beschrieben wie ich es verstanden habe: Wenn ich im Quelltext z.B. einen String definiere wie char str[] = "BlaBla"; wird dieser eigentlich konstanter Wert beim Start des Programms auf einem AVR-Kontroler von Flash-Speicher in das RAM kopiert, da es sich im Prinzip um eine veränderbare Variable handelt. Man könnte das zwar verhindern, indem man den String als Konstante definiert, also const char str[] = "BlaBla"; Dadurch würde erreicht, das diese Konstante direkt aus dem Flash-Speicher gelesen wird. Das funktioniert aber auf Grund der Havard-Architektur beim AVR-Kontroler nicht, da durch die Trennung von Programm- und Datenspeicher nicht ohne weiteres klar ist welcher Speicher adressiert werden soll. Um das trotzdem zu ermöglichen, muss dem Compiler angegeben werden, das der String str[] sich im Programmspeicher befindet und nicht ins RAM kopiert werden soll. Dazu dient das Makro PROGMEM: char str[] = PROGMEM "BlaBla"; Um jetzt im Programm auf str[] zugreifen zu können, existiert das Makro PSTR. Beispiel: ... uart_puts(PSTR str); ... Mit Hilfe dieses Makros wird dem Kompiler angezeigt, das sich str[] im Programmspeicher befindet und direkt von dort gelesen werden soll. Ist das so in etwa richtig? Wenn ja, wieso ist das so kompliziert gelöst? Gerade beim AVR wurde doch immer betont, das der Prozessorkern besonders in Hinblick auf die Verwendung von C als Programmiersprache entworfen wurde. Da hätte man doch eigentlich eine elegantere Methode vorsehen müssen um auf Hochsprachenebene auf Daten im Programmspeicher zugreifen zu können. Oder ist das ein spezifisches Problem des GNU-Compilers und bei anderen Compilern anders gelöst? rhf
@Roland Franz (rhf) >> Aber nicht beim avr gcc, dort muss man das zu Fuß über PSTR("") machen. >char str[] = "BlaBla"; >wird dieser eigentlich konstanter Wert beim Start des Programms auf >einem AVR-Kontroler von Flash-Speicher in das RAM kopiert, da es sich im >Prinzip um eine veränderbare Variable handelt. Ja. >Man könnte das zwar verhindern, indem man den String als Konstante >definiert, also >const char str[] = "BlaBla"; >Dadurch würde erreicht, das diese Konstante direkt aus dem >Flash-Speicher gelesen wird. Das funktioniert aber auf Grund der >Havard-Architektur beim AVR-Kontroler nicht, da durch die Trennung von >Programm- und Datenspeicher nicht ohne weiteres klar ist welcher >Speicher adressiert werden soll. Stimmt. >Um das trotzdem zu ermöglichen, muss dem Compiler angegeben werden, das >der String str[] sich im Programmspeicher befindet und nicht ins RAM >kopiert werden soll. Dazu dient das Makro PROGMEM: >char str[] = PROGMEM "BlaBla"; Nicht ganz. char str[] PROGMEM = "BlaBla"; >Um jetzt im Programm auf str[] zugreifen zu können, existiert das Makro >PSTR. Nein. PSTR() ist ein Makro, um konstante Strings in den Flash zu verlagern, ohne sie als Variable hinschreiben zu müssen. Z.B. so
1 | char tmp_str[50]; |
2 | int i; |
3 | |
4 | // Formatstring im FLash
|
5 | sprintf_P (tmp_strs, PSTR("Hallo Welt %d\r\n"), i); |
6 | |
7 | // Formatstring im RAM
|
8 | sprintf (tmp_strs, "Hallo Welt %d\r\n", i); |
Beide Funktionen machen das gleiche, ABER! Es sind verschiedene Funktionen. Die mit Endung _P erwartet einen String im Flash! Die kann NICHT mit einem String im RAM arbeiten! Das ist ein wenig doof beim AVR, aber man kann damit leben. >uart_puts(PSTR str); Geht nicht, weil diese Funktion einen Pointer auf den RAM erwartet. Man muss eine neue Funktion schreiben, welche mit einem Flashpointer arbeitet. In der libc beim avr gcc haben all diese Funktionen die Endung _P. >Mit Hilfe dieses Makros wird dem Kompiler angezeigt, das sich str[] im >Programmspeicher befindet und direkt von dort gelesen werden soll. Ja, der String wird dort auch direkt platziert, aber das nützt einem nur was bei Funktionen, welche dafür gedacht sind. >Wenn ja, wieso ist das so kompliziert gelöst? Gerade beim AVR wurde doch >immer betont, das der Prozessorkern besonders in Hinblick auf die >Verwendung von C als Programmiersprache entworfen wurde. Nobody is perfect. > Da hätte man >doch eigentlich eine elegantere Methode vorsehen müssen um auf >Hochsprachenebene auf Daten im Programmspeicher zugreifen zu können. Kann man auch, dann muss man aber zur Laufzeit erkennen, ob der Pointer auf den RAM oder Flash zeigt. Das kostet Speicherplatz (24 Bit statt 16 Bit Pointer) und etwas Rechenzeit (Unterscheidung des Zugriffs). >Oder ist das ein spezifisches Problem des GNU-Compilers und bei anderen >Compilern anders gelöst? Genau. Beim avr gcc hat man sich anfangs für getrennte Pointer entschieden, andere Compiler nehmen Universalpointer und prüfen zur Laufzeit. Die "neueren" avr gcc können das auch, aber die aktuelle avr gcc libc mit all den schönen Funktionen kann das nicht, die läuft noch immer mit dem alten Konzept. https://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Jenseits_von_flash
Rufus Τ. F. schrieb: > ... das ist bei von-Neumann-Maschinen so, nicht aber bei der > Harvard-Architektur, so wie sie auf den 8-Bit-AVRs umgesetzt wird. Da > muss explizit anderer Programmcode verwendet werden, damit eine > Stringkonstante im Flash bleibt und nicht ins kostbare RAM kopiert wird. Es gibt aber auch neuere AVRs, wo dasnicht mehr so ist. Beispiele sind ATtiny40 oder ATtiny817. Da genügt ein angepasstes linker description file (bei Verwendung von avr-gcc) und fertig ist der Lack :-)
Falk B. schrieb:
Hallo Falk,
Das bedeutet das man beim Zugriff auf Daten, die im Flash-Speicher
deklariert sind, 3 Fälle unterscheiden muss:
1: Die Daten sind als Variablen deklariert und werden zur Laufzeit des
Programms in das Kontroler-RAM kopiert. Für den Zugriff auf die Daten
können die normalen Funktionen genutzt werde:
const char str[] = "BlaBla";
2: Die Daten sind als Variablen deklariert und sollen zur Laufzeit des
Programms aus dem Flash-Speicher gelesen werden. Für den Zugriff auf die
Daten müssen spezielle Funktionen benutzt werden, die in der Lage sind
direkt aus dem Flash-Speicher zu lesen:
char str[] PROGMEM = "BlaBla";
3: Die Daten sind als "einfache" String-Konstanten deklariert und sollen
zur Laufzeit des Programms aus dem Flash-Speicher gelesen werden,
Zugriff erfolgt wie unter 2:
...PSTR("BlaBla")...
rhf
@Roland Franz (rhf) >1: Die Daten sind als Variablen deklariert und werden zur Laufzeit des >Programms in das Kontroler-RAM kopiert. Für den Zugriff auf die Daten >können die normalen Funktionen genutzt werde: >const char str[] = "BlaBla"; Das ist aber eine Konstante, wenn gleich sie dennoch im RAM landet. >2: Die Daten sind als Variablen deklariert und sollen zur Laufzeit des >Programms aus dem Flash-Speicher gelesen werden. Für den Zugriff auf die >Daten müssen spezielle Funktionen benutzt werden, die in der Lage sind >direkt aus dem Flash-Speicher zu lesen: >char str[] PROGMEM = "BlaBla"; Stimmt. Aber es sind auch hier Konstanten. Die aktuellen avr gcc wollen da auch ein const sehen, sonst meckert der Compiler. const char str[] PROGMEM = "BlaBla"; >3: Die Daten sind als "einfache" String-Konstanten deklariert und sollen >zur Laufzeit des Programms aus dem Flash-Speicher gelesen werden, >Zugriff erfolgt wie unter 2: >..PSTR("BlaBla")... Stimmt auch.
1 | const char str[] PROGMEM = "BlaBla"; |
2 | char ramstring[20]; |
3 | |
4 | sprintf_P(ramstring, PSTR("Hallo Welt")); // konstanter String direkt hingeschrieben |
5 | sprintf_P(ramstring, str); // identisch, nur als PROGMEM Konstante definiert |
Wenn man nicht ganze Romane schreibt, kann man Strings auch im RAM belassen. Selbst ein ATtiny85 hat ja schon 512Byte RAM, da muß man nicht mit jedem Byte knausern. Und beim ATmega1284 hat man 16kB RAM. Wenn das Programm läuft und man Lust dazu hat, kann man dann die größten Brocken in den Flash legen.
Falk B. schrieb:
Hallo Falk,
vielen Dank für die Antwort, es scheint als ob ich es so halbwegs
verstanden habe. Trotzdem finde ich es ziemlich umständlich, aber
Hauptsache ist, das es prinzipell funktioniert.
rhf
Peter D. schrieb: Hallo Peter, > Wenn man nicht ganze Romane schreibt, kann man Strings auch im RAM > belassen. Ja, da hast du recht. Bei meiner momentanen Bastelei werde ich das auch tun. Ich bin nur darauf gekommen weil ich gelesen hatte das der GNU-C-Compiler bei den AVR-Kontrolern die Konstanten standardmäßig ins RAM kopiert und mich gefragt warum das so ist. rhf
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.