Speicherfrage am UNO

hey leute,

Folgendes :
Hatte diesen Code in einer Unterfunktion:
Ein Char Array mit 10 Texten darin.

char* Modustext[10] = {

    "Kabel", // Array 0 
    "M8 Anschluss ",// Array 1 
    "M12 Anschluss ",// Array 2 
    "Klemmleiste",// Array 3 
    "TASTER", //Array 4 
    "Alles klar ",//Array 5
    "loest dann"  //Array 6
    "druecken", // Array 7 
};

Dieser benötigte beim Kompilieren im Verhältnis sehr viel Speicherplatz.

Dann habe ich den auskommentiert, und jeden Text einzeln als

char* Text [2] = {"Hier dann Text"};

im Global also in keiner Funktion angelegt. Da war auf einmal der Speicher wesentlich geringer als zuvor.

Kann mir jemand erklären, woher dass kommt und warum dass so ist ?

Konnte in beiden Fällen die Texte wie gewünscht aufrufen.
Aber warum belegt das eine sooo viel mehr Speicher, als das andere.

Viele Grüße

Franz

Da wird eher der Compiler nicht gemerkt haben was in welchen Fall wie viel Speicher braucht. Wenn du Speicher sparen willst verwende PROGMEM!

char* Text [2] = {"Hier dann Text"};

Dafür braucht man kein Array aus Zeigern. Das geht so:

const char Text[] = "Hier dann Text";

oder:

const char* Text = "Hier dann Text";

Franz_grundi:
Aber warum belegt das eine sooo viel mehr Speicher, als das andere.

Das scheint nur so.

Franz_grundi:
... funktion ...
... Global ...

In Funktionen werden Variablen lokal zur Laufzeit angelegt, das sieht der Compiler noch nicht. Globale Variablen sieht der Compiler, weshalb diese in die Speicherberechnung mit eingehen.

Du könntest den freien Speicher zur Laufzeit überprüfen:

// Quelle: http://jeelabs.org/2011/05/22/atmega-memory-use/
int freeRam () {
  extern int __heap_start, *__brkval;
  int v;
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}

Danke Serenifly,

was würde dann der Zeiger bei meinem Bsp bedeuten ?

char* Text [2] = {"Hier dann Text"};

Dachte ich rufe dann irgendwo auf : println(Text[1]); und der Zeiger zeigt auf das Gesamte was im Array an Position 1 steht, also den Gesamten Text? Ist dem nicht so ?

Und dass heisst :

const char* Text = "Hier dann Text";

dass immer wenn Text aufgerufen wird, bsp println(Text); wird mit dem Zeiger der Gesamte Bereich angezeigt? Und ohne * würde er nur einen Buchstaben anzeigen?

Hey agmue, danke für die Erklärung und den Beispielcode, probier ich mal :slight_smile:

Lerne den Zusammenhang zwischen Arrays und Zeiger. Array-Variablen entsprechen Zeigern auf das erste Element. Deshalb sind C Strings Arrays und Arrays aus Strings sind Arrays aus Zeigern

char* Text [2] = {"Hier dann Text"};

Das ist ein Array aus Strings, bei denen nur einer definiert ist

Franz_grundi:
Aber warum ...

Beschäftige Dich gelegentlich mit der „Sichtbarkeit“ von Variablen und wann diese erzeugt werden bzw. gültig sind.

Gruß

Gregor

Anregung zum Spielen:

const char* Text[3] = {
  "Kabel", // Array 0
  "M8 Anschluss",// Array 1
  "M12 Anschluss" // Array 2
};

void setup() {
  Serial.begin(9600);
  Serial.println("Anfang");
  Serial.println(Text[2]);
  Serial.println(Text[2][0]);
  Serial.println(*Text[2]);
  Serial.println(*(Text[2] + 1));
}

void loop() {}

Du könntest den freien Speicher zur Laufzeit überprüfen:

Aber nicht die lokalen variablen, denn die landen auf dem Stack.
Und dieser geht in deine Rechnung nicht mit ein.

combie:
Aber nicht die lokalen variablen, denn die landen auf dem Stack.
Und dieser geht in deine Rechnung nicht mit ein.

Doch.
Wenn du dir das freemem - Beispiel anguckst, berechnet das den Platz zwischen einer gerade eben angelegten lokalen Variablen und dem Heap. Dieser Platz ist zum Zeitpunkt des Aufrufs frei, entweder für weitere Lokale Variable / Rücksprungadressen auf dem Stack oder für dynamischen Speicher auf dem Heap.

Das Problem der freeRam-Funktion ist, dass sie nur eine Momentaufnahme zum Aufrufzeitpunkt liefert. Das heißt nicht, dass der freie Speicher zu einem anderen Zeitpunkt nicht deutlich kleiner sein kann. Man müsste die Funktion dann aufrufen, wenn der Stack seine größte Ausdehnung hat. Aber das ist nicht so einfach rauszubekommen, es könnte auch ja auch durchaus innerhalb einer Lib sein.

Doch.
Wenn du dir das freemem - Beispiel anguckst, berechnet das den Platz zwischen einer gerade eben angelegten lokalen Variablen und dem Heap.

Auch das hilft dir nicht wirklich beim abschätzen des freien Speichers

Denn:

Momentaufnahme zum Aufrufzeitpunkt


Besser ist es (evtl.), bevor main() los läuft, den ganzen Speicher mit z.B. 0xaa voll zu schreiben, und nach druch spielen aller Funktionalität, nachzuschauen, wieviel 0xaa noch da sind.
In der Hoffnung, dass die Daten keine 0xaa enthalten.

Irgendwo, in meiner Wühlkiste sollte ich das haben, müsste das nur rausoperieren.

combie:
nachzuschauen, wieviel 0xaa noch da sind.

Das ist auch eine Moment-Aufnahme beim Aufruf. Was soll es denn auch anderes sein?

Das ist auch eine Moment-Aufnahme beim Aufruf

Wenn man eine Laufspur im Teppich fotografiert ist das auch eine Momentaufnahme.

Man sieht aber den beschriebene Bereich zwischen Heap und Stack.
Denn auch freigegebene Heap/Stack Bereiche, sind von den 0xaa befreit.

Also, richtig, "nur" eine Momentaufnahme der Laufspur.

Das ist das, was die anderen Verfahren nicht leisten können.

Netter Ansatz + Karma

Du siehst damit die irgendwann mal belgten Bereiche. Richtig.
Du siehst aber nicht den Zeitbezug.

Annahme: Ich brauche am Anfang kurzzeitig viel Heap für irgendwas und gebe den vollständig wieder frei.
Danach kann ich so viel Abstand zwischen Heap und Stack frei haben, dass das für einen problemlosen Betrieb vollkommen ausreicht.

Es ist also ein Worst-Case-Darstellung, die auch nicht alle Fälle betrachtet.

Ich denke, beide Varianten sind in ihrer Kombination sinnvoll. Das 0xAA für den Worst-Case-Fall und der Aufruf von freeRam an kritischen Stellen, um den Verlauf dort zu erfassen.

Ansonsten gilt immer noch der alte Spruch: Wer misst, misst Mist.

Gruß Tommy

postmaster-ino:
Netter Ansatz + Karma

Habs mal aus der Wühlkiste gesucht...
Und grob zusammengestoppelt.
Nicht auf Schönheit geachtet.

Die Sterne, in der Anzeige, sind 0xaa, die Minus sind nicht druckbare Byte.

Das Programm besteht aus 2 Dateien/Tabs:

Eine *.S Datei, welche die Intialisierung übernimmt:

#include <avr/io.h>

.equ mask, 0xaa
.section .init3

.global memInit                 
.global __heap_start      ;__malloc_heap_start           

memInit:
  ldi r30, lo8(__heap_start) 
  ldi r31, hi8(__heap_start) 
  ldi r24, mask
  ldi r25, hi8 (RAMEND) 
0:                          
  st  Z+,  r24                
  cpi r30, lo8(RAMEND)           
  cpc r31, r25                
  brne 0b

--

Und einer *.ino, für die Anzeige:

// https://rn-wissen.de/wiki/index.php/Speicherverbrauch_bestimmen_mit_avr-gcc

extern unsigned char __heap_start;
extern unsigned char __data_start;

const byte mask = 0xaa;

void setup() 
{
  Serial.begin(9600);
  Serial.println("Start");

  byte *ptr=&__data_start;

  for(unsigned int i=0; ptr<RAMEND; i++, ptr++)
  {
    if(not(i%16))
    {
      Serial.println(); 
      Serial.print((unsigned int)ptr);Serial.print(": ");
    }
  
    if(isalpha(*ptr))
    {
      Serial.print((char)*ptr);
    }else
    {
      Serial.print(*ptr==mask?'*':'-');
    }
  }
}

void loop() 
{
}