Forum: Mikrocontroller und Digitale Elektronik AVR warum braucht String RAM?


von Thadeus Kobisky (Gast)


Lesenswert?

Hallo,

möglicherweise scheint meine Frage sehr Trollhaft zu sein. Tatsächlich 
ist diese ernst gemeint da ich mich selten mit Attinys beschäftigt habe, 
welche sehr begrenzten RAM haben.
1
void uart_string(const char *s)

Rufe ich obige Funktion mit einem String auf, wird dieser im Flash 
abgelegt und nutzt laut AtmelStudio7 ebenfalls die gleiche Anzahl an 
Bytes.
Bei einem Aufruf mit uart_string("T"):
1
Program Memory Usage   :  396 bytes   19,3 % Full
2
Data Memory Usage   :  62 bytes   48,4 % Full
Bei einem Aufruf mit uart_string("THallo"):
1
Program Memory Usage   :  400 bytes   19,5 % Full
2
Data Memory Usage   :  66 bytes   51,6 % Full
Warum wird hier der RAM ebenfalls genutzt obwohl das Schlüsselwort const 
benutzt wurde? Bin hier gerade etwas überfragt, warum in einer solchen 
Situation explizit mit PROGMEM oder __flash gearbeitet werden muss um 
dem aus dem weg zu gehen.

BG
Thadeus

von Cyblord -. (cyblord)


Lesenswert?

Thadeus Kobisky schrieb:
> Warum wird hier der RAM ebenfalls genutzt obwohl das Schlüsselwort const
> benutzt wurde? Bin hier gerade etwas überfragt, warum in einer solchen
> Situation explizit mit PROGMEM oder __flash gearbeitet werden muss um
> dem aus dem weg zu gehen.

Weil es keinen durchgehenden Adressraum gibt müssen solche Krücken 
benutzt werden.
Der AVR kann eben nicht in gleicher Weise Daten aus dem Flash lesen wie 
er sie aus dem Ram lesen würde.
Also entweder die Strings werden im Flash und im Ram abgelegt (also am 
Anfang vom Flash ins RAM kopiert) -> alles läuft normal. Oder sie liegen 
nur im Flash und du musst spezielle Funktionen nutzen (strcpy_P usw.). 
Du hast die Wahl.

Der Unterschied zwischen:

uart_string("T")

und

uart_string("THallo"):

liegt vermutlich darin dass der Compiler für ein einziges Bytes gar 
keinen String anlegt, sondern das Byte direkt an der Aufrufstelle 
übergibt. D.h. hier fällt das Byte einfach in den Codeteil. Kein RAM 
benötigt.

: Bearbeitet durch User
von Thadeus Kobisky (Gast)


Lesenswert?

Cyblord -. schrieb:
> Der Unterschied zwischen:
>
> uart_string("T")
>
> und
>
> uart_string("THallo"):
>
> liegt vermutlich darin dass der Compiler für ein einziges Bytes gar
> keinen String anlegt, sondern das Byte direkt an der Aufrufstelle
> übergibt. D.h. hier fällt das Byte einfach in den Codeteil. Kein RAM
> benötigt.

Möglicherweise ein ungünstiges Beispiel.
Zwischen uart_string("THa") und uart_string("THallo") gilt selbiges.

BG
Thadeus

von Thadeus Kobisky (Gast)


Lesenswert?

Cyblord -. schrieb:
> Weil es keinen durchgehenden Adressraum gibt müssen solche Krücken
> benutzt werden.
> Der AVR kann eben nicht in gleicher Weise Daten aus dem Flash lesen wie
> er sie aus dem Ram lesen würde.
> Also entweder die Strings werden im Flash und im Ram abgelegt (also am
> Anfang vom Flash ins RAM kopiert) -> alles läuft normal. Oder sie liegen
> nur im Flash und du musst spezielle Funktionen nutzen (strcpy_P usw.).
> Du hast die Wahl.

Danke für deine Erklärung.

von Cyblord -. (cyblord)


Lesenswert?

Thadeus Kobisky schrieb:
> Zwischen uart_string("THa") und uart_string("THallo") gilt selbiges.

Einfach mal den zugehörigen Assemblercode anschauen dann siehst du was 
passiert.
Grundsätzlich gilt das so wie beschrieben. Der Compiler kann und wird 
hier aber optimieren wenn möglich. Es ist denkbar dass ein 3 Byte 
Parameter sparender direkt im Code abgebildet werden kann als diesen 
String tatsächlich an anderer Stelle als String abzulegen.

: Bearbeitet durch User
von Joachim B. (jar)


Lesenswert?

Cyblord -. schrieb:
> du musst spezielle Funktionen nutzen (strcpy_P usw.)

alle str*** Funktionen, nur manchmal bringt str***_P keinen RAM Vorteil 
weil diese _P Funktion auch RAM braucht, ich wunderte mich das trotz _P 
mehr RAM gebraucht wurde, kann ja jeder selbst probieren, mal als _P 
kompilieren mal ohne und je nach Textlänge wird das Ergebnis in used Ram 
überraschen

von Cyblord -. (cyblord)


Lesenswert?

Joachim B. schrieb:
> Cyblord -. schrieb:
>> du musst spezielle Funktionen nutzen (strcpy_P usw.)
>
> alle str*** Funktionen, nur manchmal bringt str***_P keinen RAM Vorteil
> weil diese _P Funktion auch RAM braucht, ich wunderte mich das trotz _P
> mehr RAM gebraucht wurde, kann ja jeder selbst probieren, mal als _P
> kompilieren mal ohne und je nach Textlänge wird das Ergebnis in used Ram
> überraschen

Kannst du mal erklären wie diese Funktion RAM belegen sollte? Es handelt 
sich einfach nur um Code welcher Bytes aus dem Flash an eine Position im 
RAM kopiert. Du scheinst hier irgendwas zu verwechseln bzw. falsch 
interpretiert zu haben.
Oder bring mal ein Minimalbeispiel für deine These dann kann man die 
besser einordnen.

: Bearbeitet durch User
von Joachim B. (jar)


Lesenswert?

Cyblord -. schrieb:
> Kannst du mal erklären wie diese Funktion RAM belegen sollte? Es handelt
> sich einfach nur um Code welcher Bytes aus dem Flash an eine Position im
> RAM kopiert. Du scheinst hier irgendwas zu verwechseln bzw. falsch
> interpretiert zu haben.

nee kann ich nicht, aber ich bin kein Programmierer und stelle nur fest 
das es so ist.

ich vermute der Aufruf der str???_P Routinen braucht RAM, Stack sichern, 
Variablen sichern und wieder freigeben, oder RAM für Pointer, was weiss 
ich. Es bleibt eine Tatsche das str???_P Routinen dazu gelinkt werden 
müssen und das eben nicht ohne Folgen bleibt.

strcpy_P
strcmp_P
strlen_P

sind so Funktionen die ich nutze und ich mit und ohne _P teste also was 
für den knappen SRAM im m328 weniger weh tut. Aber selten mal ist RAM 
über und flash wird knapp dann natürlich ohne PSTR("hier ist ein langer 
Text....")

: Bearbeitet durch User
von Cyblord -. (cyblord)


Lesenswert?

Joachim B. schrieb:
> Cyblord -. schrieb:
>> Kannst du mal erklären wie diese Funktion RAM belegen sollte? Es handelt
>> sich einfach nur um Code welcher Bytes aus dem Flash an eine Position im
>> RAM kopiert. Du scheinst hier irgendwas zu verwechseln bzw. falsch
>> interpretiert zu haben.
>
> nee kann ich nicht, aber ich bin kein Programmierer und stelle nur fest
> das es so ist.

Es ist aber Unsinn, also bringe ein Beispiel oder höre auf so einen 
Quatsch zu schreiben.

> ich vermute der Aufruf der str???_P Routinen braucht RAM, Stack sichern,
> Variablen sichern und wieder freigeben, oder RAM für Pointer, was weiss
> ich. Es bleibt eine Tatsche das str???_P Routinen dazu gelinkt werden
> müssen und das eben nicht ohne Folgen bleibt.

Alles Unsinn. Den Stackverbrauch beim Funktionsaufruf siehst du in 
deinem avr-size gar nicht.

Und es ging hier darum dass Strings selber keinen Ram benötigen. Was du 
später mit diesem Strings machst, hat damit nichts zu tun. Wenn du den 
String später aus dem Flash irgendwohin kopieren willst, brauchst du 
dort natürlich Ram. Hat aber nichts mit der Problematik zu tun.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Joachim B. schrieb:
> aber ich bin kein Programmierer und stelle nur fest das es so ist.

Es ist nicht so. Die str*_P-Funktionen verwenden kein RAM - im 
statischen Sinne.

Du hast das wahrscheinlich verwechselt mit einem evtl. erhöhten 
Flash-Bedarf, der natürlich durch Hinzubinden der str*_P-Funktionen 
entstehen kann.

von Hmmm (Gast)


Lesenswert?

Joachim B. schrieb:
> nee kann ich nicht, aber ich bin kein Programmierer und stelle nur fest
> das es so ist.

Ich vermute, Du hast einen Fehler gemacht, der dafür gesorgt hat, dass 
eben nicht alles im Flash gelandet ist, z.B. Strings im Flash, Pointer 
darauf im RAM.

von EAF (Gast)


Lesenswert?

Gerne wird sowas gemacht:

const char test[] = "qwertzui";

In der Hoffnung dass es im Flash landet!
Tut es auch.
Wird aber zusätzlich, in Initphase, ins Ram kopiert.

const char test[] PROGMEM = "qwertzui";

Unterbindet das dann.
Mit der Folge, dass man keinen "gültigen" Zeiger auf die Zeichenkette 
erhält. Der Compiler kennt keinen Unterschied zwischen Flash und RAM 
Zeigern, für den zeigen alle Zeiger ins RAM.
z.B. die str***_P() Funktionen können mit solchen Flash-Zeigern umgehen.

von A. B. (Gast)


Lesenswert?

Thadeus Kobisky schrieb:
>
1
void uart_string(const char *s)
> [/code]
> Warum wird hier der RAM ebenfalls genutzt obwohl das Schlüsselwort const
> benutzt wurde? Bin hier gerade etwas überfragt, warum in einer solchen

Die Sache mit Harvard und PROGMEM ist nur sekundär. Das eigentliche 
Problem liegt woanders: Das "const" in der Deklaration sagt nur, dass 
die Funktion den String als konstant betrachten muss, ihn also nicht 
selbst ändern darf. Der Aufrufer ist jedoch frei, nach Belieben einen 
Zeiger auf einen konstanten String oder eben auch auf einen 
nichtkonstanten String zu übergeben. Die Funktion weiß aber nicht, 
welcher Fall eintritt ...

https://stackoverflow.com/questions/39124796/why-is-an-implicit-conversion-from-non-const-to-const-allowed-for-pointers-in-th

von EAF (Gast)


Lesenswert?

A. B. schrieb:
> Der Aufrufer ist jedoch frei, nach Belieben einen
> Zeiger auf einen konstanten String oder eben auch auf einen
> nichtkonstanten String zu übergeben.
Zumindest bei mir nörgelt es etwas rum!
1
#include <Streaming.h> // die Lib findest du selber ;-)
2
Stream &cout = Serial; // cout Emulation für "Arme"
3
4
5
void  boeseFunktion(char *str)
6
{
7
  *str = 'B';
8
}
9
10
void setup() 
11
{
12
  Serial.begin(9600);
13
  
14
  const char* t1   = "Hello";
15
  const char  t2[] = "Hello";
16
17
18
  boeseFunktion((char*)t1); // eigentlich ist der Cast boese
19
 // boeseFunktion(t1); // warning: invalid conversion from 'const char*' to 'char*'
20
  
21
  cout << t1 << endl;
22
  cout << t2 << endl;
23
  cout << "Hello" << endl;
24
}
25
26
void loop() 
27
{
28
29
}

Ausgabe:
1
Bello
2
Hello
3
Bello
Die Ausgabe kann bei dir anders aussehen. Der Compiler darf dir auch, 
bei der Gelegenheit, das Zimmer tapezieren.

Offensichtlich ist, dass dort eine anonyme Instanz der Zeichenkette im 
RAM liegt.

von A. B. (Gast)


Lesenswert?

EAF schrieb:
> Zumindest bei mir nörgelt es etwas rum!
> void  boeseFunktion(char *str)

Logisch, das ist ja auch genau umgedreht. Hier wird der Funktion 
ausdrücklich erlaubt, in dem String herumzuwurschteln (was sie ja auch 
tut). Da darf man natürlich keinen Zeiger auf einen konstanten String 
übergeben.
Das mit dem (char *)-cast ist ja auch eine ganz üble Gewalttat ;-)

Von (char *) nach (const char *) ist ok, anders herum aber natürlich 
nicht.

von Sebastian (Gast)


Lesenswert?

Cyblord -. schrieb:
> dass der Compiler für ein einziges Bytes gar keinen String anlegt

"T" ist zwei Bytes gross.

LG, Sebastian

von Cyblord -. (cyblord)


Lesenswert?

Sebastian schrieb:
> Cyblord -. schrieb:
>> dass der Compiler für ein einziges Bytes gar keinen String anlegt
>
> "T" ist zwei Bytes gross.
>
> LG, Sebastian

Auch wieder wahr.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Sebastian schrieb:
> Cyblord -. schrieb:
>
>> dass der Compiler für ein einziges Bytes gar keinen String anlegt
>
> "T" ist zwei Bytes gross.

Stimmt, aber das terminierende '\0' wird nicht gesendet. Bleibt also das 
nackte 'T'.

von Dennis E. (Gast)


Lesenswert?

Joachim B. schrieb:
> Cyblord -. schrieb:
>> Kannst du mal erklären wie diese Funktion RAM belegen sollte? Es handelt
>> sich einfach nur um Code welcher Bytes aus dem Flash an eine Position im
>> RAM kopiert. Du scheinst hier irgendwas zu verwechseln bzw. falsch
>> interpretiert zu haben.
>
> nee kann ich nicht, aber ich bin kein Programmierer und stelle nur fest
> das es so ist.
>

Dann verfatz dich hier uns sei still.

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.