Domanda

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 tramite <=> è un grosso mal di testa e se in qualche modo manca porta a ogni tipo di problema di memoria.

Perché l'uso di <=> è sconsigliato nonostante le funzionalità di cui sopra?

È stato utile?

Soluzione

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

  

VALORE DI RITORNO          La funzione alloca () restituisce un puntatore all'inizio di   spazio assegnato. Se la   cause di allocazione          stack overflow, 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 ingeni valori enormi), va bene. Una volta superato il & Quot; poche centinaia di byte & Quot; mark, è ora di usare malloc e gli amici, invece. Potresti comunque riscontrare errori di allocazione, ma almeno avrai qualche indicazione del fallimento invece di far esplodere lo stack.

Altri suggerimenti

Uno dei bug più memorabili che ho avuto è stato a che fare con una funzione in linea che utilizzava 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 funzione DoSomething in linea del compilatore e tutte le allocazioni dello stack stavano avvenendo all'interno della funzione Process() 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 <=>, era uno dei Macro per la conversione di stringhe ATL.

Quindi la lezione è: non usare <=> nelle funzioni che ritieni possano essere in linea.

Vecchia domanda, ma nessuno ha menzionato la necessità di sostituirla con matrici di lunghezza variabile.

char arr[size];

anziché

char *arr=alloca(size);

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

alloca () è molto utile se non puoi usare una variabile locale standard perché la sua dimensione dovrebbe essere determinata in fase di esecuzione e puoi garantisce assolutamente che il puntatore ottenuto da alloca () non verrà MAI utilizzato dopo il ritorno di questa funzione .

Puoi essere abbastanza sicuro se

  • non restituisce il puntatore o qualsiasi cosa lo contenga.
  • non memorizza il puntatore in nessuna struttura allocata sull'heap
  • non consente a nessun altro thread di utilizzare 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 :)

Come notato in la pubblicazione di questo newsgroup , ci sono alcuni motivi perché l'utilizzo di alloca può essere considerato difficile e pericoloso:

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

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.

  

l'uso di alloca è ancora sconsigliato, perché?

Non percepisco un simile 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 con un complessivo intuitivo
  • 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 un buono o un cattivo cosa - 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 ciclo while o for) o in più ambiti, la memoria si accumula per iterazione / ambito e non viene rilasciata fino alla chiusura della funzione: questo contrasta con le normali variabili definite nell'ambito di una struttura di controllo (ad es. for {int i = 0; i < 2; ++i) { X } accumulerebbe alloca - memoria richiesta per X, ma la memoria per un array di dimensioni fisse verrebbe riciclata per iterazione).
  • i compilatori moderni in genere non inline funzioni che chiamano malloc, ma se li forzate il WonderfulObject_DestructorFree(ptr) avverrà nel contesto dei chiamanti (cioè lo stack non verrà rilasciato fino a quando il chiamante non ritorna)
  • molto tempo fa WonderfulObject* p = WonderfulObject_AllocConstructor(); è passato da una funzionalità / hack non portatile a un'estensione standardizzata, ma potrebbe persistere una percezione negativa
  • la durata è legata all'ambito della funzione, che può o meno adattarsi al programmatore meglio del controllo esplicito di p
  • dover usare WonderfulObject_AllocConstructor incoraggia a pensare alla deallocazione - se gestita attraverso una funzione wrapper (ad es. free), 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 fare un po 'di registrazione) senza esplicite modifiche al codice client: a volte è un bel modello da adottare coerentemente
    • in questo stile di programmazione pseudo-OO, è naturale desiderare qualcosa come alloca() - questo è possibile quando " costruttore " è una funzione che restituisce realloc - ed memoria (poiché la memoria rimane allocata dopo che la funzione restituisce il valore da memorizzare in <=>), ma non se il " costruttore " usa <=>
      • una versione macro di <=> potrebbe raggiungere questo obiettivo, ma " le macro sono malvagie " in quanto possono essere in conflitto tra loro e con codice non macro e creare sostituzioni non intenzionali e conseguenti problemi difficili da diagnosticare
    • operazioni <=> mancanti possono essere rilevate da ValGrind, Purify ecc. ma manca " destructor " le chiamate non possono sempre essere rilevate affatto - un vantaggio molto debole in termini di applicazione dell'uso previsto; alcune <=> implementazioni (come GCC) usano una macro incorporata per <=>, quindi la sostituzione di runtime di una libreria diagnostica di utilizzo della memoria non è possibile come per <=> / <=> / <=> ( ad es. recinzione elettrica)
  • alcune implementazioni hanno problemi sottili: ad esempio, dalla manpage di Linux:

      

    Su molti sistemi alloca () non può essere usato all'interno dell'elenco degli argomenti di una chiamata di funzione, perché lo spazio dello stack riservato da alloca () apparirebbe nello stack nel mezzo 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 <=>: il codice seguente (e qui su ideone ) crea un vettore che tiene traccia di tipi polimorfici di dimensioni diverse che sono allocati in pila (con la durata legata al ritorno della funzione) anziché allocata in heap.

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

Tutte le altre risposte sono corrette. Tuttavia, se la cosa che vuoi allocare usando alloca() è ragionevolmente piccola, penso che sia una buona tecnica che è più veloce e più conveniente rispetto all'utilizzo di malloc() o altro.

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

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 prendere questa eccezione SEH e chiamare _resetstkoflw per resettare lo stack e continuare sulla buona strada. Non è l'ideale, ma è un altro meccanismo per sapere almeno che qualcosa è andato storto quando la roba colpisce 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 eventuali allocazioni alloca nell'ambito della funzione e controllo della 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. : -)

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

  • comportamento dell'ambito non funzionante (ambito della funzione anziché ambito del blocco)
  • usa incoerente con malloc ( puntatore alloca () non deve essere liberato, d'ora in poi devi tracciare da dove provengono i puntatori su solo gratuito () quelli che hai ottenuto 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, VLA sono una specie di alloca () corretto.

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

Molte risposte interessanti a questo " vecchio " domanda, anche alcune risposte relativamente nuove, ma non ho trovato nessuna che menzioni questo ....

  

Se usato correttamente e con cura, uso coerente di alloca()   (forse a livello di applicazione) per gestire allocazioni di lunghezza variabile di piccole dimensioni   (o V99 C99, ove disponibili) può portare a stack complessivo inferiore   crescita rispetto a un'implementazione altrimenti equivalente che utilizza sovradimensionato   array locali di lunghezza fissa. Quindi <=> può essere buono 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 di <=> 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 usa <=> nel qual caso dovresti aggiornare, o a meno che tu non usi <=> cicli interni, nel qual caso dovresti ... non usare <=> cicli 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 i programmatori integrati hanno poche alternative.

<=> (o VLA) potrebbe essere lo strumento giusto per il lavoro.

Ho visto il tempo & amp; ancora una volta in cui un programmatore crea un buffer allocato dallo stack " abbastanza grande da gestire l'eventuale 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 & Quot; solo per essere sicuro & Quot; quando generalmente utilizzerà solo 16 o meno di essi, e solo in casi molto rari possono usarne di più.) Un'alternativa è usare <=> o VLA e allocare solo lo spazio dello stack di cui la tua funzione ha bisogno, per evitare di caricare inutilmente lo stack. Speriamo 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 piccole allocazioni normali, 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 <=> ...

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

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 che stai passando a alloca proviene quasi sicuramente da una sorta di input, che potrebbe mirare maliziosamente a portare il tuo programma a <=> qualcosa 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 di piccole dimensioni?

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

Un posto in cui alloca() è particolarmente pericoloso di malloc() è 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 a <=> con dimensioni non garantite può causare l'arresto anomalo del kernel. Alcuni compilatori avvertono l'uso di <=> (e persino VLA per quel che riguarda) 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 è riparato da un hard- limite codificato.

Se si scrive accidentalmente oltre il blocco allocato con alloca (ad esempio a causa di un overflow del buffer), si sovrascriverà indirizzo di ritorno della propria funzione, perché quello si trova quot; sopra <> quot!; nello stack, ovvero dopo il blocco assegnato.

 _ blocco alloca 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à su 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 sull'heap si " solo " ottenere la corruzione dell'heap. Il programma probabilmente terminerà inaspettatamente, ma svolgerà correttamente lo stack, riducendo così la possibilità di esecuzione di codice dannoso.

Un insulto con alloca è che longjmp lo riavvolge.

Vale a dire, se si salva un contesto con setjmp, quindi jmp_buf un po 'di memoria, quindi <=> nel contesto, è possibile perdere la <=> memoria (senza alcun tipo di avviso). Il puntatore dello stack è tornato dov'era e quindi la memoria non è più riservata; se chiami una funzione o ne fai un'altra <=>, intaserai l'originale <=>.

Per chiarire, ciò a cui mi riferisco specificamente qui è una situazione in cui <=> non ritorna fuori dalla funzione in cui si è verificato <=>! Piuttosto, una funzione salva il contesto con <=>; quindi alloca memoria con <=> e infine un longjmp ha luogo in quel contesto. La memoria <=> di quella funzione non è completamente liberata; solo tutta la memoria che ha allocato dal <=>. Certo, sto parlando di un comportamento osservato; nessun requisito di questo tipo è documentato per qualsiasi <=> che conosco.

L'attenzione nella documentazione è di solito sul concetto che <=> la memoria è associata ad un'attivazione funzione , non ad alcun blocco; che più invocazioni di <=> catturano solo 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 <=>, lo è anche lo stato <=> precedente. È una conseguenza del registro puntatore dello stack stesso utilizzato per l'allocazione e anche (necessariamente) salvato e ripristinato in <=>.

Per inciso, questo, se funziona in questo modo, fornisce un meccanismo plausibile per liberare deliberatamente la memoria allocata con <=>.

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

Non credo che nessuno 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 del compilatore probabilmente disabilitano qualsiasi ottimizzazione che potrebbe causare problemi con alloca, se lo prenderebbe più di un piccolo sforzo per farlo funzionare con alloca.

UPDATE: Poiché le matrici locali a lunghezza variabile sono state aggiunte a C e 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.

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

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

  2. Come realloc() restituisce un puntatore non valido in caso di errore che si segfault sui sistemi moderni con un 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. Puoi utilizzare le variabili statiche per ottenere qualcosa di simile alla ricorsione della coda e usare solo poche altre informazioni per passare a ogni iterazione.

Se spingi troppo in profondità sei sicuro di un segfault (se hai una MMU).

Nota che <=> 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 <=> Uso i globi e li assegno NULL. Se il puntatore non è NULL, lo libero prima di utilizzare <=>.

Puoi anche usare <=> come caso generale se vuoi copiare qualsiasi dato esistente. Devi controllare il puntatore prima di capire se hai intenzione di copiare o concatenare dopo <=>.

3.2.5.2 Vantaggi dell'alloca

I processi hanno solo una quantità limitata di spazio di stack disponibile - molto meno della quantità di memoria disponibile a malloc().

Usando alloca() aumenti notevolmente le possibilità di ottenere un errore di overflow dello stack (se sei fortunato o un arresto inspiegabile se non lo sei).

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;

    (...)
}

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.

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 dell'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?

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

Ho imparato molto leggendo questa discussione e alcuni altri collegamenti:

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 :)

Secondo me, alloca (), ove disponibile, dovrebbe essere usato solo in modo limitato. Molto simile all'uso di & Quot; goto & Quot ;, 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, in cui la dimensione dello stack è nota e i limiti possono essere imposti tramite convenzione e analisi sulla dimensione dell'allocazione e in cui il compilatore non può essere aggiornato per supportare C99 +, l'uso di alloca () va bene, e I è stato conosciuto per usarlo.

Se disponibili, i VLA possono presentare alcuni vantaggi rispetto 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ò essere fatto) e l'analisi del codice può determinare se le espressioni di accesso all'array sono correttamente limitate. 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, tutto qui), qualsiasi variabile allocata nello stack può essere pericolosa perché l'indirizzo della variabile può essere preso e input non controllato i valori potrebbero consentire ogni sorta di danno.

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 oggetto persistente (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.

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

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

Confronto:

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

con:

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

Il problema con quest'ultimo dovrebbe essere ovvio.

Non credo che qualcuno l'abbia menzionato, ma alloca ha anche alcuni seri problemi di sicurezza non necessariamente presenti con malloc (sebbene questi problemi sorgano 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 .

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top