Forum: Mikrocontroller und Digitale Elektronik Strings direkt in Funktion eintragen


von String (Gast)


Lesenswert?

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...

von Peter II (Gast)


Lesenswert?

String schrieb:
> kann man eine Funktion
> aufrufen?

klar nur viel einfacher
1
fkt("text", "nocheintext");

von String (Gast)


Lesenswert?

ok... das heißt ich muss garnicht mitteilen, dass call by reference 
verwendet werden soll?

von Peter II (Gast)


Lesenswert?

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.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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
von Dirk B. (dirkb2)


Lesenswert?

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*

von Kasus (Gast)


Lesenswert?

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 ;)

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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.

von Jobst Q. (joquis)


Lesenswert?

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.

von Falk B. (falk)


Lesenswert?

@  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.

von Roland F. (rhf)


Lesenswert?

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

von Falk B. (falk)


Lesenswert?

@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

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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 :-)

von Roland F. (rhf)


Lesenswert?

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

von Falk B. (falk)


Lesenswert?

@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

von Peter D. (peda)


Lesenswert?

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.

von Roland F. (rhf)


Lesenswert?

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

von Roland F. (rhf)


Lesenswert?

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
Noch kein Account? Hier anmelden.