2012-06-18 10 views
6

Beim Schreiben von C++ - Code habe ich gelernt, dass die Verwendung des Stapels zum Speichern von Speicher eine gute Idee ist.Erkennen, dass der Stapel voll ist

Aber vor kurzem ich in ein Problem lief:

hatte ich ein Experiment, das Code hatte, die wie folgt aussah:

void fun(const unsigned int N) { 
    float data_1[N*N]; 
    float data_2[N*N]; 

    /* Do magic */ 
} 

Der Code exploted mit einem seqmentation Fehler zufällig, und ich hatte keine Ahnung, Warum.

Es stellte sich heraus, dass ich versuchte, Dinge zu speichern, die zu groß auf meinem Stapel waren, gibt es eine Möglichkeit, dies zu erkennen? Oder zumindest erkennen, dass es schief gelaufen ist? Diese

+1

Ist Ihre Frage zu C oder C++? –

+0

Ich denke, Sie können ein Array auf dem Stack nicht mit Variablen in C/C++ initialisieren! –

+0

Wie groß sind Ihre Daten? Ich schlage vor, dass Sie 100KB + Daten im Heap mit malloc/free speichern. (Andere Leute können vorschlagen, eine noch niedrigere Grenze) – nhahtdh

Antwort

5
float data_1[N*N]; 
float data_2[N*N]; 

sind variable Länge Arrays (VLA), wie N kein konstanter Ausdruck ist. Der Parameter const- im Parameter stellt nur sicher, dass N schreibgeschützt ist. Es teilt dem Compiler nicht mit, dass N ein konstanter Ausdruck ist.

VLAs sind nur in C99 erlaubt; in anderen Versionen von C und allen Versionen von C++ sind sie nicht erlaubt. Einige Compiler bieten jedoch VLA als Compiler-Erweiterung. Wenn Sie mit GCC kompilieren, versuchen Sie es mit -pedantic Option, es wird Ihnen sagen, es ist nicht erlaubt. Jetzt

, warum Ihr Programm gibt segfault, wahrscheinlich wegen der stack-overflow aufgrund großer Wert von N * N:


Erwägen Sie die Verwendung std::vector als:

#include <vector> 

void fun(const unsigned int N) 
{ 
    std::vector<float> data_1(N*N); 
    std::vector<float> data_2(N*N); 

    //your code 
} 
+0

Ich habe die const zum Questing hinzugefügt :-) –

+2

const Funktionsparameter ändert hier nichts, der Wert ist nur zur Laufzeit bekannt, so dass Sie immer noch auf eine Compiler-Erweiterung angewiesen sind. Egal ob C oder C++, der Punkt ist - wenn Sie viel Speicher benötigen, verwenden Sie stattdessen heap (über 'std :: vector'). – Kos

+1

@MartinKristiansen: Ich habe meine Antwort bearbeitet und gesagt: "Die Konstante im Parameter stellt nur sicher, dass" N "schreibgeschützt ist. Es sagt dem Compiler nicht, dass' N' ein konstanter Ausdruck ist. "* – Nawaz

1

Versuchen Sie stattdessen Funktionen wie malloc. Es wird NULL explizit zurückgeben, wenn es einen Speicherblock der angeforderten Größe nicht finden konnte.

Natürlich, in diesem Fall vergessen Sie nicht, diesen Speicher am Ende der Funktion zu befreien, nachdem Sie fertig sind.

Sie können auch die Einstellungen Ihres Compilers überprüfen, mit welcher Stapelspeicherbegrenzung die Binärdateien generiert werden.

+2

Das ist nicht was er fragt. –

+0

Malloc hilft - und malloc war meine Lösung, aber das bedeutet, dass ich jetzt einen Zeiger habe, den ich im Auge behalten muss. –

+0

Ja, das stimmt zwar mit dem Zeiger, aber da es sich innerhalb einer einzelnen Funktion befindet, gibt es kein Problem damit. Fügen Sie am Ende der Funktion –

1

Einer der Gründe, warum Leute sagen, dass es besser ist, Stack anstelle von Heapspeicher zu verwenden, kann daran liegen, dass Variablen, die auf dem Stack zugewiesen sind, automatisch herauskommen, wenn Sie den Hauptteil der Funktion verlassen. Um große Informationsblöcke zu speichern, werden normalerweise Heap-Speicher und andere Datenstrukturen wie verknüpfte Listen oder Bäume verwendet. Auch Speicher, die auf dem Stapel zugeordnet sind, sind begrenzt und viel weniger, als Sie im Heap-Speicher reservieren können. Ich denke, es ist besser, die Speicherzuweisung zu verwalten und vorsichtiger zu veröffentlichen, anstatt zu versuchen, einen Stapel zum Speichern von großen Daten zu verwenden.

Sie können Framework verwenden, das Ihre Speicherzuordnungen verwaltet. Außerdem können Sie mit VDL Ihre Speicherlecks und Speicher überprüfen, die nicht freigegeben sind.

+0

Das ist mein Punkt, ich habe den Code geschrieben, um "meistens" auf kleinen 'N's verwendet zu werden, aber hin und wieder wird ein großes N sich anfühlen. –

1

Gibt es eine Möglichkeit, dies zu erkennen?

Nein, im Allgemeinen.

Stapelgröße ist plattformabhängig.In der Regel entscheidet das Betriebssystem über die Größe des Stapels. So können Sie Ihr Betriebssystem überprüfen (ulimit -s auf Linux), um zu sehen, wie viel Stapelspeicher es für Ihr Programm reserviert.

Wenn Ihr Compiler unterstützt stackavail() dann können Sie es überprüfen. Es ist besser, in Situationen, in denen Sie nicht sicher sind, ob Sie das Stapellimit überschreiten, auf Heapspeicher zuzugreifen.

+0

aber hier ist die Sache, ich entwickelte auf einer Linux-Box mit ein paar High-End-Hardware und verbrachte viel Zeit herauszufinden, warum mein Code nicht auf meinem MacBookPro lief. Die Linux-Box hatte kein Problem mit einem 4-MB-Stack-Alloc, aber die OSX-Box mochte es nicht. –

2

Es ist extrem schwierig zu erkennen, dass der Stapel voll und überhaupt nicht tragbar ist. Eines der größten Probleme ist, dass Stack-Frames von variabler Größe sind (besonders bei Arrays mit variabler Länge, bei denen es sich eigentlich nur um eine Standardmethode handelt, die die Benutzer zuvor mit alloca() gemacht haben). Daher können Sie keine einfachen Proxies wie den Anzahl der Stapelrahmen.

Eine der einfachsten Methoden, die meist tragbar ist, eine Variable zu setzen (wahrscheinlich vom Typ char so dass ein Zeiger darauf a char*) bei einer bekannten Tiefe in dem Stapel und dann den Abstand von derjenigen zeigen Sie auf eine Variable (des gleichen Typs) im aktuellen Stapelrahmen durch einfache Zeigerarithmetik. Fügen Sie eine Schätzung dazu hinzu, wie viel Speicherplatz Sie zuweisen möchten, und Sie können gut abschätzen, ob der Stack Sie in die Luft jagen wird. Die Probleme dabei sind, dass Sie nicht wissen, in welche Richtung der Stapel wächst (nein, sie wachsen nicht alle in die gleiche Richtung!) Und die Größe des Stapelraums zu berechnen, ist selbst ziemlich unordentlich (Sie können versuche Dinge wie Systemgrenzen, aber sie sind wirklich ziemlich peinlich). Plus der Hackfaktor ist sehr hoch.

Der andere Trick, den ich auf 32-Bit-Windows nur verwendet gesehen habe war alloca() ausreichend Platz, um zu versuchen und das System Ausnahme zu behandeln, wenn es nicht genügend Platz war auftreten würde.

int have_enough_stack_space(void) { 
    int enough_space = 0; 

    __try {   /* Yes, that's got a double-underscore. */ 
     alloca(SOME_VALUE_THAT_MEANS_ENOUGH_SPACE); 
     enough_space = 1; 
    } __except (EXCEPTION_EXECUTE_HANDLER) {} 

    return enough_space; 
} 

Dieser Code sehr nicht tragbar ist (zum Beispiel zählt nicht auf es auf 64-Bit-Windows-Betrieb) und Gebäude mit älteren gcc erfordert einige bösen Inline-Assembler statt! Die strukturierte Ausnahmebehandlung (von der dies Gebrauch macht) gehört zu den schwärzesten Schwarzkünsten unter Windows. (Und nicht return aus dem __try Konstrukt.)

+0

Dies alles basierte auf dem Stack-Prüfcode, der in der Implementierung von Tcl 8.4 war; Der Code mit SEH-Verwendung wurde in 8.5 in die erste zeigerbasierte Methode konvertiert und in 8.6 (die eine "stackless" -Modellimplementierung verwendet und daher diese Art von Ding nicht so sehr benötigt) vollständig gelöscht. –

+0

BTW, das Speichern der Arrays variabler Größe auf dem Heap wird viel zuverlässiger sein, besonders wenn Sie RAII-Techniken verwenden können, um eine zuverlässige Löschung zu gewährleisten. –