Forum: Mikrocontroller und Digitale Elektronik Array gräßer als Speicher? äh


von Tim (Gast)


Lesenswert?

Hallo wenn ich in einer Funktion eine array anlegen dann wird sie beim 
Compilieren nicht überprüft.

Beispiel
1
int main(void)
2
{
3
  unsigned char buffer[5000];
4
  unsigned int i;
5
  for(i=0; i<5000; i++)
6
  {
7
    buffer[i] = SPDR;
8
9
  }
10
  while(1)
11
  {
12
  }
13
}

AVR Memory Usage
----------------
Device: atmega644

Program:     196 bytes (0.3% Full)
(.text + .data + .bootloader)

Data:          0 bytes (0.0% Full)
(.data + .bss + .noinit)

Build succeeded with 0 Warnings...

Dies kann eigentlich nich funktionieren (Mega644 4k Ram).

Wieso spuckt der Compilieren keinen Fehler aus?

//PS
buffer[5000] wird nicht weg Optimiert.

von Karl H. (kbuchegg)


Lesenswert?

Tim wrote:

> Wieso spuckt der Compilieren keinen Fehler aus?

Weil ein Compiler nun mal kein Rundumsorglos Packet ist und es im 
Allgemeinen ziemlich schwierig ist, den Speicherverbrauch eines 
Programmes zur Laufzeit vorherzusagen. Was ist dir lieber: Wenn der 
Compiler eine Zahl oder eine Warnung ausspuckt, auf die du dich nicht 
verlassen kannst, oder wenn er dies nicht tut und dich daher zwingt 
selbst zu überlegen was du tust.

Wie gross ist der Speicherverbrauch in folgendem Programm
1
void foo()
2
{
3
  char bar[5000];
4
}
5
6
int main()
7
{
8
}

die Allokierung in foo ist völlig irrelevant, da foo ja nie aufgerufen 
wird.

was ist hier?
1
void foo( int i )
2
{
3
  char bar[100];
4
5
  if( i == 0 )
6
    return;
7
8
  foo( i - 1 );
9
}
10
11
int main()
12
{
13
  foo( 5 );
14
}

wie gross ist jetzt der Speicherverbrauch zur Laufzeit für lokale 
Variablen?
(Es werden so um die 550 Bytes sein, Stackverbrauch für Returnadressen 
nicht mitgerechnet)
Änderst du den Aufruf in main auf  foo( 100 ), dann wird auch das deinen 
Mega32 sprengen.

von P. S. (Gast)


Lesenswert?

Ueberlege mal, was fuer ein Aufwand das waere, sowas in gcc einzubauen. 
Die meisten Zielplatformen haben kein festes Speicherlimit, selbst bei 
den AVRs nicht - es gibt ja auch welche, mit externem Speicherinterface. 
Und selbst wenn das nun drin waere, wuerde es nur in den raren, 
offensichtlichen Faellen anschlagen koennen.

von Benedikt K. (benedikt)


Lesenswert?

Tim wrote:

> Wieso spuckt der Compilieren keinen Fehler aus?

Das Array landet auf dem Stack und da prüft der Compiler nicht, ob noch 
genügend Platz vorhanden ist, denn was soll er machen wenn an sich zwar 
genügend Platz im µC ist, aber dieser bereits durch andere Funktionen 
belegt ist (was sich meist erst zur Laufzeit rausstellt)? Er kann nicht 
einfach die Funktion nicht aufrufen.
Eine Möglichkeit wäre ein Stack Overflow Trap (quasi eine Art Interrupt) 
in dem z.B. die Software kontrolliert neu gestartet werden kann.
Solch einer existier auch bei etlichen größeren Controllern und z.B. bei 
den dsPICs. Sowas ist sehr hilfreich bei der Softwareentwicklung.

von Tim (Gast)


Lesenswert?

mh ok danke

mal noch eine frage

Wenn eine Funktion verlassen wird, wird doch der Speicher für die 
Variablen wieder freigegeben oder?

Kann ich jetzt z.b. 3 Funktionen mit einer Buffer größe von 3000 
anlegen? Oder nur 3 Funktionen mit eine buffer größe von 1000?

von Benedikt K. (benedikt)


Lesenswert?

Tim wrote:

> Wenn eine Funktion verlassen wird, wird doch der Speicher für die
> Variablen wieder freigegeben oder?

Ja.

> Kann ich jetzt z.b. 3 Funktionen mit einer Buffer größe von 3000
> anlegen?

Ja. Allerdings nur wenn die nie gleichzeitig laufen. Die eine darf also 
nicht die andere aufrufen usw.

von Karl H. (kbuchegg)


Lesenswert?

Tim wrote:
> mh ok danke
>
> mal noch eine frage
>
> Wenn eine Funktion verlassen wird, wird doch der Speicher für die
> Variablen wieder freigegeben oder?

Richtig.

> Kann ich jetzt z.b. 3 Funktionen mit einer Buffer größe von 3000
> anlegen? Oder nur 3 Funktionen mit eine buffer größe von 1000?

Das kann man so nicht generell sagen.
Das hängt auch davon ab, wie die Aufrufschachtelung ist, welche und 
wieviele Variablen beim Aufrufer angelegt wurden etc.

von Tim (Gast)


Lesenswert?

Danke

Wenn ich jetzt dieses Beispiel ausführen würde:
1
void test1(void)
2
{
3
 unsigned char buffer[3000];
4
}
5
void test2(void)
6
{
7
 unsigned char buffer[3000];
8
}
9
int main(void)
10
{
11
 test1();
12
 test2();
13
}

Dann würde das doch Funktionieren?

Jetzt meine Frage
Beim Compilieren (Linken) werden doch die variablen in Adressen 
aufgelöst? Da müssten ja in test1 und test2 die variablen buffer die 
gleiche adresse haben? Woher weis der Linken das dies Funktionen wie 
verschachtet aufgerufen werden?

von Karl H. (kbuchegg)


Lesenswert?

Tim wrote:

> Dann würde das doch Funktionieren?

Kann funktionieren.
Dein Programm benötigt zu keinem Zeitpunkt mehr als etwas über 3000 
Bytes. Du hast 4KB zur Verfügung. Passt also

> Jetzt meine Frage
> Beim Compilieren (Linken) werden doch die variablen in Adressen
> aufgelöst? Da müssten ja in test1 und test2 die variablen buffer die
> gleiche adresse haben? Woher weis der Linken das dies Funktionen wie
> verschachtet aufgerufen werden?

Du missverstehst da etwas. Diese Variablen werden nicht vom Compiler 
erzeugt, sondern sie werden erst zur Laufzeit auf dem Stack erzeugt. 
Daher interessiert sich auch der Compiler bzw. Linker nicht dafür.

Der Ablauf ist so:

* Dein Programm startet. Der Stack ist leer (4KB minus ein paar 
zerquetschte sind frei
* Funktion test1 wird aufgerufen
* Von den vorhendenen 4KB werden 3000 Bytes für buffer abgestellt
* test1 arbeitet
* test1 wird verlassen
* Die Speicherallokierung für buffer wird rückgängig gemacht, der Stack 
ist wieder komplett frei, die 4KB sind wieder völlig frei (minus ein 
paar Zerquetschte für Verwaltungsinfo)
* test2 wird aufgerufen
* Von den vorhandenen 4KB werden 3000 Bytes für eine neue Variable 
buffer bereitgestellt
* test2 arbeitet
* test2 wird verlassen, die Speicherreservierung wird wieder rückgängig 
gemacht.


Zu keinem Zeitpunkt hat dein Programm mehr als die 3000 Bytes in 
Beschlag (minus ein paar Zerquetschte für Verwaltungsinfo)

von Benedikt K. (benedikt)


Lesenswert?

Tim wrote:
> Wenn ich jetzt dieses Beispiel ausführen würde:
> Dann würde das doch Funktionieren?

Ja.

> Beim Compilieren (Linken) werden doch die variablen in Adressen
> aufgelöst?

Nur bei globalen und statischen Variablen.
Lokale Variablen landen zur Laufzeit irgendwo auf dem Stack, je nachdem 
wo gerade der Stackpointer steht.

Beitrag "StackViewer (RAM Rechner) für WinAVR"
Damit kannst du das Ergebnis nachprüfen: Die Software sollte dann 
anzeigen, wieviel RAM der Code tatsächlich belegt.

von Tim (Gast)


Lesenswert?

Ich Danke euch!! Das hat mir echt weitergeholfen.

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.