Perché l'uso di alloca () non è considerato una buona pratica?


401

alloca()alloca memoria nello stack anziché nell'heap, come nel caso di malloc(). Quindi, quando torno dalla routine, la memoria viene liberata. Quindi, in realtà, questo risolve il mio problema di liberare memoria allocata dinamicamente. La liberazione della memoria allocata attraverso malloc()è un grosso mal di testa e se in qualche modo mancato porta a tutti i tipi di problemi di memoria.

Perché l'uso di è alloca()scoraggiato nonostante le funzionalità di cui sopra?


40
Solo una breve nota. Sebbene questa funzione sia presente nella maggior parte dei compilatori, non è richiesta dallo standard ANSI-C e pertanto potrebbe limitare la portabilità. Un'altra cosa è che non devi! free () il puntatore che ottieni e viene liberato automaticamente dopo aver chiuso la funzione.
Merkuro,

9
Inoltre, una funzione con alloca () non sarà inclusa se dichiarata come tale.
Justicle il

2
@Justicle, puoi fornire qualche spiegazione? Sono molto curioso di
sapere

47
Dimentica tutto il rumore relativo alla portabilità, non è necessario chiamare free(il che è ovviamente un vantaggio), non capacità di incorporarlo (ovviamente le allocazioni di heap sono molto più pesanti) ed ecc. L'unica ragione per evitare allocaè per le grandi dimensioni. Cioè, sprecare tonnellate di memoria dello stack non è una buona idea, inoltre hai la possibilità di un overflow dello stack. In questo caso, considera l'utilizzo di malloca/freea
valdo il

5
Un altro aspetto positivo di allocaè che la pila non può essere frammentata come l'heap. Ciò potrebbe rivelarsi utile per le applicazioni in stile run-forever in tempo reale, o anche per le applicazioni critiche per la sicurezza, poiché la WCRU può quindi essere analizzata staticamente senza ricorrere a pool di memoria personalizzati con il proprio set di problemi (nessuna località temporale, risorsa non ottimale uso).
Andreas,

Risposte:


245

La risposta è proprio lì nella manpagina (almeno su Linux ):

VALORE DI RITORNO La funzione alloca () restituisce un puntatore all'inizio dello spazio allocato. Se l'allocazione causa uno stack overflow, il comportamento del programma non è definito.

Il che non vuol dire che non dovrebbe mai essere usato. Uno dei progetti OSS a cui sto lavorando lo usa ampiamente, e fintanto che non lo stai abusando ( alloca'ingenti valori), va bene. Una volta superato il segno "poche centinaia di byte", è tempo di usare malloce amici, invece. Potresti comunque riscontrare errori di allocazione, ma almeno avrai qualche indicazione del fallimento invece di far esplodere lo stack.


35
Quindi non c'è davvero alcun problema con ciò che non avresti anche con la dichiarazione di array di grandi dimensioni?
TED

88
@Sean: Sì, il rischio di overflow dello stack è il motivo indicato, ma tale motivo è un po 'sciocco. Innanzitutto perché (come dice Vaibhav) le grandi matrici locali causano esattamente lo stesso problema, ma non sono altrettanto diffamate. Inoltre, la ricorsione può far esplodere altrettanto facilmente la pila. Mi dispiace, ma ti sto facendo sperare di contrastare l'idea prevalente che il motivo indicato nella pagina man sia giustificato.
j_random_hacker,

49
Il mio punto è che la giustificazione fornita nella pagina man non ha senso, dal momento che alloca () è esattamente "cattivo" come quelle altre cose (array locali o funzioni ricorsive) che sono considerate kosher.
j_random_hacker il

39
@ninjalj: Non da programmatori C / C ++ di grande esperienza, ma penso che molte persone che temono alloca()non abbiano la stessa paura degli array locali o della ricorsione (in effetti molte persone che urleranno alloca()loderanno la ricorsione perché "sembra elegante") . Concordo con il consiglio di Shaun ("alloca () va bene per piccole allocazioni") ma non sono d'accordo con la mentalità che inquadra alloca () come univocamente malvagio tra i 3 - sono ugualmente pericolosi!
j_random_hacker il

35
Nota: data la strategia "ottimistica" di allocazione della memoria di Linux, molto probabilmente non otterrai alcuna indicazione di un errore di esaurimento dell'heap ... invece malloc () ti restituirà un bel puntatore non NULL, e quindi quando proverai a accedere allo spazio di indirizzi a cui punta, il tuo processo (o qualche altro processo, imprevedibilmente) verrà ucciso dall'omicida OOM. Naturalmente questa è una "caratteristica" di Linux piuttosto che un problema C / C ++ in sé, ma è qualcosa da tenere a mente quando si discute se alloca () o malloc () sia "più sicuro". :)
Jeremy Friesner,

209

Uno dei bug più memorabili che ho avuto è stato a che fare con una funzione in linea che ha usato alloca. Si è manifestato come overflow dello stack (perché si alloca sullo stack) in punti casuali dell'esecuzione del programma.

Nel file di intestazione:

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

Nel file di implementazione:

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

Quindi quello che è successo è stata la DoSomethingfunzione integrata del compilatore e tutte le allocazioni dello stack stavano avvenendo all'interno della Process()funzione e quindi facevano esplodere lo stack. A mia difesa (e non sono stato io a trovare il problema; dovevo andare a piangere con uno degli sviluppatori senior quando non riuscivo a risolverlo), non era semplice alloca, era una conversione di stringa ATL macro.

Quindi la lezione è: non usare allocain funzioni che pensi possano essere integrate.


91
Interessante. Ma questo non si qualificherebbe come un bug del compilatore? Dopotutto, l'allineamento ha cambiato il comportamento del codice (ha ritardato la liberazione dello spazio allocato usando alloca).
sleske,

60
Apparentemente, almeno GCC terrà conto di questo: "Nota che alcuni usi in una definizione di funzione possono renderlo inadatto per la sostituzione in linea. Tra questi usi ci sono: l'uso di varargs, l'uso di alloca, [...]". gcc.gnu.org/onlinedocs/gcc/Inline.html
sleske,

139
Che compilatore stavi fumando?
Thomas Eding,

22
Quello che non capisco è il motivo per cui il compilatore non fa buon uso dell'ambito per determinare che le allocazioni nel sottoscope siano più o meno "liberate": il puntatore dello stack potrebbe tornare al suo punto prima di entrare nell'ambito, come quello che viene fatto quando di ritorno dalla funzione (no?)
moala

8
Ho effettuato il downgrade, ma la risposta è ben scritta: sono d'accordo con gli altri che stai criticando alloca per quello che è chiaramente un bug del compilatore . Il compilatore ha fatto un'ipotesi errata in un'ottimizzazione che non avrebbe dovuto fare. Lavorare attorno a un bug del compilatore va bene, ma non ho niente da ridire se non il compilatore.
Evan Carroll,

75

Vecchia domanda ma nessuno ha menzionato che dovrebbe essere sostituito da array di lunghezza variabile.

char arr[size];

invece di

char *arr=alloca(size);

È nello standard C99 ed esisteva come estensione del compilatore in molti compilatori.


5
È menzionato da Jonathan Leffler in un commento alla risposta di Arthur Ulfeldt.
ninjalj,

2
Anzi, ma mostra anche quanto è facile perdere, poiché non l'avevo visto nonostante avessi letto tutte le risposte prima di postare.
Patrick Schlüter,

6
Una nota: quelli sono array di lunghezza variabile, non array dinamici. Questi ultimi sono ridimensionabili e generalmente implementati nell'heap.
Tim Čas,

1
Visual Studio 2015 che compila del C ++ ha lo stesso problema.
ahcox,

2
A Linus Torvalds non piacciono i VLA nel kernel di Linux . A partire dalla versione 4.20 Linux dovrebbe essere quasi VLA libero.
Cristian Ciupitu,

60

alloca () è molto utile se non è possibile utilizzare una variabile locale standard poiché la sua dimensione dovrebbe essere determinata in fase di esecuzione e si può assolutamente garantire che il puntatore che si ottiene da alloca () NON sarà MAI usato dopo il ritorno di questa funzione .

Puoi essere abbastanza sicuro se lo fai

  • non restituire il puntatore o qualsiasi cosa lo contenga.
  • non memorizzare il puntatore in nessuna struttura allocata sull'heap
  • non lasciare che nessun altro thread usi il puntatore

Il vero pericolo deriva dalla possibilità che qualcun altro violi queste condizioni qualche tempo dopo. Con questo in mente è ottimo per passare i buffer alle funzioni che formattano il testo in essi :)


12
La funzione VLA (array a lunghezza variabile) di C99 supporta variabili locali di dimensioni dinamiche senza richiedere esplicitamente l'utilizzo di alloca ().
Jonathan Leffler,

2
Neato! trovate maggiori informazioni nella sezione '3.4 Matrice a lunghezza variabile' di programmersheaven.com/2/Pointers-and-Arrays-page-2
Arthur Ulfeldt,

1
Ma questo non è diverso dalla gestione con puntatori a variabili locali. Possono anche essere presi in giro con ...
glglgl,

2
@Jonathan Leffler una cosa che puoi fare con alloca ma non puoi fare con VLA è usare la parola chiave limit con loro. In questo modo: float * restring heavily_used_arr = alloca (sizeof (float) * size); invece di float heavily_used_arr [dimensione]. Potrebbe aiutare alcuni compilatori (gcc 4.8 nel mio caso) per ottimizzare l'assemblaggio anche se la dimensione è una costante di compilazione. Vedere la mia domanda a questo proposito: stackoverflow.com/questions/19026643/using-restrict-with-arrays
Piotr Lopusiewicz

@JonathanLeffler Un VLA è locale al blocco che lo contiene. D'altra parte, alloca()alloca memoria che dura fino alla fine della funzione. Ciò significa che non sembra esserci una traduzione semplice e conveniente per VLA di f() { char *p; if (c) { /* compute x */ p = alloca(x); } else { p = 0; } /* use p */ }. Se ritieni che sia possibile tradurre automaticamente gli usi degli allocain VLA ma richiedi più di un commento per descrivere come, posso farne una domanda.
Pascal Cuoq,

40

Come notato in questo post di newsgroup , ci sono alcuni motivi per cui l'utilizzo allocapuò essere considerato difficile e pericoloso:

  • Non tutti i compilatori supportano alloca.
  • Alcuni compilatori interpretano il comportamento previsto in modo allocadiverso, quindi la portabilità non è garantita nemmeno tra i compilatori che lo supportano.
  • Alcune implementazioni sono difettose.

24
Una cosa che ho visto menzionato su quel link che non è altrove in questa pagina è che una funzione che utilizza alloca()richiede registri separati per contenere il puntatore dello stack e il puntatore del frame. Su CPU x86> = 386, il puntatore dello stack ESPpuò essere utilizzato per entrambi, liberando EBP- a meno che non alloca()venga utilizzato.
j_random_hacker,

10
Un altro punto positivo in quella pagina è che, a meno che il generatore di codice del compilatore non lo gestisca come un caso speciale, f(42, alloca(10), 43);potrebbe bloccarsi a causa della possibilità che il puntatore dello stack venga regolato alloca() dopo che almeno uno degli argomenti è stato spinto su di esso.
j_random_hacker,

3
Il post collegato sembra essere stato scritto da John Levine, il tipo che ha scritto "Linkers and Loaders", mi fiderei di tutto ciò che dice.
user318904,

3
Il post collegato è una risposta a un post di John Levine.
A. Wilcox,

6
Tenete presente che dal 1991 molte cose sono cambiate. Tutti i moderni compilatori C (anche nel 2009) devono gestire l'alloca come un caso speciale; è una funzione intrinseca piuttosto che ordinaria e potrebbe anche non chiamare una funzione. Quindi, il problema alloca-in-parameter (sorto in K&R C dagli anni '70) non dovrebbe essere un problema ora. Maggiori dettagli in un commento che ho fatto sulla risposta di Tony D
Greggo,

26

Un problema è che non è standard, sebbene sia ampiamente supportato. A parità di altre condizioni, utilizzerei sempre una funzione standard anziché un'estensione comune del compilatore.


21

l'uso alloca è ancora scoraggiato, perché?

Non percepisco un tale consenso. Molti professionisti forti; alcuni svantaggi:

  • C99 fornisce array di lunghezza variabile, che spesso verrebbero utilizzati preferibilmente in quanto la notazione è più coerente con array di lunghezza fissa e intuitivo complessivo
  • molti sistemi hanno meno spazio di memoria / indirizzo complessivo disponibile per lo stack di quanto non facciano per l'heap, il che rende il programma leggermente più suscettibile all'esaurimento della memoria (tramite overflow dello stack): questo può essere visto come una cosa buona o cattiva - uno dei motivi per cui lo stack non cresce automaticamente come fa l'heap è impedire ai programmi fuori controllo di avere lo stesso impatto negativo sull'intera macchina
  • se utilizzata in un ambito più locale (come un whileo forloop) o in più ambiti, la memoria si accumula per iterazione / ambito e non viene rilasciata fino alla chiusura della funzione: ciò contrasta con le normali variabili definite nell'ambito di una struttura di controllo (ad es. for {int i = 0; i < 2; ++i) { X }accumulerebbe la allocamemoria richiesta da X, ma la memoria per un array di dimensioni fisse verrebbe riciclata per iterazione).
  • i compilatori moderni in genere non inlinefunzionano con quelle chiamate alloca, ma se li forzate allocaaccadrà nel contesto dei chiamanti (cioè lo stack non verrà rilasciato fino a quando il chiamante non ritorna)
  • molto tempo fa è allocapassato da una funzionalità / hack non portatile a un'estensione standardizzata, ma una certa percezione negativa potrebbe persistere
  • la durata è legata all'ambito della funzione, che può o meno adattarsi al programmatore meglio del malloccontrollo esplicito
  • il dover usare mallocincoraggia a pensare alla deallocazione - se è gestita tramite una funzione wrapper (ad es. WonderfulObject_DestructorFree(ptr)), allora la funzione fornisce un punto per le operazioni di pulizia dell'implementazione (come la chiusura dei descrittori di file, la liberazione di puntatori interni o la registrazione) senza modifiche esplicite al client codice: a volte è un bel modello da adottare in modo coerente
    • in questo stile di programmazione pseudo-OO, è naturale desiderare qualcosa del genere WonderfulObject* p = WonderfulObject_AllocConstructor();- ciò è possibile quando il "costruttore" è una funzione che restituisce mallocmemoria con il nome (poiché la memoria rimane allocata dopo che la funzione restituisce il valore da archiviare p), ma non se il "costruttore" utilizzaalloca
      • una versione macro di WonderfulObject_AllocConstructorpotrebbe raggiungere questo obiettivo, ma "le macro sono cattive" in quanto possono essere in conflitto tra loro e con codice non macro e creare sostituzioni non intenzionali e conseguenti problemi di difficile diagnosi
    • le freeoperazioni mancanti possono essere rilevate da ValGrind, Purify ecc. ma le chiamate "distruttive" mancanti non possono sempre essere rilevate affatto - un vantaggio molto debole in termini di applicazione dell'uso previsto; alcune alloca()implementazioni (come GCC) usano una macro incorporata per alloca(), quindi la sostituzione di runtime di una libreria diagnostica di utilizzo della memoria non è possibile come è malloc/ realloc/ free(es. recinto elettrico)
  • alcune implementazioni hanno problemi sottili: ad esempio, dalla manpage di Linux:

    Su molti sistemi alloca () non può essere utilizzato all'interno dell'elenco degli argomenti di una chiamata di funzione, poiché lo spazio dello stack riservato da alloca () apparirebbe nello stack al centro dello spazio per gli argomenti della funzione.


So che questa domanda è taggata C, ma come programmatore C ++ ho pensato di usare C ++ per illustrare la potenziale utilità di alloca: il codice qui sotto (e qui a ideone ) crea un vettore che traccia tipi polimerici di dimensioni diverse che sono allocati nello stack (con durata legata al ritorno della funzione) anziché all'heap allocato.

#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
    }
}

no +1 a causa del modo di pesce idiosincratico di gestire diversi tipi :-(
einpoklum

@einpoklum: beh, questo è profondamente illuminante ... grazie.
Tony Delroy,

1
Vorrei riformulare: questa è un'ottima risposta. Fino al punto in cui penso che tu stia suggerendo che le persone usano una sorta di contro-schema.
einpoklum,

Il commento dalla manpage di Linux è molto vecchio e, ne sono abbastanza sicuro, obsoleto. Tutti i compilatori moderni sanno cos'è alloca () e non inciamperanno sui loro lacci in quel modo. Nel vecchio K&R C, (1) tutte le funzioni utilizzavano i puntatori di trama (2) Tutte le chiamate di funzione erano {push args sullo stack} {call func} {add # n, sp}. alloca era una funzione lib che avrebbe semplicemente aumentato lo stack, il compilatore non sapeva nemmeno che ciò stesse accadendo. (1) e (2) non sono più veri, quindi alloca non può funzionare in questo modo (ora è intrinseco). Nella vecchia C, chiamare alloca nel mezzo della spinta degli args avrebbe ovviamente rotto anche queste ipotesi.
Greggo,

4
Per quanto riguarda l'esempio, sarei generalmente preoccupato per qualcosa che richiede always_inline per evitare il danneggiamento della memoria ....
Greggo

14

Tutte le altre risposte sono corrette. Tuttavia, se la cosa che si desidera allocare utilizzando alloca()è ragionevolmente piccola, penso che sia una buona tecnica che è più veloce e più conveniente rispetto all'uso malloc()o meno.

In altre parole, alloca( 0x00ffffff )è pericoloso e può causare straripamento, esattamente quanto lo char hugeArray[ 0x00ffffff ];è. Sii cauto e ragionevole e starai bene.


12

Molte risposte interessanti a questa "vecchia" domanda, anche alcune risposte relativamente nuove, ma non ho trovato nessuna che menzionasse questo ...

Se utilizzato correttamente e con cura, l'uso coerente di alloca() (forse a livello di applicazione) per gestire allocazioni di lunghezza variabile di piccole dimensioni (o VLA C99, ove disponibili) può portare a una crescita complessiva dello stack inferiore rispetto a un'implementazione altrimenti equivalente utilizzando array locali sovradimensionati di lunghezza fissa . Quindi alloca()può essere utile per il tuo stack se lo usi con attenzione.

Ho trovato quella citazione in .... OK, ho inventato quella citazione. Ma davvero, pensaci ...

@j_random_hacker ha ragione nei suoi commenti sotto altre risposte: Evitare l'uso alloca()a favore di array locali sovradimensionati non rende il programma più sicuro dagli overflow dello stack (a meno che il compilatore non sia abbastanza vecchio da consentire l'inserimento di funzioni che usano alloca()nel qual caso dovresti aggiornamento, o a meno che non utilizzi i alloca()loop interni, nel qual caso non dovresti ... non utilizzare i alloca()loop interni).

Ho lavorato su ambienti desktop / server e sistemi embedded. Molti sistemi embedded non usano affatto un heap (non si collegano nemmeno a supporto per esso), per ragioni che includono la percezione che la memoria allocata dinamicamente sia malvagia a causa dei rischi di perdite di memoria su un'applicazione che non ha mai riavvia mai per anni alla volta, o la giustificazione più ragionevole che la memoria dinamica è pericolosa perché non si può sapere con certezza che un'applicazione non frammenterà mai il suo mucchio fino al punto di esaurimento della falsa memoria. Quindi ai programmatori incorporati restano poche alternative.

alloca() (o VLA) potrebbe essere lo strumento giusto per il lavoro.

Ho visto più volte in cui un programmatore rende un buffer allocato nello stack "abbastanza grande da gestire ogni possibile caso". In un albero di chiamate profondamente annidato, l'uso ripetuto di quel modello (anti -?) Porta a un uso esagerato dello stack. (Immagina un albero di chiamata con 20 livelli di profondità, dove ad ogni livello per motivi diversi, la funzione alloca ciecamente alloca un buffer di 1024 byte "solo per sicurezza" quando generalmente ne userà solo 16 o meno, e solo in casi rari possono usarne di più.) Un'alternativa è usarealloca()o VLA e alloca solo lo spazio dello stack di cui ha bisogno la tua funzione, per evitare di caricare inutilmente lo stack. Si spera che quando una funzione nella struttura ad albero delle chiamate abbia bisogno di un'allocazione più grande del normale, altre nella struttura ad albero delle chiamate stiano ancora usando le loro normali allocazioni di piccole dimensioni e l'utilizzo complessivo dello stack dell'applicazione è significativamente inferiore rispetto a quando ogni funzione cieca ha sovrallocato in modo eccessivo un buffer locale .

Ma se scegli di usare alloca()...

Sulla base di altre risposte in questa pagina, sembra che i VLA dovrebbero essere sicuri (non compongono allocazioni di stack se chiamati da all'interno di un ciclo), ma se lo stai usando alloca(), fai attenzione a non usarlo all'interno di un ciclo e crea assicurati che la tua funzione non possa essere incorporata se c'è qualche possibilità che possa essere chiamata all'interno del loop di un'altra funzione.


Sono d'accordo con questo punto. Il pericoloso di alloca()è vero, ma lo stesso si può dire delle perdite di memoria con malloc()(perché non usare un GC allora? Si potrebbe discutere). alloca()se usato con cura può essere davvero utile per ridurre le dimensioni dello stack.
Felipe Tonello,

Un altro buon motivo per non usare la memoria dinamica, specialmente in embedded: è più complicato che restare nello stack. L'uso della memoria dinamica richiede procedure e strutture di dati speciali, mentre sullo stack è (per semplificare le cose) una questione di aggiungere / sottrarre un numero più alto dallo stackpointer.
tehftw

Sidenote: l'esempio "utilizzo di un buffer fisso [MAX_SIZE]" evidenzia il motivo per cui la politica di memoria di overcommit funziona così bene. I programmi allocano la memoria che potrebbero non toccare mai tranne i limiti della lunghezza del buffer. Quindi va bene che Linux (e altri SO) in realtà non assegnino una pagina di memoria fino al suo primo utilizzo (al contrario di malloc'd). Se il buffer è più grande di una pagina, il programma può utilizzare solo la prima pagina e non sprecare il resto della memoria fisica.
Katastic Voyage,

@KatasticVoyage A meno che MAX_SIZE non sia maggiore (o almeno uguale a) della dimensione della dimensione della pagina della memoria virtuale del sistema, l'argomento non contiene acqua. Anche su sistemi embedded senza memoria virtuale (molti MCU embedded non dispongono di MMU), la politica di memoria di overcommit può essere buona dal punto di vista "assicurati che il tuo programma funzionerà in tutte le situazioni", ma che la garanzia ha il prezzo della dimensione dello stack deve essere allo stesso modo allocato per supportare i criteri di memoria di overcommit. Su alcuni sistemi embedded, questo è un prezzo che alcuni produttori di prodotti a basso costo non sono disposti a pagare.
Fonagger,

11

Tutti hanno già sottolineato la cosa importante che è un potenziale comportamento indefinito da un overflow dello stack, ma dovrei menzionare che l'ambiente Windows ha un ottimo meccanismo per catturarlo usando eccezioni strutturate (SEH) e pagine di guardia. Poiché la pila cresce solo quando necessario, queste pagine di guardia si trovano in aree non allocate. Se si assegna in essi (traboccando lo stack) viene generata un'eccezione.

Puoi catturare questa eccezione SEH e chiamare _resetstkoflw per ripristinare lo stack e continuare a modo tuo. Non è l'ideale, ma è un altro meccanismo per sapere almeno che qualcosa è andato storto quando la roba ha colpito il fan. * nix potrebbe avere qualcosa di simile di cui non sono a conoscenza.

Raccomando di limitare la dimensione massima dell'allocazione avvolgendo alloca e monitorandola internamente. Se ne fossi davvero entusiasta, potresti lanciare alcune sentinelle dell'ambito nella parte superiore della tua funzione per tenere traccia di tutte le allocazioni alloca nell'ambito della funzione e verificare la sanità mentale rispetto all'importo massimo consentito per il tuo progetto.

Inoltre, oltre a non consentire perdite di memoria, alloca non provoca frammentazione della memoria, il che è piuttosto importante. Non credo che l'alloca sia una cattiva pratica se la usi in modo intelligente, il che è sostanzialmente vero per tutto. :-)


Il problema è che alloca()può richiedere così tanto spazio che lo stackpointer atterra nell'heap. Con ciò, un utente malintenzionato che può controllare la dimensione alloca()e i dati che vanno in quel buffer può sovrascrivere l'heap (il che è molto male).
12431234123412341234123

SEH è una cosa solo per Windows. È fantastico se ti preoccupi solo del tuo codice in esecuzione su Windows, ma se il tuo codice deve essere multipiattaforma (o se stai scrivendo codice che funziona solo su una piattaforma non Windows), non puoi fare affidamento su SEH.
George,

10

alloca () è bello ed efficiente ... ma è anche profondamente rotto.

  • comportamento dell'ambito rotto (ambito funzione anziché ambito blocco)
  • usa incoerente con malloc (il puntatore alloca () non deve essere liberato, d'ora in poi devi tracciare da dove provengono i tuoi puntatori su free () solo quelli che hai con malloc () )
  • comportamento scorretto quando si utilizza anche l'allineamento (l'ambito a volte passa alla funzione chiamante a seconda che la chiamata sia inline o meno).
  • nessun controllo del limite dello stack
  • comportamento indefinito in caso di fallimento (non restituisce NULL come malloc ... e cosa significa fallimento in quanto non controlla comunque i limiti dello stack ...)
  • non standard ansi

Nella maggior parte dei casi è possibile sostituirlo utilizzando variabili locali e dimensioni maggiori. Se viene utilizzato per oggetti di grandi dimensioni, metterli sull'heap è di solito un'idea più sicura.

Se ne hai davvero bisogno C puoi usare VLA (no vla in C ++, peccato). Sono molto meglio di alloca () per quanto riguarda il comportamento e la coerenza dell'ambito. A mio avviso , i VLA sono una specie di alloca () fatto bene.

Naturalmente una struttura locale o un array che utilizza un majorant dello spazio necessario è ancora migliore, e se non si dispone di una tale allocazione di heap majorant usando semplicemente malloc () è probabilmente sano. Non vedo alcun caso di utilizzo sano in cui hai davvero bisogno di alloca () o VLA.


Non vedo il motivo del downvote (a proposito, senza alcun commento)
gd1

Solo i nomi hanno portata. allocanon crea un nome, solo un intervallo di memoria, che ha una durata .
curiousguy,

@curiousguy: stai semplicemente giocando con le parole. Per le variabili automatiche potrei anche parlare della durata della memoria sottostante in quanto corrisponde all'ambito del nome. Comunque il problema non è come lo chiamiamo, ma l'instabilità della vita / portata della memoria restituita dall'alloca e il comportamento eccezionale.
Kriss,

2
Vorrei che alloca avesse avuto una "freea" corrispondente, con una specifica che la chiamata di "freea" avrebbe annullato gli effetti della "alloca" che ha creato l'oggetto e tutti quelli successivi, e un requisito che la memorizzazione "alloca" all'interno di una fucntion deve essere 'libero' anche al suo interno. Ciò avrebbe permesso a quasi tutte le implementazioni di supportare alloca / freea in modo compatibile, avrebbe alleggerito i problemi di allineamento e in generale reso le cose molto più pulite.
supercat

2
@supercat - Lo desidero anche io. Per questo motivo (e più), io uso un livello di astrazione (per lo più macro e funzioni inline) in modo che non ho mai chiamare allocao malloco freedirettamente. Dico cose come {stack|heap}_alloc_{bytes,items,struct,varstruct}e {stack|heap}_dealloc. Quindi, heap_deallocchiama freee basta stack_dealloc. In questo modo, le allocazioni di stack possono essere facilmente declassate per allocazioni di heap e anche le intenzioni sono più chiare.
Todd Lehman,

9

Ecco perché:

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

Non che nessuno scriva questo codice, ma l'argomento dimensioni a cui stai passando allocaproviene quasi sicuramente da una sorta di input, che potrebbe mirare maliziosamente a portare il tuo programma a allocaqualcosa di così grande. Dopotutto, se la dimensione non si basa sull'input o non ha la possibilità di essere grande, perché non hai semplicemente dichiarato un buffer locale di dimensioni fisse?

Praticamente tutto il codice che utilizza allocae / o C99 vlas presenta gravi bug che causano arresti anomali (se sei fortunato) o compromissione dei privilegi (se non sei così fortunato).


1
Il mondo potrebbe non saperlo mai. :( Detto questo, spero che tu possa chiarire una domanda che ho alloca. Hai detto che quasi tutto il codice che lo utilizza ha un bug, ma stavo pianificando di usarlo; normalmente ignorerei tale affermazione, ma venendo da te non lo farò. Sto scrivendo una macchina virtuale e mi piacerebbe allocare variabili che non sfuggono alla funzione nello stack, invece che dinamicamente, a causa dell'enorme accelerazione. approccio con le stesse caratteristiche prestazionali? So che posso avvicinarmi ai pool di memoria, ma che non è altrettanto economico. Cosa faresti?
GManNickG

7
Sai cos'è anche pericoloso? Questo: *0 = 9;INCREDIBILE !!! Immagino che non dovrei mai usare i puntatori (o almeno dereferirli). Ehm, aspetta. Posso provare per vedere se è nullo. Hmm. Immagino di poter anche testare la dimensione della memoria che voglio allocare via alloca. Uomo strano. Strano.
Thomas Eding,

7
*0=9;non è valido C. Per quanto riguarda il test delle dimensioni a cui si passa alloca, testarlo contro cosa? Non c'è modo di conoscere il limite e se lo testerai solo su una piccola dimensione nota sicura (ad esempio 8k), potresti anche usare un array di dimensioni fisse nello stack.
R .. GitHub smette di aiutare ICE il

7
Il problema con il tuo argomento "o la dimensione è abbastanza piccola o dipende dall'input e quindi potrebbe essere arbitrariamente grande" come vedo è che si applica altrettanto fortemente alla ricorsione. Un compromesso pratico (per entrambi i casi) è quello di supporre che se la dimensione è limitata small_constant * log(user_input), probabilmente abbiamo abbastanza memoria.
j_random_hacker

1
In effetti, hai identificato il caso ONE in cui VLA / alloca è utile: algoritmi ricorsivi in ​​cui lo spazio massimo necessario in qualsiasi frame di chiamata potrebbe essere grande quanto N, ma in cui la somma dello spazio necessario a tutti i livelli di ricorsione è N o qualche funzione di N che non cresce rapidamente.
R .. GitHub smette di aiutare ICE il

9

Non credo che qualcuno l'abbia menzionato: l'uso di alloca in una funzione ostacolerà o disabiliterà alcune ottimizzazioni che potrebbero altrimenti essere applicate nella funzione, poiché il compilatore non può conoscere la dimensione del frame di stack della funzione.

Ad esempio, un'ottimizzazione comune da parte dei compilatori C consiste nell'eliminare l'uso del puntatore al frame all'interno di una funzione, mentre gli accessi al frame vengono fatti relativamente al puntatore dello stack; quindi c'è un altro registro per uso generale. Ma se alloca viene chiamato all'interno della funzione, la differenza tra sp e fp sarà sconosciuta per parte della funzione, quindi questa ottimizzazione non può essere eseguita.

Data la rarità del suo utilizzo e il suo losco status di funzione standard, i progettisti di compilatori probabilmente disabilitano qualsiasi ottimizzazione che potrebbe causare problemi con alloca, se ci vorrebbe più di un piccolo sforzo per farlo funzionare con alloca.

AGGIORNAMENTO: poiché le matrici locali a lunghezza variabile sono state aggiunte a C e poiché presentano problemi di generazione del codice molto simili al compilatore come alloca, vedo che "rarità d'uso e stato losco" non si applicano al meccanismo sottostante; ma sospetterei comunque che l'uso di alloca o VLA tende a compromettere la generazione di codice all'interno di una funzione che li utilizza. Gradirei qualsiasi feedback dai progettisti del compilatore.


1
Le matrici a lunghezza variabile non sono mai state aggiunte al C ++.
Nir Friedman,

@NirFriedman Effettivamente. Penso che ci fosse un elenco di funzionalità di Wikipedia basato su una vecchia proposta.
Greggo,

> Sospetterei ancora che l'uso di alloca o VLA tende a compromettere la generazione del codice. Penserei che l'uso di alloca richieda un puntatore a frame, poiché il puntatore dello stack si sposta in modi non ovvi al momento della compilazione. alloca può essere chiamato in un ciclo per continuare ad afferrare più memoria dello stack, o con una dimensione calcolata in fase di esecuzione, ecc. Se c'è un puntatore al frame, il codice generato ha un riferimento stabile ai locali e il puntatore dello stack può fare quello che vuole; non è usato.
Kaz

8

Una trappola allocaè che lo longjmpriavvolge.

Vale a dire, se si salva un contesto con setjmp, quindi un allocapo 'di memoria, quindi longjmpnel contesto, si potrebbe perdere la allocamemoria. Il puntatore dello stack è tornato dov'era e quindi la memoria non è più riservata; se chiami una funzione o ne fai un'altra alloca, intaserai l'originale alloca.

Per chiarire, ciò a cui mi riferisco specificamente qui è una situazione in cui longjmpnon ritorna fuori dalla funzione in cui allocaè avvenuta! Piuttosto, una funzione salva il contesto con setjmp; quindi alloca la memoria con allocae infine un longjmp ha luogo in quel contesto. La allocamemoria di quella funzione non è completamente liberata; solo tutta la memoria che ha allocato dal setjmp. Certo, sto parlando di un comportamento osservato; nessun requisito di questo tipo è documentato da nessuno allocache io conosca.

L'attenzione nella documentazione è di solito sul concetto che la allocamemoria è associata a funzione , non a nessun blocco; che più invocazioni richiedono allocasolo più memoria dello stack che viene rilasciata al termine della funzione. Non così; la memoria è effettivamente associata al contesto della procedura. Quando il contesto viene ripristinato con longjmp, lo è anche lo allocastato precedente . È una conseguenza del registro puntatore dello stack stesso utilizzato per l'allocazione e anche (necessariamente) salvato e ripristinato nel file jmp_buf.

Per inciso, questo, se funziona in questo modo, fornisce un meccanismo plausibile per liberare deliberatamente la memoria assegnata alloca.

Mi sono imbattuto in questo come la causa principale di un bug.


1
Questo è ciò che dovrebbe fare però - longjmptorna indietro e lo fa in modo che il programma dimentichi tutto ciò che è accaduto nello stack: tutte le variabili, le chiamate di funzione ecc. Ed allocaè proprio come un array nello stack, quindi si prevede che saranno bloccati come tutto il resto in pila.
tehftw

1
man allocaha dato la seguente frase: "Poiché lo spazio allocato da alloca () è allocato all'interno del frame dello stack, quello spazio viene automaticamente liberato se il ritorno della funzione viene saltato da una chiamata a longjmp (3) o siglongjmp (3).". Quindi è documentato che la memoria allocata con allocaviene ostruita dopo a longjmp.
tehftw

@tehftw La situazione descritta si verifica senza che un ritorno di funzione venga saltato da longjmp. La funzione target non è ancora tornata. Ha fatto setjmp, allocae poi longjmp. L' longjmppuò riavvolgere il allocaretro dello Stato a quello che era in setjmptempo. Vale a dire, il puntatore spostato allocasoffre dello stesso problema di una variabile locale che non è stata contrassegnata volatile!
Kaz,

3
Non capisco perché dovrebbe essere inaspettato. Quando setjmppoi alloca, e poi longjmp, è normale che allocavenga riavvolto. Il punto longjmpè di tornare allo stato in cui è stato salvato setjmp!
tehftw

@tehftw Non ho mai visto documentare questa particolare interazione. Pertanto, non può essere invocato in alcun modo, tranne che da un'indagine empirica con i compilatori.
Kaz,

7

Un posto in cui alloca()è particolarmente pericoloso di quanto non lo malloc()sia il kernel - il kernel di un tipico sistema operativo ha uno spazio di stack di dimensioni fisse codificato in uno dei suoi header; non è flessibile come lo stack di un'applicazione. Effettuare una chiamata alloca()con dimensioni non garantite può causare l'arresto anomalo del kernel. Alcuni compilatori avvisano l'uso di alloca()(e persino VLA per questo) in base a determinate opzioni che dovrebbero essere attivate durante la compilazione di un codice del kernel - qui, è meglio allocare memoria nell'heap che non è fissata da un limite hardcoded.


7
alloca()non è più pericoloso di int foo[bar];dove si bartrova un numero intero arbitrario.
Todd Lehman,

@ToddLehman È corretto, e per questo motivo esatto abbiamo bandito i VLA nel kernel da diversi anni, e non abbiamo più VLA dal 2018 :-)
Chris Down

6

Se si scrive accidentalmente oltre il blocco allocato con alloca(ad esempio a causa di un overflow del buffer), si sovrascriverà l' indirizzo di ritorno della propria funzione, poiché quello si trova "sopra" nello stack, cioè dopo il blocco allocato.

_alloca block nello stack

La conseguenza di ciò è duplice:

  1. Il programma si bloccherà in modo spettacolare e sarà impossibile dire perché o dove si è bloccato (molto probabilmente lo stack si svolgerà a un indirizzo casuale a causa del puntatore del frame sovrascritto).

  2. Rende il buffer overflow molte volte più pericoloso, dal momento che un utente malintenzionato può creare uno speciale payload che verrebbe messo in pila e potrebbe quindi finire eseguito.

Al contrario, se si scrive oltre un blocco nell'heap "si" ottiene solo la corruzione dell'heap. Il programma probabilmente terminerà inaspettatamente, ma svolgerà correttamente lo stack, riducendo così la possibilità di esecuzione di codice dannoso.


11
Niente in questa situazione è drammaticamente diverso dai pericoli del buffer overflow di un buffer allocato in stack di dimensioni fisse. Questo pericolo non è unico alloca.
Fonagger

2
Ovviamente no. Ma per favore controlla la domanda originale. La domanda è: qual è il pericolo allocarispetto a malloc(quindi non un buffer di dimensioni fisse nello stack).
Rustyx,

Punto minore, ma alcuni stack crescono verso l'alto (ad es. Microprocessori PIC a 16 bit).
EBlake

5

Purtroppo il veramente fantastico alloca()manca dal quasi fantastico TCT. Gcc ha alloca().

  1. Semina il seme della propria distruzione. Con ritorno come distruttore.

  2. Come malloc()se restituisse un puntatore non valido in caso di errore che si segfault sui sistemi moderni con una MMU (e si spera di riavviare quelli senza).

  3. A differenza delle variabili automatiche, è possibile specificare la dimensione in fase di esecuzione.

Funziona bene con la ricorsione. È possibile utilizzare le variabili statiche per ottenere qualcosa di simile alla ricorsione della coda e utilizzare solo poche altre informazioni di passaggio a ciascuna iterazione.

Se spingi troppo in profondità, hai la certezza di un segfault (se hai una MMU).

Nota che malloc() non offre più in quanto restituisce NULL (che segfault anche se assegnato) quando il sistema ha esaurito la memoria. Cioè tutto ciò che puoi fare è cauzione o semplicemente provare ad assegnarlo in qualsiasi modo.

Per usare malloc()io uso i globi e li assegno NULL. Se il puntatore non è NULL, lo libero prima dell'usomalloc() .

È inoltre possibile utilizzare realloc()come caso generale se si desidera copiare eventuali dati esistenti. Devi controllare il puntatore prima di capire se hai intenzione di copiare o concatenare dopo il realloc().

3.2.5.2 Vantaggi dell'alloca


4
In realtà la specifica alloca non dice che restituisce un puntatore non valido in caso di fallimento (overflow dello stack) dice che ha un comportamento indefinito ... e per malloc dice che restituisce NULL, non un puntatore non valido casuale (OK, l'implementazione della memoria ottimistica di Linux lo rende inutili).
Kriss,

@kriss Linux potrebbe interrompere il tuo processo, ma almeno non si avventurerà in comportamenti indefiniti
craig65535

@ craig65535: l'espressione comportamento indefinito di solito significa che quel comportamento non è definito dalle specifiche C o C ++. Non sarà in alcun modo casuale o instabile su un determinato sistema operativo o compilatore. Pertanto non ha senso associare UB al nome di un sistema operativo come "Linux" o "Windows". Non c'entra niente.
Kriss,

Stavo cercando di dire che malloc che restituisce NULL, o nel caso di Linux, un accesso alla memoria che uccide il tuo processo, è preferibile al comportamento indefinito di alloca. Penso di aver letto male il tuo primo commento.
craig65535

3

I processi hanno solo una quantità limitata di spazio di stack disponibile, molto inferiore alla quantità di memoria disponibile malloc().

Utilizzando alloca()si aumenta notevolmente la probabilità di ottenere un errore di Stack Overflow (se siete fortunati, o un incidente inspiegabile se non sei).


Dipende molto dall'applicazione. Non è insolito che un'applicazione integrata con memoria limitata abbia dimensioni dello stack più grandi dell'heap (se esiste anche un heap).
EBlake

3

Non molto carino, ma se le prestazioni contano davvero, potresti preallocare un po 'di spazio nello stack.

Se hai già la dimensione massima del blocco di memoria di cui hai bisogno e vuoi mantenere i controlli di overflow, potresti fare qualcosa del tipo:

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

    (...)
}

12
L'array char è garantito per essere correttamente allineato per qualsiasi tipo di dati? alloca offre tale promessa.
Juho Östman,

@ JuhoÖstman: puoi usare una matrice di struct (o di qualunque tipo) invece di char se hai problemi di allineamento.
Kriss,

Si chiama array a lunghezza variabile . È supportato in C90 e versioni successive, ma non in C ++. Vedi Posso usare un array a lunghezza variabile C in C ++ 03 e C ++ 11?
JWW

3

La funzione alloca è ottima e tutti gli oppositori si stanno semplicemente diffondendo FUD.

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

L'array e il parray sono ESATTAMENTE gli stessi con ESATTAMENTE gli stessi rischi. Dire che uno è meglio di un altro è una scelta sintattica, non tecnica.

Per quanto riguarda la scelta delle variabili dello stack rispetto alle variabili heap, ci sono MOLTI vantaggi per i programmi a esecuzione prolungata che utilizzano stack over heap per le variabili con durata nell'ambito. Si evita la frammentazione dell'heap e si può evitare di aumentare lo spazio del processo con lo spazio heap non utilizzato (non utilizzabile). Non è necessario ripulirlo. È possibile controllare l'allocazione dello stack nel processo.

Perché è così male?


3

In realtà, alloca non è garantito l'uso della pila. In effetti, l'implementazione gcc-2.95 di alloca alloca memoria dall'heap usando lo stesso malloc. Inoltre, l'implementazione è errata, può causare una perdita di memoria e comportamenti imprevisti se la si chiama all'interno di un blocco con un ulteriore uso di goto. No, per dire che non dovresti mai usarlo, ma alcune volte l'alloca porta a un sovraccarico maggiore di quello che ti libera.


Sembra che gcc-2.95 abbia rotto alloca e probabilmente non può essere utilizzato in sicurezza per i programmi che lo richiedono alloca. Come avrebbe ripulito la memoria quando longjmpviene utilizzato per abbandonare i frame che lo hanno fatto alloca? Quando qualcuno userebbe gcc 2.95 oggi?
Kaz,

2

IMHO, alloca è considerata una cattiva pratica perché tutti hanno paura di esaurire il limite delle dimensioni dello stack.

Ho imparato molto leggendo questo thread e alcuni altri link:

Uso alloca principalmente per rendere i miei semplici file C compilabili su msvc e gcc senza alcuna modifica, stile C89, no #ifdef _MSC_VER, ecc.

Grazie ! Questa discussione mi ha fatto iscrivere a questo sito :)


Tieni presente che non esiste un "thread" in questo sito. Stack Overflow ha un formato di domande e risposte, non un formato di discussione. "Rispondi" non è come "Rispondi" in un forum di discussione; significa che stai effettivamente fornendo una risposta alla domanda e non dovrebbe essere usato per rispondere ad altre risposte o commenti sull'argomento. Una volta che hai almeno 50 rappresentanti, puoi pubblicare commenti , ma assicurati di leggere "Quando non dovrei commentare?" sezione. Leggere la pagina Informazioni per comprendere meglio il formato del sito.
Adi Inbar,

1

Secondo me, alloca (), ove disponibile, dovrebbe essere usato solo in modo limitato. Molto simile all'uso di "goto", un numero piuttosto elevato di persone altrimenti ragionevoli ha una forte avversione non solo all'uso, ma anche all'esistenza di alloca ().

Per l'uso incorporato, dove è nota la dimensione dello stack e i limiti possono essere imposti tramite convenzione e analisi sulla dimensione dell'allocazione e dove il compilatore non può essere aggiornato per supportare C99 +, l'uso di alloca () va bene, e sono stato noto per usarlo.

Se disponibili, i VLA possono presentare alcuni vantaggi rispetto a alloca (): il compilatore può generare controlli dei limiti dello stack che cattureranno l'accesso fuori limite quando viene utilizzato l'accesso in stile array (non so se alcuni compilatori lo fanno, ma può deve essere eseguito) e l'analisi del codice può determinare se le espressioni di accesso all'array sono delimitate correttamente. Si noti che, in alcuni ambienti di programmazione, come quello automobilistico, delle apparecchiature mediche e dell'avionica, questa analisi deve essere eseguita anche per array di dimensioni fisse, sia allocazione automatica (in pila) che statica (globale o locale).

Sulle architetture che archiviano sia i dati che gli indirizzi di ritorno / i puntatori di frame nello stack (da quello che so, sono tutti), qualsiasi variabile allocata nello stack può essere pericolosa perché l'indirizzo della variabile può essere preso e valori di input non controllati potrebbero consentire ogni sorta di malizia.

La portabilità è meno preoccupante nello spazio incorporato, tuttavia è un buon argomento contro l'uso di alloca () al di fuori di circostanze attentamente controllate.

Al di fuori dello spazio incorporato, ho usato alloca () principalmente all'interno delle funzioni di registrazione e formattazione per l'efficienza, e in uno scanner lessicale non ricorsivo, dove strutture temporanee (allocate usando alloca () vengono create durante la tokenizzazione e la classificazione, quindi un persistente L'oggetto (allocato tramite malloc ()) viene popolato prima che la funzione ritorni. L'uso di alloca () per le strutture temporanee più piccole riduce notevolmente la frammentazione quando viene allocato l'oggetto persistente.


1

La maggior parte delle risposte qui in gran parte mancano il punto: c'è un motivo per cui l'uso _alloca()è potenzialmente peggio che semplicemente archiviare oggetti di grandi dimensioni nello stack.

La differenza principale tra l'archiviazione automatica _alloca()è che quest'ultimo soffre di un ulteriore (grave) problema: il blocco allocato non è controllato dal compilatore , quindi non c'è modo per il compilatore di ottimizzarlo o riciclarlo.

Confrontare:

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

con:

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

Il problema con quest'ultimo dovrebbe essere ovvio.


Hai qualche esempio pratico che dimostra la differenza tra VLA e alloca(sì, dico VLA, perché allocaè più che un semplice creatore di array di dimensioni statiche)?
Ruslan,

Ci sono casi d'uso per il secondo, che il primo non supporta. Potrei voler avere 'n' record dopo che il ciclo è stato eseguito 'n' volte - forse in una lista collegata o in un albero; questa struttura di dati viene quindi eliminata quando la funzione alla fine ritorna. Il che non vuol dire che
codificherei

1
E direi che "il compilatore non può controllarlo" è perché è così che viene definito alloca (); i compilatori moderni sanno cos'è alloca e lo trattano in modo speciale; non è solo una funzione di libreria come era negli anni '80. I VLA C99 sono fondamentalmente alloca con ambito di blocco (e migliore digitazione). Non più o meno controllo, solo conforme a diverse semantiche.
Greggo,

@greggo: Se sei il downvoter, sarei felice di sapere perché pensi che la mia risposta non sia utile.
alecov

In C, il riciclo non è compito del compilatore, ma è compito della libreria c (free ()). alloca () viene liberato al ritorno.
Peter - Ripristina Monica il

1

Non penso che qualcuno l'abbia menzionato, ma alloca ha anche alcuni seri problemi di sicurezza non necessariamente presenti con malloc (anche se questi problemi sorgono anche con array basati su stack, dinamici o meno). Poiché la memoria è allocata nello stack, gli overflow / underflow del buffer hanno conseguenze molto più gravi rispetto al solo malloc.

In particolare, l'indirizzo di ritorno per una funzione è memorizzato nello stack. Se questo valore viene danneggiato, il codice potrebbe essere indirizzato a qualsiasi area della memoria eseguibile. I compilatori fanno di tutto per renderlo difficile (in particolare randomizzando il layout degli indirizzi). Tuttavia, questo è chiaramente peggio di un semplice overflow dello stack poiché il caso migliore è SEGFAULT se il valore restituito è danneggiato, ma potrebbe anche iniziare a eseguire un pezzo di memoria casuale o, nel peggiore dei casi, una regione di memoria che compromette la sicurezza del programma .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.