alloca()
ordnet Speicher eher auf dem Stapel als auf dem Heap zu, wie im Fall von malloc()
. Wenn ich von der Routine zurückkehre, wird der Speicher freigegeben. Tatsächlich löst dies mein Problem, dynamisch zugewiesenen Speicher freizugeben. Die Freigabe des zugewiesenen Speichers bereitet malloc()
große Kopfschmerzen und führt, wenn sie irgendwie übersehen wird, zu allen möglichen Speicherproblemen.
Warum wird von der Verwendung alloca()
trotz der oben genannten Merkmale abgeraten?
free
(was offensichtlich von Vorteil ist), die Nicht-Inline-Fähigkeit (offensichtlich sind die Heap-Zuweisungen sehr viel schwerer) usw. Der einzige Grund, dies zu vermeiden,alloca
sind große Größen. Das heißt, Tonnen von Stapelspeicher zu verschwenden ist keine gute Idee, und Sie haben die Möglichkeit eines Stapelüberlaufs. Wenn dies der Fall ist, sollten Siemalloca
/freea
alloca
ist, dass der Stapel nicht wie der Heap fragmentiert werden kann. Dies könnte sich für harte Echtzeitanwendungen im Run-Forever-Stil oder sogar für sicherheitskritische Anwendungen als nützlich erweisen, da die WCRU dann statisch analysiert werden kann, ohne auf benutzerdefinierte Speicherpools mit eigenen Problemen zurückzugreifen (keine zeitliche Lokalität, suboptimale Ressource) verwenden).Antworten:
Die Antwort ist genau dort auf der
man
Seite (zumindest unter Linux ):Was nicht heißt, dass es niemals verwendet werden sollte. Eines der OSS-Projekte, an denen ich arbeite, verwendet es ausgiebig, und solange Sie es nicht missbrauchen (mit
alloca
riesigen Werten), ist es in Ordnung. Sobald Sie die Marke "einige hundert Bytes" überschritten haben, ist es Zeitmalloc
, stattdessen "Freunde" zu verwenden. Möglicherweise erhalten Sie immer noch Zuordnungsfehler, aber zumindest haben Sie einige Hinweise auf den Fehler, anstatt nur den Stapel auszublasen.quelle
alloca()
haben, nicht die gleiche Angst vor lokalen Arrays oder Rekursionen haben (tatsächlich werden viele Leute, die nach unten schreien,alloca()
die Rekursion loben, weil sie "elegant aussieht"). . Ich stimme Shauns Rat zu ("alloca () ist in Ordnung für kleine Allokationen"), aber ich bin nicht einverstanden mit der Einstellung, die alloca () als einzigartig böse unter den 3 einstuft - sie sind gleichermaßen gefährlich!Einer der denkwürdigsten Fehler, den ich hatte, war die Verwendung einer Inline-Funktion, die verwendet wurde
alloca
. Es manifestierte sich als Stapelüberlauf (weil es auf dem Stapel zugeordnet ist) an zufälligen Punkten der Programmausführung.In der Header-Datei:
In der Implementierungsdatei:
Was also geschah, war die Compiler-Inline-
DoSomething
Funktion, und alle Stapelzuweisungen erfolgten innerhalb derProcess()
Funktion und sprengten so den Stapel in die Luft . Zu meiner Verteidigung (und ich war nicht derjenige, der das Problem gefunden hat; ich musste zu einem der leitenden Entwickler gehen und weinen, wenn ich es nicht beheben konnte), es war nicht geradealloca
, es war eine der ATL-String-Konvertierungen Makros.Die Lektion lautet also: Verwenden
alloca
Sie sie nicht in Funktionen, von denen Sie glauben, dass sie inline sind.quelle
Alte Frage, aber niemand erwähnte, dass es durch Arrays variabler Länge ersetzt werden sollte.
anstatt
Es ist im Standard C99 enthalten und existierte in vielen Compilern als Compiler-Erweiterung.
quelle
alloca () ist sehr nützlich, wenn Sie eine lokale Standardvariable nicht verwenden können, da ihre Größe zur Laufzeit bestimmt werden müsste und Sie absolut garantieren können, dass der Zeiger, den Sie von alloca () erhalten, NIEMALS verwendet wird, nachdem diese Funktion zurückgegeben wurde .
Sie können ziemlich sicher sein, wenn Sie
Die wirkliche Gefahr besteht darin, dass irgendwann später jemand anderes gegen diese Bedingungen verstößt. In diesem Sinne ist es großartig, Puffer an Funktionen zu übergeben, die Text in sie formatieren :)
quelle
alloca()
Speicher zu, der bis zum Ende der Funktion dauert. Dies bedeutet, dass es anscheinend keine einfache und bequeme Übersetzung in VLA von gibtf() { char *p; if (c) { /* compute x */ p = alloca(x); } else { p = 0; } /* use p */ }
. Wenn Sie der Meinung sind, dass es möglich ist, Verwendungen vonalloca
in Verwendungen von VLA automatisch zu übersetzen, aber mehr als einen Kommentar benötigen, um zu beschreiben, wie, kann ich dies zu einer Frage machen.Wie in diesem Newsgroup-Beitrag erwähnt , gibt es einige Gründe, warum die Verwendung
alloca
als schwierig und gefährlich angesehen werden kann:alloca
.alloca
unterschiedlich, sodass die Portabilität auch zwischen Compilern, die es unterstützen, nicht garantiert ist.quelle
alloca()
, separate Register zum Halten des Stapelzeigers und des Rahmenzeigers erfordert. Auf x86-CPUs> = 386 kann der StapelzeigerESP
für beide verwendet werden, um freizugebenEBP
- sofern nichtalloca()
verwendet.f(42, alloca(10), 43);
Absturz auftreten kann, da möglicherweise der Stapelzeiger angepasst wird,alloca()
nachdem mindestens eines der Argumente darauf gedrückt wurde.Ein Problem ist, dass es nicht Standard ist, obwohl es weitgehend unterstützt wird. Wenn andere Dinge gleich sind, würde ich immer eine Standardfunktion anstelle einer gemeinsamen Compiler-Erweiterung verwenden.
quelle
Ich sehe keinen solchen Konsens. Viele starke Profis; ein paar Nachteile:
while
oder einerfor
Schleife) oder in mehreren Bereichen sammelt sich der Speicher pro Iteration / Bereich an und wird erst freigegeben, wenn die Funktion beendet wird. Dies steht im Gegensatz zu normalen Variablen, die im Bereich einer Steuerungsstruktur definiert sind (zfor {int i = 0; i < 2; ++i) { X }
würde sich ansammelnalloca
bei X angeforderten Speicher , aber der Speicher für ein Array mit fester Größe würde pro Iteration recycelt).inline
funktionieren normalerweise nicht mit aufrufenden Funktionen.alloca
Wenn Sie sie jedoch erzwingen,alloca
geschieht dies im Kontext des Aufrufers (dh der Stapel wird erst freigegeben, wenn der Aufrufer zurückkehrt).alloca
von einer nicht portablen Funktion / einem nicht portierbaren Hack auf eine standardisierte Erweiterung umgestellt, aber eine gewisse negative Wahrnehmung kann bestehen bleibenmalloc
die explizite Steuerungmalloc
Wenn Sie diese verwenden müssen , müssen Sie über die Freigabe nachdenken. Wenn dies über eine Wrapper-Funktion (z. B.WonderfulObject_DestructorFree(ptr)
) verwaltet wird, bietet die Funktion einen Punkt für Implementierungsbereinigungsvorgänge (wie das Schließen von Dateideskriptoren, das Freigeben interner Zeiger oder das Durchführen einer Protokollierung) ohne explizite Änderungen am Client Code: Manchmal ist es ein schönes Modell, konsequent zu übernehmenWonderfulObject* p = WonderfulObject_AllocConstructor();
- das ist möglich, wenn der "Konstruktor" eine Funktion ist,malloc
die Speicher zurückgibt (da der Speicher zugewiesen bleibt, nachdem die Funktion den zu speichernden Wert zurückgegeben hatp
), aber nicht wenn der "Konstruktor" verwendetalloca
WonderfulObject_AllocConstructor
könnte dies erreichen, aber "Makros sind böse", da sie miteinander und mit Nicht-Makro-Code in Konflikt stehen und unbeabsichtigte Ersetzungen und daraus resultierende schwer zu diagnostizierende Probleme verursachen könnenfree
Vorgänge können von ValGrind, Purify usw. erkannt werden, aber fehlende "Destruktor" -Aufrufe können nicht immer erkannt werden - ein sehr schwacher Vorteil in Bezug auf die Durchsetzung der beabsichtigten Verwendung; Einigealloca()
Implementierungen (z. B. GCCs) verwenden ein Inline-Makro füralloca()
, sodass das Ersetzen einer Diagnosebibliothek zur Speichernutzung zur Laufzeit nicht so möglich ist wie fürmalloc
/realloc
/free
(z. B. Elektrozaun).Ich weiß, dass diese Frage mit C gekennzeichnet ist, aber als C ++ - Programmierer dachte ich, ich würde C ++ verwenden, um den möglichen Nutzen von zu veranschaulichen
alloca
: Der folgende Code (und hier bei ideone ) erstellt einen Vektor, der polymorphe Typen unterschiedlicher Größe verfolgt, denen Stapel zugewiesen sind (mit Lebensdauer an Funktionsrückgabe gebunden) statt Heap zugewiesen.quelle
Alle anderen Antworten sind richtig. Wenn das, was Sie verwenden möchten,
alloca()
jedoch relativ klein ist, denke ich, dass es eine gute Technik ist, die schneller und bequemer ist als die Verwendungmalloc()
oder auf andere Weise.Mit anderen Worten,
alloca( 0x00ffffff )
ist gefährlich und verursacht wahrscheinlich genau so viel Überlauf wie eschar hugeArray[ 0x00ffffff ];
ist. Seien Sie vorsichtig und vernünftig und es wird Ihnen gut gehen.quelle
Viele interessante Antworten auf diese "alte" Frage, sogar einige relativ neue Antworten, aber ich habe keine gefunden, die dies erwähnen ...
Ich fand dieses Zitat in ... OK, ich habe dieses Zitat erfunden. Aber wirklich, denk darüber nach ...
@j_random_hacker ist in seinen Kommentaren unter anderen Antworten sehr richtig: Wenn Sie die Verwendung von
alloca()
übergroßen lokalen Arrays vermeiden, wird Ihr Programm nicht vor Stapelüberläufen sicherer (es sei denn, Ihr Compiler ist alt genug, um das Inlining von Funktionen zu ermöglichen, diealloca()
in diesem Fall verwendet werden sollten Upgrade oder es sei denn, Sie verwendenalloca()
Inside-Loops. In diesem Fall sollten Sie ... keinealloca()
Inside-Loops verwenden.Ich habe an Desktop / Server-Umgebungen und eingebetteten Systemen gearbeitet. Viele eingebettete Systeme verwenden überhaupt keinen Heap (sie verknüpfen ihn nicht einmal), aus Gründen, die die Wahrnehmung einschließen, dass dynamisch zugewiesener Speicher aufgrund des Risikos von Speicherverlusten in einer Anwendung, die niemals funktioniert, böse ist Neustarts über Jahre hinweg oder die vernünftigere Rechtfertigung, dass dynamischer Speicher gefährlich ist, da nicht sicher bekannt sein kann, dass eine Anwendung ihren Heap niemals bis zur Erschöpfung des falschen Speichers fragmentieren wird. Eingebettete Programmierer haben also nur wenige Alternativen.
alloca()
(oder VLAs) sind möglicherweise genau das richtige Werkzeug für den Job.Ich habe immer wieder gesehen, wie ein Programmierer einen vom Stapel zugewiesenen Puffer "groß genug macht, um jeden möglichen Fall zu behandeln". In einem tief verschachtelten Aufrufbaum führt die wiederholte Verwendung dieses (Anti -?) Musters zu einer übertriebenen Stapelverwendung. (Stellen Sie sich einen Anrufbaum mit einer Tiefe von 20 Ebenen vor, bei dem die Funktion auf jeder Ebene aus unterschiedlichen Gründen blind einen Puffer von 1024 Bytes "nur um sicher zu gehen" zu viel zuweist, wenn im Allgemeinen nur 16 oder weniger davon verwendet werden, und dies nur in sehr In seltenen Fällen kann mehr verwendet werden.) Eine Alternative ist die Verwendung
alloca()
oder VLAs und weisen nur so viel Stapelspeicher zu, wie Ihre Funktion benötigt, um eine unnötige Belastung des Stapels zu vermeiden. Wenn eine Funktion im Aufrufbaum eine überdurchschnittliche Zuweisung benötigt, verwenden andere im Aufrufbaum hoffentlich immer noch ihre normalen kleinen Zuordnungen, und die Gesamtnutzung des Anwendungsstapels ist erheblich geringer, als wenn jede Funktion einen lokalen Puffer blind überbelegt hätte .Aber wenn Sie sich entscheiden zu verwenden
alloca()
...Basierend auf anderen Antworten auf dieser Seite scheint es, dass VLAs sicher sein sollten (sie setzen keine Stapelzuweisungen zusammen, wenn sie aus einer Schleife heraus aufgerufen werden), aber wenn Sie sie verwenden
alloca()
, achten Sie darauf , sie nicht innerhalb einer Schleife zu verwenden, und machen Sie Stellen Sie sicher, dass Ihre Funktion nicht eingebunden werden kann, wenn die Möglichkeit besteht, dass sie in der Schleife einer anderen Funktion aufgerufen wird.quelle
alloca()
ist wahr, aber das Gleiche gilt für Speicherlecks mitmalloc()
(warum dann nicht einen GC verwenden? Man könnte argumentieren).alloca()
Bei sorgfältiger Verwendung kann dies sehr nützlich sein, um die Stapelgröße zu verringern.Jeder hat bereits auf die große Sache hingewiesen, die ein potenzielles undefiniertes Verhalten aufgrund eines Stapelüberlaufs ist, aber ich sollte erwähnen, dass die Windows-Umgebung einen großartigen Mechanismus hat, um dies mithilfe von strukturierten Ausnahmen (SEH) und Schutzseiten abzufangen. Da der Stapel nur nach Bedarf wächst, befinden sich diese Schutzseiten in Bereichen, die nicht zugeordnet sind. Wenn Sie ihnen zuweisen (indem Sie den Stapel überlaufen), wird eine Ausnahme ausgelöst.
Sie können diese SEH-Ausnahme abfangen und _resetstkoflw aufrufen, um den Stapel zurückzusetzen und Ihren fröhlichen Weg fortzusetzen. Es ist nicht ideal, aber es ist ein weiterer Mechanismus, um zumindest zu wissen, dass etwas schief gelaufen ist, wenn das Zeug den Fan trifft. * nix könnte etwas Ähnliches haben, das mir nicht bekannt ist.
Ich empfehle, Ihre maximale Zuordnungsgröße zu begrenzen, indem Sie die Zuordnung einwickeln und intern verfolgen. Wenn Sie wirklich hart im Nehmen sind, können Sie einige Bereichswachen an die Spitze Ihrer Funktion setzen, um alle Zuordnungszuordnungen im Funktionsumfang zu verfolgen, und überprüfen Sie dies anhand des für Ihr Projekt maximal zulässigen Betrags.
Zusätzlich dazu, dass Speicherlecks nicht berücksichtigt werden, verursacht die Zuweisung keine Speicherfragmentierung, was ziemlich wichtig ist. Ich denke nicht, dass Alloca eine schlechte Praxis ist, wenn Sie es intelligent verwenden, was im Grunde für alles gilt. :-)
quelle
alloca()
so viel Platz benötigt werden kann, dass der Stapelzeiger auf dem Haufen landet. Damit kann ein Angreifer, der die Größealloca()
und die Daten, die in diesen Puffer gelangen , steuern kann, den Heap überschreiben (was sehr schlecht ist).alloca () ist nett und effizient ... aber es ist auch tief gebrochen.
In den meisten Fällen können Sie es durch lokale Variablen und Hauptgröße ersetzen. Wenn es für große Objekte verwendet wird, ist es normalerweise sicherer, sie auf den Haufen zu legen.
Wenn Sie es wirklich brauchen, können Sie VLA verwenden (kein Vla in C ++, schade). Sie sind in Bezug auf Umfangsverhalten und -konsistenz viel besser als alloca (). Aus meiner Sicht sind VLA eine Art Allokation () die richtig gemacht wurde.
Natürlich ist eine lokale Struktur oder ein lokales Array, das einen Großteil des benötigten Speicherplatzes verwendet, immer noch besser, und wenn Sie nicht über eine so große Heap-Zuordnung mit normalem malloc () verfügen, ist dies wahrscheinlich sinnvoll. Ich sehe keinen vernünftigen Anwendungsfall, in dem Sie wirklich entweder alloca () oder VLA brauchen .
quelle
alloca
erstellt keinen Namen, sondern nur einen Speicherbereich, der eine Lebensdauer hat .alloca
odermalloc
oderfree
direkt. Ich sage Dinge wie{stack|heap}_alloc_{bytes,items,struct,varstruct}
und{stack|heap}_dealloc
. Also,heap_dealloc
ruft einfach anfree
undstack_dealloc
ist ein No-Op. Auf diese Weise können die Stapelzuweisungen leicht auf Heapzuweisungen herabgestuft werden, und die Absichten sind auch klarer.Hier ist der Grund:
Nicht, dass irgendjemand diesen Code schreiben würde, aber das Größenargument, an das Sie übergeben,
alloca
stammt mit ziemlicher Sicherheit von einer Art Eingabe, die böswillig darauf abzielen könnte, Ihr Programm auf soalloca
etwas Großes zu bringen. Wenn die Größe nicht auf Eingaben basiert oder nicht groß sein kann, warum haben Sie dann nicht einfach einen kleinen lokalen Puffer mit fester Größe deklariert?Praktisch jeder Code, der
alloca
und / oder C99 vlas verwendet, weist schwerwiegende Fehler auf, die zu Abstürzen (wenn Sie Glück haben) oder Kompromissen bei Privilegien (wenn Sie nicht so viel Glück haben) führen.quelle
alloca
. Sie sagten, dass fast jeder Code, der ihn verwendet, einen Fehler aufweist, aber ich hatte vor, ihn zu verwenden. Normalerweise würde ich eine solche Behauptung ignorieren, aber kommen Ich schreibe eine virtuelle Maschine und möchte Variablen zuweisen, die aufgrund der enormen Beschleunigung nicht dynamisch, sondern dynamisch aus der Funktion auf dem Stapel entkommen. Gibt es eine Alternative? Ich weiß, dass ich mit Speicherpools in die Nähe kommen kann, aber das ist immer noch nicht so billig. Was würden Sie tun?*0 = 9;
ERSTAUNLICH !!! Ich denke, ich sollte niemals Zeiger verwenden (oder sie zumindest dereferenzieren). Ähm, warte. Ich kann testen, ob es null ist. Hmm. Ich denke, ich kann auch die Größe des Speichers testen, über den ich zuweisen möchtealloca
. Seltsamer Mann. Seltsam.*0=9;
ist nicht gültig C. Um die Größe zu testen, an die Sie übergebenalloca
, testen Sie sie gegen was? Es gibt keine Möglichkeit, das Limit zu kennen, und wenn Sie es nur mit einer winzigen festen bekannten sicheren Größe (z. B. 8 KB) testen möchten, können Sie auch einfach ein Array mit fester Größe auf dem Stapel verwenden.small_constant * log(user_input)
wir wahrscheinlich über genügend Speicher verfügen , wenn die Größe bis dahin begrenzt ist .Ich glaube nicht, dass dies jemand erwähnt hat: Die Verwendung von Alloca in einer Funktion behindert oder deaktiviert einige Optimierungen, die andernfalls in der Funktion angewendet werden könnten, da der Compiler die Größe des Stapelrahmens der Funktion nicht kennen kann.
Eine übliche Optimierung durch C-Compiler besteht beispielsweise darin, die Verwendung des Rahmenzeigers innerhalb einer Funktion zu eliminieren. Stattdessen werden Rahmenzugriffe relativ zum Stapelzeiger vorgenommen. Es gibt also noch ein Register für den allgemeinen Gebrauch. Wenn jedoch innerhalb der Funktion eine Zuordnung aufgerufen wird, ist der Unterschied zwischen sp und fp für einen Teil der Funktion unbekannt, sodass diese Optimierung nicht durchgeführt werden kann.
Angesichts der Seltenheit seiner Verwendung und seines zwielichtigen Status als Standardfunktion deaktivieren Compiler-Designer möglicherweise jede Optimierung, die Probleme mit der Allokation verursachen könnte, wenn mehr als ein wenig Aufwand erforderlich wäre, damit sie mit der Allokation funktioniert.
UPDATE: Da lokale Arrays mit variabler Länge zu C hinzugefügt wurden und diese dem Compiler sehr ähnliche Probleme bei der Codegenerierung wie alloca stellen, sehe ich, dass "Seltenheit der Verwendung und schattiger Status" nicht für den zugrunde liegenden Mechanismus gilt. Ich würde jedoch immer noch vermuten, dass die Verwendung von Alloca oder VLA die Codegenerierung innerhalb einer Funktion, die sie verwendet, beeinträchtigt. Ich würde mich über Feedback von Compiler-Designern freuen.
quelle
Eine Gefahr
alloca
besteht darin, dasslongjmp
es zurückgespult wird.Das heißt, wenn Sie einen Kontext mit
setjmp
, dannalloca
etwas Speicher, dannlongjmp
im Kontext speichern, können Sie denalloca
Speicher verlieren . Der Stapelzeiger befindet sich wieder dort, wo er war, und der Speicher ist nicht mehr reserviert. Wenn Sie eine Funktion aufrufen oder eine anderealloca
ausführen, wird das Original beschädigtalloca
.Um dies zu verdeutlichen, beziehe ich mich hier ausdrücklich auf eine Situation
longjmp
, in der die Funktion, in deralloca
die stattgefunden hat, nicht verlassen wird! Vielmehr speichert eine Funktion den Kontext mitsetjmp
; ordnet dann Speicher zualloca
und schließlich findet ein longjmp zu diesem Kontext statt. Deralloca
Speicher dieser Funktion wird nicht vollständig freigegeben. nur der gesamte Speicher, den es seit dem zugewiesen hatsetjmp
. Natürlich spreche ich von einem beobachteten Verhalten; Von denenalloca
, die ich kenne, ist keine solche Anforderung dokumentiert .Der Fokus in der Dokumentation liegt normalerweise auf dem Konzept, dass der
alloca
Speicher einer Funktionsaktivierung zugeordnet ist , nicht einem Block. dass mehrere Aufrufe vonalloca
nur mehr Stapelspeicher greifen, die alle freigegeben werden, wenn die Funktion beendet wird. Nicht so; Der Speicher ist tatsächlich dem Prozedurkontext zugeordnet. Wenn der Kontext mit wiederhergestellt wirdlongjmp
, ist dies auch der vorherigealloca
Status. Dies ist eine Folge davon, dass das Stapelzeigerregister selbst für die Zuordnung verwendet und (notwendigerweise) im Speicher gespeichert und wiederhergestellt wirdjmp_buf
.Im Übrigen bietet dies, wenn es so funktioniert, einen plausiblen Mechanismus, um den zugewiesenen Speicher absichtlich freizugeben
alloca
.Ich bin darauf als Grundursache eines Fehlers gestoßen.
quelle
longjmp
geht zurück und macht es so, dass das Programm alles vergisst, was im Stapel passiert ist: alle Variablen, Funktionsaufrufe usw. Und esalloca
ist wie ein Array auf dem Stapel, also wird erwartet, dass sie überlastet werden wie alles andere auf dem Stapel.man alloca
gab den folgenden Satz aus: "Da der von alloca () zugewiesene Speicherplatz innerhalb des Stapelrahmens zugewiesen wird, wird dieser Speicherplatz automatisch freigegeben, wenn die Funktionsrückgabe durch einen Aufruf von longjmp (3) oder siglongjmp (3) übersprungen wird." Es ist also dokumentiert, dass der mit zugewiesenealloca
Speicher nach a überlastet wirdlongjmp
.longjmp
. Die Zielfunktion ist noch nicht zurückgekehrt. Es wird getansetjmp
,alloca
und dannlongjmp
. Sielongjmp
können denalloca
Zustand auf das zurückspulen, was er zu dersetjmp
Zeit war. Das heißt, der von bewegte Zeiger hatalloca
das gleiche Problem wie eine lokale Variable, die nicht markiert wurdevolatile
!setjmp
dannalloca
und dannlongjmp
ist es normal,alloca
dass zurückgespult wird. Der springende Punktlongjmp
ist, zu dem Zustand zurückzukehren, in dem gerettet wurdesetjmp
!Ein Ort, an dem
alloca()
es besonders gefährlich ist alsmalloc()
der Kernel - der Kernel eines typischen Betriebssystems hat einen Stapelspeicher mit fester Größe, der in einem seiner Header fest codiert ist. Es ist nicht so flexibel wie der Stapel einer Anwendung. Wenn Sie einen Anrufalloca()
mit einer ungerechtfertigten Größe tätigen, kann der Kernel abstürzen. Bestimmte Compiler warnen vor der Verwendung vonalloca()
(und sogar VLAs) unter bestimmten Optionen, die beim Kompilieren eines Kernel-Codes aktiviert werden sollten. Hier ist es besser, Speicher im Heap zuzuweisen, der nicht durch ein fest codiertes Limit festgelegt ist.quelle
alloca()
ist nicht gefährlicher als inint foo[bar];
dembar
eine willkürliche ganze Zahl ist.Wenn Sie versehentlich über den mit zugewiesenen Block hinaus schreiben
alloca
(z. B. aufgrund eines Pufferüberlaufs), überschreiben Sie die Rücksprungadresse Ihrer Funktion, da sich diese "über" dem Stapel befindet, dh nach Ihrem zugewiesenen Block.Dies hat zwei Konsequenzen:
Das Programm stürzt spektakulär ab und es ist unmöglich zu sagen, warum oder wo es abgestürzt ist (der Stapel wird aufgrund des überschriebenen Frame-Zeigers höchstwahrscheinlich zu einer zufälligen Adresse abgewickelt).
Dies macht den Pufferüberlauf um ein Vielfaches gefährlicher, da ein böswilliger Benutzer eine spezielle Nutzlast erstellen kann, die auf den Stapel gelegt wird und daher ausgeführt werden kann.
Wenn Sie dagegen über einen Block auf dem Heap hinaus schreiben, erhalten Sie "nur" eine Heap-Beschädigung. Das Programm wird wahrscheinlich unerwartet beendet, aber den Stapel ordnungsgemäß abwickeln, wodurch die Wahrscheinlichkeit einer Ausführung von bösartigem Code verringert wird.
quelle
alloca
.alloca
im Vergleich zumalloc
(also kein Puffer mit fester Größe auf dem Stapel).Leider
alloca()
fehlt das wirklich großartige im fast fantastischen tcc. Gcc hatalloca()
.Es sät den Samen seiner eigenen Zerstörung. Mit Rückkehr als Destruktor.
Als
malloc()
ob es einen ungültigen Zeiger auf Fail zurückgibt, der auf modernen Systemen mit einer MMU fehlerhaft ist (und hoffentlich diejenigen ohne neu startet).Im Gegensatz zu automatischen Variablen können Sie die Größe zur Laufzeit angeben.
Es funktioniert gut mit Rekursion. Sie können statische Variablen verwenden, um etwas Ähnliches wie die Schwanzrekursion zu erreichen, und nur wenige andere verwenden, um Informationen an jede Iteration zu übergeben.
Wenn Sie zu tief drücken, ist Ihnen ein Segfault sicher (wenn Sie eine MMU haben).
Beachten Sie, dass dies
malloc()
nicht mehr bietet, da NULL zurückgegeben wird (was bei Zuweisung ebenfalls zu einem Segfault führt), wenn das System nicht über genügend Speicher verfügt. Das heißt, Sie können nur eine Kaution hinterlegen oder einfach versuchen, sie auf irgendeine Weise zuzuweisen.Zur Verwendung verwende
malloc()
ich Globals und weise sie NULL zu. Wenn der Zeiger nicht NULL ist, gebe ich ihn frei, bevor ich ihn benutzemalloc()
.Sie können auch
realloc()
als allgemeinen Fall verwenden, wenn Sie vorhandene Daten kopieren möchten. Sie müssen den Zeiger vorher überprüfen, um herauszufinden, ob Sie nach dem kopieren oder verketten möchtenrealloc()
.3.2.5.2 Vorteile der Zuteilung
quelle
Für Prozesse steht nur eine begrenzte Menge an Stapelspeicherplatz zur Verfügung - weit weniger als der verfügbare Speicherplatz
malloc()
.Durch
alloca()
Ihre Verwendung erhöhen Sie die Wahrscheinlichkeit eines Stapelüberlauffehlers erheblich (wenn Sie Glück haben, oder eines unerklärlichen Absturzes, wenn Sie dies nicht tun).quelle
Nicht sehr hübsch, aber wenn die Leistung wirklich wichtig ist, können Sie Speicherplatz auf dem Stapel vorab zuweisen.
Wenn Sie bereits jetzt die maximale Größe des Speicherblocks haben, den Sie benötigen, und Überlaufprüfungen durchführen möchten, können Sie Folgendes tun:
quelle
Die Allokationsfunktion ist großartig und alle Neinsager verbreiten einfach FUD.
Array und Parray sind genau gleich mit genau den gleichen Risiken. Zu sagen, einer sei besser als der andere, ist eine syntaktische und keine technische Wahl.
Bei der Auswahl von Stapelvariablen im Vergleich zu Heap-Variablen bieten lang laufende Programme, die Stack-over-Heap für Variablen mit In-Scope-Lebensdauer verwenden, viele Vorteile. Sie vermeiden eine Heap-Fragmentierung und Sie können vermeiden, Ihren Prozessspeicher mit nicht verwendetem (unbrauchbarem) Heap-Speicherplatz zu vergrößern. Sie müssen es nicht aufräumen. Sie können die Stapelzuordnung im Prozess steuern.
Warum ist das so schlimm?
quelle
Tatsächlich ist nicht garantiert, dass Alloca den Stapel verwendet. In der Tat reserviert die gcc-2.95-Implementierung von alloca Speicher vom Heap unter Verwendung von malloc selbst. Auch diese Implementierung ist fehlerhaft. Sie kann zu einem Speicherverlust und zu unerwartetem Verhalten führen, wenn Sie sie innerhalb eines Blocks mit einer weiteren Verwendung von goto aufrufen. Nicht, um zu sagen, dass Sie es niemals verwenden sollten, aber manchmal führt Allokation zu mehr Overhead, als es von Ihnen löst.
quelle
alloca
. Wie hätte es den Speicher bereinigt, wennlongjmp
es verwendet wird, um Frames aufzugeben, die dies getan habenalloca
? Wann würde jemand heute gcc 2.95 verwenden?IMHO wird Alloca als schlechte Praxis angesehen, da jeder Angst hat, die Stapelgrößenbeschränkung zu erschöpfen.
Ich habe viel gelernt, indem ich diesen Thread und einige andere Links gelesen habe:
Ich verwende alloca hauptsächlich, um meine einfachen C-Dateien auf msvc und gcc ohne Änderung, C89-Stil, ohne #ifdef _MSC_VER usw. kompilierbar zu machen.
Vielen Dank ! Dieser Thread hat mich dazu gebracht, mich auf dieser Seite anzumelden :)
quelle
Meiner Meinung nach sollte alloca (), sofern verfügbar, nur eingeschränkt verwendet werden. Ähnlich wie bei der Verwendung von "goto" hat eine große Anzahl ansonsten vernünftiger Leute eine starke Abneigung nicht nur gegen die Verwendung von, sondern auch gegen die Existenz von alloca ().
Für die eingebettete Verwendung, bei der die Stapelgröße bekannt ist und durch Konvention und Analyse Grenzen für die Größe der Zuordnung festgelegt werden können und bei der der Compiler nicht zur Unterstützung von C99 + aktualisiert werden kann, ist die Verwendung von alloca () in Ordnung, und das war ich auch bekannt, um es zu verwenden.
Wenn verfügbar, können VLAs einige Vorteile gegenüber alloca () haben: Der Compiler kann Stapelbegrenzungsprüfungen generieren, die den Zugriff außerhalb der Grenzen abfangen, wenn der Zugriff im Array-Stil verwendet wird (ich weiß nicht, ob Compiler dies tun, aber es kann durchgeführt werden), und die Analyse des Codes kann bestimmen, ob die Array-Zugriffsausdrücke ordnungsgemäß begrenzt sind. Beachten Sie, dass in einigen Programmierumgebungen, z. B. in der Automobilindustrie, in medizinischen Geräten und in der Avionik, diese Analyse auch für Arrays mit fester Größe durchgeführt werden muss, sowohl automatisch (auf dem Stapel) als auch statisch (global oder lokal).
Auf Architekturen, die sowohl Daten als auch Rücksprungadressen / Rahmenzeiger auf dem Stapel speichern (soweit ich weiß, sind das alle), kann jede dem Stapel zugewiesene Variable gefährlich sein, da die Adresse der Variablen verwendet werden kann und ungeprüfte Eingabewerte dies möglicherweise zulassen alle Arten von Unheil.
Die Portabilität ist im eingebetteten Bereich weniger wichtig, ist jedoch ein gutes Argument gegen die Verwendung von alloca () außerhalb sorgfältig kontrollierter Umstände.
Außerhalb des eingebetteten Bereichs habe ich alloca () hauptsächlich innerhalb von Protokollierungs- und Formatierungsfunktionen verwendet, um die Effizienz zu steigern, und in einem nicht rekursiven lexikalischen Scanner, in dem temporäre Strukturen (die mit alloca () zugewiesen wurden, während der Tokenisierung und Klassifizierung erstellt werden, und dann eine dauerhafte Objekt (über malloc () zugewiesen) wird gefüllt, bevor die Funktion zurückkehrt. Die Verwendung von alloca () für die kleineren temporären Strukturen reduziert die Fragmentierung erheblich, wenn das persistente Objekt zugewiesen wird.
quelle
Die meisten Antworten hier verfehlen weitgehend den Punkt: Es gibt einen Grund, warum zu verwenden
_alloca()
möglicherweise schlechter ist als das bloße Speichern großer Objekte im Stapel.Der Hauptunterschied zwischen automatischer Speicherung und
_alloca()
letzterer besteht darin, dass letztere unter einem zusätzlichen (schwerwiegenden) Problem leidet: Der zugewiesene Block wird nicht vom Compiler gesteuert , sodass der Compiler ihn nicht optimieren oder recyceln kann.Vergleichen Sie:
mit:
Das Problem mit letzterem sollte offensichtlich sein.
quelle
alloca
(ja, ich sage VLA, weilalloca
es mehr als nur der Schöpfer von Arrays statischer Größe ist)?Ich glaube nicht, dass dies jemand erwähnt hat, aber alloca hat auch einige schwerwiegende Sicherheitsprobleme, die bei malloc nicht unbedingt auftreten müssen (obwohl diese Probleme auch bei stapelbasierten Arrays auftreten, ob dynamisch oder nicht). Da der Speicher auf dem Stapel zugewiesen ist, haben Pufferüberläufe / -unterläufe viel schwerwiegendere Konsequenzen als nur mit malloc.
Insbesondere wird die Rücksprungadresse für eine Funktion auf dem Stapel gespeichert. Wenn dieser Wert beschädigt wird, kann Ihr Code in einen ausführbaren Speicherbereich verschoben werden. Compiler unternehmen große Anstrengungen, um dies zu erschweren (insbesondere durch zufälliges Adresslayout). Dies ist jedoch eindeutig schlimmer als nur ein Stapelüberlauf, da der beste Fall ein SEGFAULT ist, wenn der Rückgabewert beschädigt ist. Es kann jedoch auch ein zufälliger Speicher ausgeführt werden oder im schlimmsten Fall ein Speicherbereich, der die Sicherheit Ihres Programms gefährdet .
quelle