Forum: Compiler & IDEs Probleme mit __builtin_alloc


von Tobi (Gast)


Lesenswert?

Hallo zusammen,

ich habe nach langem Probieren nun endlich herausgefunden, dass ich 
Probleme bekomme wenn ich in einer Funktion __builtin_alloc aufrufe um 
Speicherplatz zu allokieren und im weiteren Funktionsverlauf eine 
weitere Funktion aufrufe die ebenfalls Speicher über dieses Macro 
allokiert.

Wenn ich in zweiter Funktion dann den Speicherplatz beschreiben will 
funktionuert das aus irgend einem Grudn nicht bzw. ich lese nur mist aus 
diesem Speicherbereich

Liegt das irgendwie daran dass in erster Funktion der reservierte 
Speicher erst wieder nach seinem return freigegeben wird und solange 
nicht ein zweites mal dieses macro aufgerufen werden kann? Wenn ja, wie 
löse ich das dann??


Folgendes funktioniert
1
void function1()
2
{
3
 function2();
4
 __builtin_alloc();
5
6
...
7
8
return;
9
}
10
11
void function2()
12
{
13
 uint8 * speicher = __builtin_alloc();
14
 <"speicher" mit Daten belegen>     
15
16
 <"speicher"-Daten über LAN senden >    <--------- klappt
17
 ...
18
 return;
19
}

und folgendes nicht
1
void function1()
2
{
3
 __builtin_alloc();
4
 function2();         <------------ nur das hier ändert sich
5
6
...
7
8
return;
9
}
10
11
void function2()
12
{
13
 uint8 * speicher = __builtin_alloc();
14
 <"speicher" mit Daten belegen>
15
16
<"speicher"-Daten über LAN senden >    <--------- klappt nicht
17
 ...
18
 return;
19
}

Hat dafür jemand eine Erklärung? Ich komm hier nicht weiter..

Vielen Dank
Tobi

von Karl H. (kbuchegg)


Lesenswert?

Tobi schrieb:

> Liegt das irgendwie daran dass in erster Funktion der reservierte
> Speicher erst wieder nach seinem return freigegeben wird und solange
> nicht ein zweites mal dieses macro aufgerufen werden kann?

Aufrufen kannst du es schon.
Aber der Speicher wird ja erst mit Ende der Funktion freigegeben.

D.h. du hast in deinem ersten Beispiel immer nur 1 Speicheranforderung 
aktiv, während im zweiten Beispiel beide Speicheranforderungen in den 
Speicher passen müssen. Und höchst wahrscheinlich hast du diesen 
Speicher für beide Anforderungen gleichzeitig ganz einfach nicht mehr.

von Tobi (Gast)


Lesenswert?

HAllo Karl Heinz,

Danke für deine antwort.

Ja, so wie es scheint werden meine zuletzt angelegten variablen aus dem 
stack durch den alloc im heap überschrieben.
Kann doch sein oder?

Nur wie bekomme ich das am besten in den griff? Als die überwachung 
solcher Fälle.

Jemand nen tip?

Grüsse
Tobi

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Tobi schrieb:

> ich habe nach langem Probieren nun endlich herausgefunden, dass ich
> Probleme bekomme wenn ich in einer Funktion __builtin_alloc aufrufe um
> Speicherplatz zu allokieren

Das ist nicht verwunderlich.  Funktionen, die mit zwei Unterstrichen
beginnen, sind reserviert, und diese darfst du nur benutzen, wenn dir
eine Doku dazu irgendwelche Versprechungen macht.  Für eine Funktion
namens __builtin__alloc finde ich da beim GCC nichts.

Falls du __builtin_alloca meinst, dann ist zumindest deren Verwendung
fehlerhaft, denn diese Funktion braucht ein Argument.  C99 hat einige
Erweiterungen gegenüber C89, die die Benutzung von alloca in vielen
Fällen überflüssig machen, vielleicht postest du dein Problem ja mal
von Beginn an, statt einen halbfertigen (und vermutlich nicht korrekt
abgetippten) Lösungsansatz zu präsentieren, den dann andere für dich
entwuseln sollen ...

von Tobi (Gast)


Lesenswert?

hallo,

klar meine ich __builtin_alloca.

Der Code war auch nur schematisch. Ich dachte das wäre klar was gemeint 
ist.

Ausserdem gehts mir auch vielmehr um eine Überwachung des Stack und 
Heaps das diese sich nicht überschneiden und einen Tip wie man da 
vorzugehen hat.

Danke.
Tobi

von Karl H. (kbuchegg)


Lesenswert?

Tobi schrieb:
> hallo,
>
> klar meine ich __builtin_alloca.
>
> Der Code war auch nur schematisch. Ich dachte das wäre klar was gemeint
> ist.

Das ist nie eine gute Idee.

Du solltest dir dasselbe angewöhnen, wie wir auch:
Sobald es um Fehler geht, gilt so gut wie nichts mehr als gesichert. 
Alles, aber auch wirklich alles, kann und wird erst mal in Frage 
gestellt, bis das Gegenteil erwiesen ist. Und raten, bzw darauf 
vertrauen, dass die anderen schon wissen was gemeint ist ... das ist 
meistens eine ganz schlechte Idee.

> Ausserdem gehts mir auch vielmehr um eine Überwachung des Stack und
> Heaps das diese sich nicht überschneiden und einen Tip wie man da
> vorzugehen hat.

Es gibt keinen Tip.
Genau das ist einer der Gründe, warum auf kleinen µC wie den AVR mit 
ihrem relativ wenigem Speicher dynamische Allokierung keine so gute Idee 
ist.

Allokierst du alles statisch und mit fix vorgegebenen Größen, dann 
kriegst du am Ende vom Compiler eine Summary, in der steht, wieviel 
Speicher deine Variablen insgesammt verbrauchen. Und dann kann man schon 
ganz gut abschätzen: hab ich Funktionen mit vielen lokalen Variablen, 
dann brauch ich etwas mehr Stack Speicher, hab ich das nicht, dann kann 
ich mit statisch allokierten Variablen den Speicher bis auf 90 oder 95% 
anfüllen, ohne dass sich die beiden in die Quere kommen.

Aber mit dynamischen Speicherallokierung kannst du dann gar nichts mehr 
sagen, weil du im Vorfeld nicht weißt wieviel das sein wird. Ausser 
natürlich wenns kracht. Dann weißt du, das es zuviel war.

Ausserdem bringt dir dynamisch allokieren auf diesen kleinen µC nichts. 
Denn: Du hast ja deswegen auch nicht mehr Speicher zur Verfügung. Wenn 
er aus ist, dann ist er aus.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Tobi schrieb:
> Ausserdem gehts mir auch vielmehr um eine Überwachung des Stack und
> Heaps das diese sich nicht überschneiden

Ich seh' da keinen "Heap".

von Tobi (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Du solltest dir dasselbe angewöhnen, wie wir auch:
> Sobald es um Fehler geht, gilt so gut wie nichts mehr als gesichert.
> Alles, aber auch wirklich alles, kann und wird erst mal in Frage
> gestellt, bis das Gegenteil erwiesen ist. Und raten, bzw darauf
> vertrauen, dass die anderen schon wissen was gemeint ist ... das ist
> meistens eine ganz schlechte Idee.

ok ihr habt ja recht. Ich werde mich bessern. :)

...

Das ganze Speichermanagement Thema ist für mich in der Praxis noch recht 
neu, da man auf einem normalen Rechner sich nicht so sehr drum kümmern 
muss.
Ich lerne was das betrifft dies erst mit dem µC so richtig.

Deshalb hast du mir mit deinem Beitrag Karl Heinz schon sehr weiter 
geholfen. Ich hatte das Problem, dass ich schon bei wenigen zusätzlichen 
Variablen schon Abstürze hatte, jedoch der Compiler unter 50% meldete.
Jetzt habe ich den Speicher einmal fest allokiert.. und siehe da 96% 
voll.
So langsam entwickle ich dafür ein Gefühl! Danke euch!

Jedoch bin ich mit deiner Aussage Karl Heinz nicht so ganz einverstanden 
dass man durch dynamische Allokierung mehr Speicherplatz hat.
__builtin_alloca bietet ja gerade den Vorteil das man in der jeweiligen 
Funktion in der man sie verwendet zwar den Speicher benutzt den man auch 
bei statischer allokierung verwenden würde, jedoch in anderen Funktionen 
auch nochmal Speicher allokieren kann. Nur eben darf dies nicht 
gleichzeitig (in einer Unterfunktion) passieren sonst kracht es 
natürlich

Ich denke ich werde nun trotzdem die statische Allokierung vorziehen da 
man einfach sicherer fährt.

@ Jörg: wieso keinen Heap? Auch beim ATMega wächst doch ein Stack 
entgegen einem Heap?!?

MfG Tobi

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Tobi schrieb:

> @ Jörg: wieso keinen Heap?

Weil du kein malloc() benutzt.

von (prx) A. K. (prx)


Lesenswert?

__builtin_alloca() implementiert die Funktion alloca().
Verwendet werden sollte allenfalls alloca().

von Tobi (Gast)


Lesenswert?

ist ja schön das ihr euch die Zeit nehmt und euer Wissen raushaut aber 
eine kurze Erklärung wäre vielleicht ganz hilfreich damit es auch unter 
den Unwissenden die Lampe zum leuchten bringt :)

Tobi

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

malloc und calloc geben Speicher vom Heap, der recht umständlich über 
eine verkettete Liste verwaltet wird. Auf einem kleinen µC ist das idR 
der Overkill und auch garnicht nötig.

Das alloca-Zeug schafft Platz auf dem Stapel. In GNU-C geht auch sowas:
1
void foo (size_t n)
2
{
3
   int feld[n];
4
   ...
5
}

Was lesbarer und intuitiver ist. feld hat den gleichen 
Gültigkeitsbereich wie eine foo-lokale Variable, das heßt feld ist 
innerhalb von foo (und damit auch allen Unterfunktionen) gültig, aber 
nicht ausserhalb von foo, genauso wie
1
void bar (void*);
2
void * foo ()
3
{ 
4
   int i;
5
   bar (&i);
6
7
   return &i;
8
}
auf i von bar zugegriffen werden kann, aber &i aus foo zurückzugeben ist 
ein Fehler, denn bei Verwendung der Adresse knallts (und wenn nicht, hat 
man lediglich Glück gehabt).

Übrigens sollte man auf kleinen µC auch vermeiden, Adressen von lokalen 
Variablen oder Funktonsparametern zu nehmen, weil das zu ungünstigem 
Code führt.

Für den Fall, daß kein malloc/calloc verwendet wird, hatte ich mal ne 
kleine Routine geschrieben, die den Speicherverbrauch zur Laufzeit 
abschätzt (AVR)

http://www.rn-wissen.de/index.php/Speicherverbrauch_bestimmen_mit_avr-gcc

Je nach Architektur/Compiler kann man was ähnliches machen, aber sowas 
ist stark systemabhängig.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Johann L. schrieb:

> Das alloca-Zeug schafft Platz auf dem Stapel. In GNU-C geht auch sowas:
1
void foo (size_t n)
2
{
3
   int feld[n];
4
   ...
5
}

Das ist seit C99 auch offiziell im Standard (6.7.5.2 Array declarators,
Absatz 4).  Ja, ich würde es auch alloca() (welches nicht standardi-
siert ist) vorziehen, soweit möglich.

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.