Come fai a evitare gli array di lunghezza variabile da arrestandosi quando non c'è abbastanza memoria?

StackOverflow https://stackoverflow.com/questions/4151172

  •  08-10-2019
  •  | 
  •  

Domanda

Prima array di lunghezza variabile sono state supportate, avrei dinamicamente allocare in questo modo:

int foo(size_t n)
{
    int *arr = malloc(n * sizeof int);
    if (!arr) return ENOMEM; /* not enough memory */
    .
    . else do stuff with arr[]
    .
    free(arr);
    return 0;
}

Con gli array di lunghezza variabile ora posso farlo sembrare più pulita:

int bar(size_t n)
{
    int arr[n];
    .
    . do stuff with arr[]
    .
    return 0;
}

Ma ora non ho "out of memory" controllo. In effetti, il programma si blocca se n è troppo grande.

Come posso garbo cauzione da bar (n) se n è troppo grande?

È stato utile?

Soluzione

La situazione è esattamente invariata da qualsiasi altro variabili locali - una dichiarazione come questa:

int baz(void)
{
    int arr[100000];
    .
    . do stuff with arr[]
    .
    return 0;
}

ha esattamente lo stesso problema. La "soluzione" è lo stesso come è sempre stato - non recurse troppo profondamente, e non assegnare grandi strutture di dati con durata di conservazione automatica (continuare ad usare malloc() per questi casi). Il valore di "molto grande" dipende fortemente dal vostro ambiente.

In altre parole, non si dichiara int array[n]; a meno che non si sa che n è delimitata a un valore ragionevole, in modo tale che si sarebbe stato felice di dichiarare un array di dimensione massima di un ordinario, un array di tipo non-variabile-modificato .

(Sì, questo significa che in modo variabile modificati array di tipo non sono così utili come primo apparire, dal momento che si guadagna molto poco nel corso appena si dichiara la matrice alla dimensione massima necessario).

Altri suggerimenti

E 'possibile impedire loro di crash non usando loro. :)

In tutta serietà, non v'è quasi nessun modo sicuro da usare array di lunghezza variabile per rendere la vita più facile a meno che non si hanno forti limiti sulla dimensione. D'altra parte, è possibile utilizzare in modo condizionale, in modo simile a questo:

char vla_buf[n < 1000 ? n : 1];
char *buf = sizeof vla_buf < n ? malloc(n) : vla_buf;
if (!buf) goto error;
/* ... Do stuff with buf ... */
if (buf != vla_buf) free(buf);

Mentre questo appare come il dolore inutile, può fare una differenza di prestazioni enorme, soprattutto nelle applicazioni filettate dove molte chiamate a malloc e free potrebbero risultare in conflitto di blocco. (Un vantaggio collaterale notevole di questo trucco è che è possibile supportare i vecchi compilatori senza VLA con la semplice sostituzione [n < 1000 ? n : 1] con 1000, per esempio con una macro.)

Un altro caso oscuro dove i VLA possono essere utili sia in algoritmi ricorsivi in ??cui si conosce il numero totale di elementi dell'array richiesti a tutti i livelli di ricorsione è delimitata da n, dove n è abbastanza piccolo che siete sicuri che non sarà in overflow il pila, ma dove ci potrebbero essere fino a livelli n di ricorsione e singoli livelli, che usano fino ad elementi n. Prima di C99, l'unico modo per gestire questo caso senza prendere spazio di stack n^2 era quello di utilizzare malloc. Con Vlas, è possibile risolvere il problema del tutto sullo stack.

Tenete a mente, questi i casi in cui i VLA sono davvero utile sono dannatamente rare. Normalmente VLA è solo un modo di ingannare te stesso che la gestione della memoria è facile, fino ad arrivare a poco a conseguenti (banale-to-exploit) vulnerabilità che hai creato. : -)

Modifica Per una migliore domanda iniziale indirizzo di OP:

#define MAX_VLA 10000
int bar(size_t n)
{
    int arr[n <= MAX_VLA ? n : 1];
    if (sizeof arr/sizeof *arr < n) return ENOMEM;
    /* ... */
    return 0;
}

In realtà è proibitivo per verificare la presenza di condizioni di memoria in tutto il mondo. Il modo enterprisy di trattare con i dati di massa è di dimensioni dei dati limite definendo hard cap in formato a singolo punto di controllo precoce e non riescono veloce e con grazia quando il tappo è successo.

Quello che ho appena suggerito è semplice e stupido. Ma la sua ciò che ogni prodotto ordinario (non-scientifica o speciale) fa sempre. E 'quello che normalmente ci si aspetta da parte del cliente.

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