it-swarm.com.de

Warum wird der Einsatz von Allocationa () nicht als bewährte Methode angesehen?

alloca() reserviert Speicher auf dem Stack und nicht auf dem Heap, wie im Fall von malloc(). Wenn ich von der Routine zurückkomme, wird der Speicher freigegeben. Das löst also eigentlich mein Problem, dynamisch zugewiesenen Speicher freizugeben. Das Freigeben von Speicherplatz, der durch malloc() zugewiesen wurde, ist ein schwerwiegender Kopfschmerz und führt bei fehlendem Speicher zu allen möglichen Speicherproblemen.

Warum wird die Verwendung von alloca() trotz der oben genannten Funktionen nicht empfohlen?

347
Vaibhav

Die Antwort ist genau dort auf der man-Seite (zumindest auf Linux ):

RÜCKGABEWERT Die Allocationa () - Funktion gibt einen Zeiger auf den Anfang von .__ zurück. zugewiesener Speicherplatz. Wenn der Zuordnungsursachen Stapelüberlauf, Programmverhalten ist undefiniert.

Was nicht zu sagen ist, dass es niemals verwendet werden sollte. Eines der OSS-Projekte, an denen ich arbeite, verwendet es ausgiebig. Solange Sie es nicht missbrauchen (alloca große Werte), ist es in Ordnung. Sobald Sie die Marke "einige hundert Bytes" überschritten haben, müssen Sie stattdessen malloc und Freunde verwenden. Es kann immer noch zu Zuordnungsfehlern kommen, aber Sie haben zumindest Anzeichen für den Fehler, anstatt nur den Stack auszublasen.

206
Sean Bright

Einer der denkwürdigsten Fehler, die ich hatte, war mit einer Inline-Funktion zu tun, die alloca verwendete. Es manifestierte sich an zufälligen Punkten der Programmausführung als Stapelüberlauf (weil es auf dem Stapel reserviert).

In der Headerdatei:

void DoSomething() {
   wchar_t* pStr = alloca(100);
   //......
}

In der Implementierungsdatei:

void Process() {
   for (i = 0; i < 1000000; i++) {
     DoSomething();
   }
}

Was also passiert ist, war die inline-Funktion des Compilers DoSomething, und alle Stack-Zuweisungen fanden innerhalb der Funktion Process() statt und sprengten den Stack auf. Zu meiner Verteidigung (und ich war nicht derjenige, der das Problem fand, musste ich zu einem der älteren Entwickler gehen, wenn ich es nicht reparieren konnte), es war nicht gerade alloca, es war eine ATL-Saite Konvertierungsmakros.

Die Lektion lautet also: Verwenden Sie alloca nicht in Funktionen, von denen Sie annehmen, dass sie inline sind.

181
Igor Zevaka

Alte Frage, aber niemand hat erwähnt, dass es durch Arrays mit variabler Länge ersetzt werden sollte.

char arr[size];

anstatt

char *arr=alloca(size);

Es ist im Standard-C99 enthalten und bestand in vielen Compilern als Compiler-Erweiterung.

65

alloca () ist sehr nützlich, wenn Sie keine lokale Standardvariable verwenden können, da ihre Größe zur Laufzeit bestimmt werden müsste und Sie Stellen Sie absolut sicher, dass der Zeiger, den Sie vonalloca () erhalten, NIEMALS verwendet wird, nachdem diese Funktion zurückgegeben wurde.

Sie können ziemlich sicher sein, wenn Sie

  • geben Sie den Zeiger oder alles, was ihn enthält, nicht zurück. 
  • speichern Sie den Zeiger nicht in einer Struktur, die auf dem Heap zugewiesen ist
  • lassen Sie den Zeiger nicht von einem anderen Thread verwenden

Die eigentliche Gefahr besteht in der Wahrscheinlichkeit, dass jemand anderes diese Bedingungen später verletzt. In diesem Sinne ist es großartig, Puffer an Funktionen zu übergeben, die Text in ihnen formatieren :)

53
Arthur Ulfeldt

Wie in dieser Newsgroup-Veröffentlichung erwähnt, gibt es einige Gründe, warum die Verwendung von alloca als schwierig und gefährlich angesehen werden kann:

  • Nicht alle Compiler unterstützen alloca.
  • Einige Compiler interpretieren das beabsichtigte Verhalten von alloca unterschiedlich, sodass die Portabilität selbst zwischen Compilern, die dies unterstützen, nicht gewährleistet ist.
  • Einige Implementierungen sind fehlerhaft.
38
FreeMemory

Ein Problem ist, dass es nicht Standard ist, obwohl es allgemein unterstützt wird. Wenn andere Dinge gleich sind, würde ich immer eine Standardfunktion anstelle einer üblichen Compiler-Erweiterung verwenden.

25
David Thornley

trotzdem wird von der Verwendung abgeraten, warum?

Ich sehe keinen solchen Konsens. Viele starke Profis; ein paar Nachteile:

  • C99 stellt Arrays mit variabler Länge bereit, die häufig bevorzugt als Notationen verwendet werden, die eher mit Arrays mit fester Länge und intuitiver Gesamtdarstellung übereinstimmen
  • in vielen Systemen steht für den Stack insgesamt weniger Speicher/Adressraum zur Verfügung als für den Heap. Dadurch ist das Programm etwas anfälliger für Speicherauslastung (durch Stapelüberlauf): Dies kann als eine gute oder eine schlechte Sache angesehen werden Einer der Gründe, warum der Stack nicht automatisch so wächst wie Heap, besteht darin, zu verhindern, dass außer Kontrolle geratene Programme die gesamte Maschine so stark beeinträchtigen
  • wird der Speicher in einem lokaleren Bereich (z. B. einer while- oder for-Schleife) oder in mehreren Bereichen verwendet, wird der Speicher pro Iteration/Gültigkeitsbereich akkumuliert und erst freigegeben, wenn die Funktion beendet wird. Dies steht im Gegensatz zu normalen Variablen, die im Gültigkeitsbereich einer Kontrollstruktur definiert sind (Zum Beispiel würde for {int i = 0; i < 2; ++i) { X }alloca- geforderten Speicher anhäufen, der bei X angefordert wurde, aber der Speicher für ein Array mit fester Größe würde pro Iteration wiederverwendet werden).
  • moderne Compiler haben normalerweise keine inline-Funktionen, die alloca aufrufen, aber wenn Sie sie erzwingen, wird alloca im Kontext des Aufrufers vorkommen (d. h. der Stack wird erst freigegeben, wenn der Aufrufer zurückkehrt).
  • vor langer Zeit alloca wurde von einem nicht portablen Feature/Hack auf eine standardisierte Erweiterung umgestellt, aber einige negative Wahrnehmungen bleiben bestehen
  • die Lebensdauer ist an den Funktionsumfang gebunden, der für den Programmierer möglicherweise besser geeignet ist als das explizite Steuerelement von malloc
  • die Verwendung von malloc ermutigt zum Nachdenken über die Deallocation. Wenn diese Funktion über eine Wrapper-Funktion (z. B. WonderfulObject_DestructorFree(ptr)) verwaltet wird, bietet die Funktion einen Punkt für Implementierungsbereinigungsoperationen (z. B. Schließen von Dateideskriptoren, Freigeben interner Zeiger oder Protokollieren) ohne explizite Änderungen an den Client-Code: Manchmal ist es ein schönes Modell, das konsequent übernommen wird
    • in dieser Pseudo-OO-Art der Programmierung ist es normal, etwas wie WonderfulObject* p = WonderfulObject_AllocConstructor(); zu wollen - das ist möglich, wenn der "Konstruktor" eine Funktion ist, die malloc- Speicher zurückgibt (da der Speicher reserviert bleibt, nachdem die Funktion den in p zu speichernden Wert zurückgibt). , aber nicht, wenn der "Konstruktor" alloca.__ verwendet.
      • eine Makroversion von WonderfulObject_AllocConstructor könnte dies erreichen, aber "Makros sind böse", da sie miteinander und mit Nicht-Makrocode in Konflikt geraten und unbeabsichtigte Substitutionen erzeugen können, was zu schwer zu diagnostizierenden Problemen führt
    • fehlende free-Vorgänge können von ValGrind, Purify usw. erkannt werden. Fehlende "Destruktor" -Aufrufe können jedoch nicht immer erkannt werden - ein sehr geringer Vorteil in Bezug auf die Durchsetzung der beabsichtigten Verwendung. Einige alloca()-Implementierungen (wie z. B. GCCs) verwenden ein eingebettetes Makro für alloca(), sodass die Laufzeitersetzung einer Speicherbenutzungs-Diagnosebibliothek nicht wie für malloc/realloc/free (z. B. elektrischer Zaun) möglich ist.
  • einige Implementierungen haben subtile Probleme: Zum Beispiel von der Linux-Manpage:</ p>

    Auf vielen Systemen kann alloca () nicht in der Liste der Argumente eines Funktionsaufrufs verwendet werden, da der von Allocationa () reservierte Speicherplatz auf dem Stack in der Mitte des Speicherbereichs für die Funktionsargumente angezeigt wird.</ li> </ ul>


    Ich weiß, dass diese Frage mit C markiert ist, aber als C++ - Programmierer dachte ich, ich würde C++ verwenden, um den möglichen Nutzen von alloca zu veranschaulichen: Der folgende Code (und hier bei Ideone ) erstellt eine Vektor-Verfolgung, die unterschiedlich große polymorphe Typen darstellt sind Stapelzuweisungen (mit einer Lebensdauer, die an die Funktionsrückgabe gebunden ist) und nicht an Heap zugewiesen.

    #include <alloca.h>
    #include <iostream>
    #include <vector>
    
    struct Base
    {
        virtual ~Base() { }
        virtual int to_int() const = 0;
    };
    
    struct Integer : Base
    {
        Integer(int n) : n_(n) { }
        int to_int() const { return n_; }
        int n_;
    };
    
    struct Double : Base
    {
        Double(double n) : n_(n) { }
        int to_int() const { return -n_; }
        double n_;
    };
    
    inline Base* factory(double d) __attribute__((always_inline));
    
    inline Base* factory(double d)
    {
        if ((double)(int)d != d)
            return new (alloca(sizeof(Double))) Double(d);
        else
            return new (alloca(sizeof(Integer))) Integer(d);
    }
    
    int main()
    {
        std::vector<Base*> numbers;
        numbers.Push_back(factory(29.3));
        numbers.Push_back(factory(29));
        numbers.Push_back(factory(7.1));
        numbers.Push_back(factory(2));
        numbers.Push_back(factory(231.0));
        for (std::vector<Base*>::const_iterator i = numbers.begin();
             i != numbers.end(); ++i)
        {
            std::cout << *i << ' ' << (*i)->to_int() << '\n';
            (*i)->~Base();   // optionally / else Undefined Behaviour iff the
                             // program depends on side effects of destructor
        }
    }
    
21
Tony Delroy

Alle anderen Antworten sind korrekt. Wenn jedoch das, was Sie mit alloca() zuweisen möchten, relativ klein ist, denke ich, ist es eine gute Technik, die schneller und bequemer ist als malloc() oder auf andere Weise.

Mit anderen Worten, alloca( 0x00ffffff ) ist gefährlich und verursacht wahrscheinlich einen Überlauf, genau wie char hugeArray[ 0x00ffffff ];. Seien Sie vorsichtig und vernünftig und es wird Ihnen gut gehen.

12
JSBձոգչ

Jeder hat bereits auf das große Problem hingewiesen, das potenziell undefiniertes Verhalten aufgrund eines Stapelüberlaufs darstellt. Ich sollte jedoch erwähnen, dass die Windows-Umgebung über einen hervorragenden Mechanismus verfügt, um dies mithilfe strukturierter Ausnahmen (SEH) und Schutzseiten abzufangen. Da der Stapel nur bei Bedarf wächst, befinden sich diese Schutzseiten in Bereichen, die nicht zugewiesen sind. Wenn Sie ihnen zuordnen (durch Überlaufen des Stapels), wird eine Ausnahme ausgelöst.

Sie können diese SEH-Ausnahme abrufen und _resetstkoflw aufrufen, um den Stapel zurückzusetzen und auf Ihrem lustigen Weg fortzufahren. 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 bewusst ist.

Ich empfehle Ihnen, Ihre maximale Zuweisungsgröße zu kappen, indem Sie Allocationa umschließen und intern verfolgen. Wenn Sie wirklich hart darauf waren, könnten Sie oben in Ihrer Funktion einige Bereichswächter ablegen, um Allocation-Zuordnungen im Funktionsumfang nachzuverfolgen, und prüfen Sie dies anhand des maximal für Ihr Projekt zulässigen Betrags.

Zusätzlich dazu, dass keine Speicherlecks zugelassen werden, führt die Zuteilung zu keiner Fragmentierung des Speichers, was ziemlich wichtig ist. Ich denke nicht, dass das Zuteilen einer falschen Methode ist, wenn Sie es intelligent einsetzen, was grundsätzlich für alles gilt. :-)

11
SilentDirge

Viele interessante Antworten auf diese "alte" Frage, sogar einige relativ neue Antworten, aber ich fand keine, die dies erwähnte ...

Bei bestimmungsgemäßer Verwendung und sorgfältiger Verwendung von alloca() (möglicherweise anwendungsweit), um kleine Zuordnungen mit variabler Länge zu behandeln (oder C99-VLAs, sofern verfügbar) können zu niedrigerer Gesamtstack Wachstum führen als eine ansonsten äquivalente Implementierung mit übergroßen lokale Arrays mit fester Länge. Daher kann alloca()gut für Ihren Stack sein, wenn Sie ihn sorgfältig verwenden.

Ich fand dieses Zitat in ... OK, ich habe dieses Zitat gemacht. Aber wirklich, denk darüber nach ...

@j_random_hacker hat in seinen Kommentaren unter anderen Antworten sehr recht: Wenn Sie die Verwendung von alloca() zugunsten übergroßer lokaler Arrays vermeiden, ist Ihr Programm nicht sicherer vor Stack-Überläufen (es sei denn, Ihr Compiler ist alt genug, um in diesem Fall Funktionen verwenden zu können, die alloca() verwenden Sie sollten ein Upgrade durchführen, oder wenn Sie nicht alloca() in Schleifen verwenden. In diesem Fall sollten Sie ... alloca() nicht in Schleifen verwenden.

Ich habe an Desktop-/Server-Umgebungen und eingebetteten Systemen gearbeitet. Viele eingebettete Systeme verwenden überhaupt keinen Heap (sie unterstützen nicht einmal die Unterstützung dafür), aus Gründen, zu denen die Wahrnehmung gehört, dass dynamisch zugewiesener Speicher aufgrund der Gefahr von Speicherverlusten in einer Anwendung, die niemals genutzt wird, schlecht ist Es wird immer jahrelang neu gestartet, oder die vernünftigere Begründung, dass dynamischer Speicher gefährlich ist, da nicht sicher ist, dass eine Anwendung ihren Heap niemals bis zur Erschöpfung des falschen Speichers fragmentiert. Eingebetteten Programmierern bleiben daher nur wenige Alternativen.

alloca() (oder VLAs) ist möglicherweise das richtige Werkzeug für den Job.

Ich habe immer wieder gesehen, wo ein Programmierer einen Stack-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 Aufrufbaum vor, der 20 Ebenen tief ist, wobei die Funktion auf jeder Ebene aus verschiedenen Gründen einen Puffer von 1024 Bytes "einfach um sicher" ist), der normalerweise nur 16 oder weniger davon verwendet, und nur in sehr starkem Umfang In seltenen Fällen kann mehr verwendet werden.) Alternativ können Sie alloca() oder VLAs verwenden und nur so viel Stapelspeicherplatz zuweisen, wie Ihre Funktion benötigt, um den Stapel nicht unnötig zu belasten. Wenn eine Funktion in der Aufrufstruktur eine größere Zuweisung als normal benötigt, verwenden die Funktionen in der Aufrufstruktur ihre normalen kleinen Zuordnungen und die Gesamtauslastung des Anwendungsstacks ist deutlich geringer, als wenn jede Funktion einen lokalen Puffer blind überbelegt hätte .

Wenn Sie sich jedoch für alloca()... entscheiden.

Basierend auf den anderen Antworten auf dieser Seite scheint es, dass VLAs sicher sein sollten (sie bilden keine Stack-Zuordnungen, wenn sie innerhalb einer Schleife aufgerufen werden). Wenn Sie jedoch alloca() verwenden, sollten Sie sie nicht innerhalb einer Schleife verwenden make sure Ihre Funktion kann nicht eingebettet werden, wenn die Möglichkeit besteht, dass sie innerhalb der Schleife einer anderen Funktion aufgerufen wird.

9
phonetagger

Hier ist der Grund:

char x;
char *y=malloc(1);
char *z=alloca(&x-y);
*z = 1;

Nicht, dass irgendjemand diesen Code schreiben würde, aber das Größenargument, an das Sie alloca übergeben, stammt fast sicher von einer Art Eingabe, die böswillig darauf abzielen könnte, Ihr Programm in alloca zu bringen. Wenn die Größe nicht auf der Eingabe 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 Privileg-Kompromissen (wenn Sie nicht so viel Glück haben) führt.

9
R..

alloca () ist nett und effizient ... aber es ist auch sehr kaputt.

  • defektes Bereichsverhalten (Funktionsumfang statt Blockumfang)
  • verwenden Sie inkonsistent mit malloc ( alloca () - ted Zeiger sollte nicht freigegeben werden, fortan müssen Sie verfolgen, woher die Zeiger nach free () nur diejenigen kommen, die Sie mit malloc () erhalten haben. )
  • schlechtes Verhalten, wenn Sie auch Inlining verwenden (der Gültigkeitsbereich wechselt manchmal zur Anruferfunktion, abhängig davon, ob der Angerufene inline ist oder nicht). 
  • keine Überprüfung der Stapelgrenze
  • undefined Verhalten im Fehlerfall (gibt nicht NULL zurück wie malloc ... und was bedeutet Fehler, da Stack-Grenzen sowieso nicht geprüft werden ...)
  • nicht ansi standard

In den meisten Fällen können Sie es mit lokalen Variablen und Majorantengröß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 (keine Vla in C++, zu schlecht). Sie sind viel besser als Allocation () hinsichtlich des Verhaltens und der Konsistenz des Bereichs. Wie ich es sehe, sindVLAeine Art alloca () / richtig gemacht. 

Natürlich ist eine lokale Struktur oder ein Array, das einen Majoranten des benötigten Speicherplatzes verwendet, immer noch besser, und wenn Sie keine solche Major-Heap-Zuweisung mit Plain Malloc () haben, ist dies wahrscheinlich vernünftig. Ich sehe keinen vernünftigen Anwendungsfall, bei dem Sie wirklich alloca () oder VLA wirklich benötigen.

9
kriss

Ein Ort, an dem alloca() besonders gefährlich ist als malloc(), ist der Kernel-Kernel eines typischen Betriebssystems, dessen Stack-Speicherplatz fest in seiner Kopfzeile fest codiert ist. Es ist nicht so flexibel wie der Stapel einer Anwendung. Wenn Sie alloca() mit einer ungerechtfertigten Größe anrufen, kann dies zum Absturz des Kernels führen . Bestimmte Compiler warnen die Verwendung von alloca() (und sogar VLAs) unter bestimmten Optionen, die beim Kompilieren eines Kernel-Codes aktiviert sein sollten. Es ist besser, Speicher in dem Heapspeicher zuzuordnen, der nicht durch ein fest vorgegebenes Limit festgelegt ist.

7

Ich glaube nicht, dass irgendjemand dies erwähnt hat: Die Verwendung von Allocationa in einer Funktion behindert oder deaktiviert einige Optimierungen, die ansonsten in der Funktion angewendet werden könnten, da der Compiler die Größe des Stack-Frames der Funktion nicht kennen kann. 

Eine übliche Optimierung durch C-Compiler besteht beispielsweise darin, die Verwendung des Frame-Zeigers innerhalb einer Funktion zu eliminieren, stattdessen werden Frame-Zugriffe relativ zum Stack-Pointer ausgeführt. Es gibt also noch ein Register für den allgemeinen Gebrauch. Wenn Allocationa jedoch innerhalb der Funktion aufgerufen wird, ist der Unterschied zwischen sp und fp für einen Teil der Funktion unbekannt, sodass diese Optimierung nicht durchgeführt werden kann.

In Anbetracht der Seltenheit ihrer Verwendung und ihres schattigen Status als Standardfunktion deaktivieren Compiler-Designer möglicherweise any -Optimierung, die eventuell Probleme mit Allocationa verursachen, wenn dies mehr als nur ein wenig Aufwand erfordert Damit es mit Zuteilung funktioniert. 

4
greggo

Eine Falle bei alloca ist, dass longjmp es zurückspult.

Das heißt, wenn Sie einen Kontext mit setjmp speichern, dann alloca etwas Speicher und dann longjmp in den Kontext stellen, können Sie den alloca-Speicher (ohne jegliche Benachrichtigung) verlieren. Der Stapelzeiger ist wieder da, wo er war, und der Speicher ist nicht mehr reserviert. Wenn Sie eine Funktion aufrufen oder eine andere alloca-Funktion ausführen, werden Sie die ursprüngliche alloca-Funktion übernehmen.

Um zu verdeutlichen, beziehe ich mich hier speziell auf eine Situation, in der longjmp nicht aus der Funktion zurückkehrt, in der alloca stattgefunden hat! Vielmehr speichert eine Funktion den Kontext mit setjmp; ordnet dann mit alloca Speicher zu und schließlich findet ein Longjmp in diesem Kontext statt. Der alloca-Speicher dieser Funktion wird nicht vollständig freigegeben. nur der gesamte Speicher, den es seit dem setjmp zugewiesen hat. Ich spreche natürlich von einem beobachteten Verhalten; Kein solches Erfordernis ist für irgendein alloca dokumentiert, das ich kenne. 

In der Dokumentation liegt der Fokus normalerweise auf dem Konzept, dass alloca-Speicher mit einer FunktionsAktivierung verbunden ist, nicht mit einem Block; dass mehrere Aufrufe von alloca einfach mehr Stapelspeicher greifen, der alle freigegeben wird, wenn die Funktion beendet wird. Nicht so; Der Speicher ist tatsächlich mit dem Prozedurkontext verbunden. Wenn der Kontext mit longjmp wiederhergestellt wird, ist dies auch der vorherige alloca-Zustand. Dies ist eine Folge davon, dass das Stack-Pointer-Register selbst für die Zuweisung verwendet wird und (notwendigerweise) im jmp_buf gespeichert und wiederhergestellt wird.

Wenn dies so funktioniert, bietet dies übrigens einen plausiblen Mechanismus für das absichtliche Freigeben von Speicher, der mit alloca belegt wurde.

Ich bin auf dieses Problem als die Ursache eines Fehlers gestoßen.

4
Kaz

Wenn Sie versehentlich über den mit alloca zugewiesenen Block schreiben (z. B. aufgrund eines Pufferüberlaufs), überschreiben Sie das Rücksprungadresse Ihrer Funktion, da sich dieser auf dem Stack "oben" befindet, dh after Ihr zugewiesener Block.

 _alloca block on the stack

Die Folge davon ist zweifach:

  1. Das Programm wird spektakulär abstürzen, und es ist unmöglich zu sagen, warum oder wo es abgestürzt ist (der Stapel wird höchstwahrscheinlich aufgrund des überschriebenen Rahmenzeigers zu einer zufälligen Adresse zurückgespult).

  2. 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 somit ausgeführt werden kann.

Wenn Sie dagegen über einen Block hinaus auf den Heap schreiben, erhalten Sie "nur" Heap-Korruption. Das Programm wird möglicherweise unerwartet beendet, der Stack wird jedoch ordnungsgemäß abgewickelt, wodurch die Gefahr der Ausführung von schädlichem Code verringert wird.

4
rustyx

Leider fehlt das wirklich großartige alloca() in dem fast geilen tcc. Gcc hat alloca().

  1. Es sät den Samen seiner eigenen Zerstörung. Mit Rückkehr als Zerstörer.

  2. Wie malloc() gibt es bei einem Fehler einen ungültigen Zeiger zurück, der auf modernen Systemen mit einem MMU segfault wird (und hoffentlich diejenigen ohne Neustart).

  3. Im Gegensatz zu Auto-Variablen können Sie die Größe zur Laufzeit angeben.

Es funktioniert gut mit Rekursion. Sie können statische Variablen verwenden, um eine ähnliche Funktion wie die Rekursion des Endes zu erreichen, und nur wenige andere verwenden Informationen zu jeder Iteration.

Wenn Sie zu weit nach unten drücken, können Sie sicher sein, dass ein Segfault vorliegt (wenn Sie eine MMU haben).

Beachten Sie, dass malloc() nichts mehr anbietet, da NULL zurückgegeben wird (was bei Zuweisung auch segfault wird), wenn der Speicher nicht ausreicht. D.h. Alles, was Sie tun können, ist die Kaution oder versuchen Sie es einfach zuzuweisen.

Um malloc() zu verwenden, verwende ich Globals und weise ihnen NULL zu. Wenn der Zeiger nicht NULL ist, gebe ich ihn frei, bevor ich malloc() verwende.

Sie können realloc() auch als generellen Fall verwenden, wenn Sie vorhandene Daten kopieren möchten. Bevor Sie herausfinden, ob Sie nach der realloc() kopieren oder verketten möchten, müssen Sie den Zeiger überprüfen.

3.2.5.2 Vorteile von Allocation

4
zagam

In Prozessen steht nur ein begrenzter Stapelspeicherplatz zur Verfügung - weit weniger als der für malloc() zur Verfügung stehende Speicher.

Durch die Verwendung von alloca() erhöhen Sie Ihre Chancen auf einen Stack Overflow-Fehler (wenn Sie Glück haben, oder einen unerklärlichen Absturz, wenn dies nicht der Fall ist).

3
RichieHindle

Eigentlich kann nicht garantiert werden, dass Allocationa den Stack verwendet ..__ In der Tat ordnet die Implementierung von Alloca gcc-2.95 Speicher aus dem Heap mithilfe von Malloc selbst zu. 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 zu einem höheren Aufwand, als es von sich gibt.

2
user7491277

Nicht sehr hübsch, aber wenn Leistung wirklich eine Rolle spielt, könnten Sie etwas Platz auf dem Stack vorbelegen.

Wenn Sie bereits jetzt die maximale Größe des Speichers blockieren und Sie die Überlaufüberprüfung beibehalten möchten, können Sie Folgendes tun:

void f()
{
    char array_on_stack[ MAX_BYTES_TO_ALLOCATE ];
    SomeType *p = (SomeType *)array;

    (...)
}
2

Die Allocation-Funktion ist großartig und alle Neinsager verbreiten einfach FUD. 

void foo()
{
    int x = 50000; 
    char array[x];
    char *parray = (char *)alloca(x);
}

Array und Parray sind genau das gleiche mit genau den gleichen Risiken. Zu sagen, einer sei besser als ein anderer, ist eine syntaktische Entscheidung, keine technische.

Bei der Auswahl von Stack-Variablen gegenüber Heap-Variablen gibt es viele Vorteile für lange laufende Programme, die Stack-for-Heap für Variablen mit einer Gültigkeitsdauer im Gültigkeitsbereich verwenden. Sie vermeiden die Fragmentierung des Heapspeichers, und Sie können vermeiden, dass Ihr Prozessspeicherplatz mit nicht verwendetem (unbrauchbarem) Heapspeicherplatz vergrößert wird. Sie müssen es nicht aufräumen. Sie können die Stapelzuordnung im Prozess steuern. 

Warum ist das so schlimm? 

1
mlwmohawk

IMHO gilt Allocationa als schlechte Praxis, weil jeder Angst hat, die Stackgrößenbegrenzung zu erschöpfen.

Ich habe durch das Lesen dieses Threads und einiger anderer Links viel gelernt:

Ich benutze Allocationa hauptsächlich, um meine einfachen C-Dateien unter msvc und gcc ohne Änderung, C89-Stil, kein #ifdef _MSC_VER usw. zu kompilieren.

Vielen Dank ! Dieser Thread hat mich dazu veranlasst, mich auf dieser Site anzumelden :)

1
ytoto

Meines Erachtens sollte Allocationa (), sofern verfügbar, nur eingeschränkt verwendet werden. Ähnlich wie bei der Verwendung von "goto" haben ziemlich viele ansonsten vernünftige Menschen eine starke Abneigung nicht nur gegenüber der Verwendung von Allocationa, sondern auch der Existenz von Allocationa ().

Für die Embedded-Verwendung, bei der die Stack-Größe bekannt ist und durch Konventionen und Analysen Limits für die Größe der Zuweisung festgelegt werden kann, und bei denen der Compiler nicht zur Unterstützung von C99 + aktualisiert werden kann, ist die Verwendung von Alloca () in Ordnung, und das war ich bekannt, um es zu benutzen.

Wenn verfügbar, haben VLAs möglicherweise einige Vorteile gegenüber Allocationa (): Der Compiler kann Stack-Limit-Prüfungen generieren, die den Zugriff außerhalb des zulässigen Bereichs abfangen, wenn ein Array-Stilzugriff verwendet wird (ich weiß nicht, ob Compiler dies tun, dies jedoch möglich ist ), und die Analyse des Codes kann bestimmen, ob die Array-Zugriffsausdrücke richtig begrenzt sind. Beachten Sie, dass diese Analyse in einigen Programmierumgebungen, z. B. im Automobilbereich, in medizinischen Geräten und in der Avionik, auch für Arrays mit fester Größe durchgeführt werden muss, sowohl automatisch (auf dem Stack) als auch statisch (global oder lokal).

Bei Architekturen, die sowohl Daten als auch Rücksprungadressen/Frame-Zeiger auf dem Stack speichern (von denen ich weiß, dass sie alle sind), kann jede dem Stack zugewiesene Variable gefährlich sein, da die Adresse der Variablen übernommen werden kann und ungeprüfte Eingabewerte dies zulassen allerlei Unfug.

Portabilität ist im eingebetteten Raum weniger ein Problem, ist jedoch ein gutes Argument gegen die Verwendung von Alloca () außerhalb sorgfältig kontrollierter Umstände.

Außerhalb des eingebetteten Bereichs habe ichape () hauptsächlich aus Protokollierungs- und Formatierungsfunktionen verwendet, um die Effizienz zu verbessern, und bei einem nicht-rekursiven lexikalischen Scanner, bei dem temporäre Strukturen (zugeteilt mit Allocationa ()) während der Tokenisierung und Klassifizierung erstellt werden, die dann dauerhaft sind object (wird über malloc ()) zugewiesen, bevor die Funktion zurückkehrt Die Verwendung vonolda () für kleinere temporäre Strukturen reduziert die Fragmentierung erheblich, wenn das persistente Objekt zugewiesen wird.

1
Daniel Glasser

Bei den meisten Antworten wird der Punkt weitgehend übersehen: Es gibt einen Grund, warum die Verwendung von _alloca() möglicherweise schlechter ist, als nur große Objekte im Stapel zu speichern.

Der Hauptunterschied zwischen dem automatischen Speichern und _alloca() besteht darin, dass letztere unter einem zusätzlichen (schwerwiegenden) Problem leiden: Der zugewiesene Block wird wird nicht vom Compiler gesteuert , sodass der Compiler ihn nicht optimieren oder recyceln kann.

Vergleichen Sie:

while (condition) {
    char buffer[0x100]; // Chill.
    /* ... */
}

mit:

while (condition) {
    char* buffer = _alloca(0x100); // Bad!
    /* ... */
}

Das Problem mit letzterem sollte offensichtlich sein.

0
alecov

Ich glaube nicht, dass dies von irgendjemandem erwähnt wurde, aber Allplana hat auch einige schwerwiegende Sicherheitsprobleme, die bei malloc nicht unbedingt vorhanden sind (obwohl diese Probleme auch bei Stack-basierten Arrays auftreten, dynamisch oder nicht). Da der Speicher auf dem Stack reserviert ist, haben Pufferüberläufe/-unterläufe weitaus ernstere 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 jeden ausführbaren Speicherbereich verschoben werden. Compiler tun alles, um dies zu erschweren (insbesondere durch das zufällige Adresslayout). Dies ist jedoch eindeutig schlimmer als ein Stapelüberlauf, da der beste Wert ein SEGFAULT-Wert ist, wenn der Rückgabewert beschädigt ist. Es könnte jedoch auch ein zufälliger Speicherbereich oder im schlimmsten Fall ein Speicherbereich ausgeführt werden, der die Sicherheit Ihres Programms beeinträchtigt .

0
Dyllon Gagnier