Perché dovrei usare malloc () quando “char bigchar [1u < < 31 - 1]; ”funziona bene?

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

  •  22-07-2019
  •  | 
  •  

Domanda

Qual è il vantaggio dell'utilizzo di malloc (oltre al ritorno NULL in caso di errore) rispetto agli array statici? Il seguente programma consumerà tutto il mio ariete e inizierà a riempire lo scambio solo se i loop sono decommentati. Non si arresta in modo anomalo.

...

#include <stdio.h>

unsigned int bigint[ 1u << 29 - 1 ];
unsigned char bigchar[ 1u << 31 - 1 ];

int main (int argc, char **argv) {
  int i;
/*   for (i = 0; i < 1u << 29 - 1; i++) bigint[i] = i; */
/*   for (i = 0; i < 1u << 31 - 1; i++) bigchar[i] = i & 0xFF; */

  getchar();
  return 0;
}

...

Dopo alcuni tentativi ed errori ho scoperto che quanto sopra è l'array statico più grande consentito sulla mia macchina Intel a 32 bit con GCC 4.3. È un limite standard, un limite del compilatore o un limite della macchina? Apparentemente posso averne quanti ne voglio. Sarà segfault, ma solo se chiedessi (e provassi a usare) più di quanto malloc mi darebbe comunque.

C'è un modo per determinare se un array statico è stato effettivamente allocato e sicuro da usare?

EDIT: sono interessato al motivo per cui malloc viene utilizzato per gestire l'heap anziché lasciare che il sistema di memoria virtuale lo gestisca. Apparentemente posso dimensionare un array a molte volte le dimensioni di cui penso avrò bisogno e il sistema di memoria virtuale manterrà solo ciò che è necessario. Se non scrivo mai ad es la fine (o l'inizio) di questi enormi array, quindi il programma non utilizza la memoria fisica. Inoltre, se posso scrivere in ogni posizione, cosa fa malloc oltre a incrementare un puntatore nell'heap o cercare attorno alle allocazioni precedenti nello stesso processo?

Nota dell'editore: 1 < < 31 provoca un comportamento indefinito se int è A 32 bit, quindi ho modificato la domanda per leggere 1u . L'intento della domanda è di porre domande sull'allocazione di grandi buffer statici.

È stato utile?

Soluzione

Beh, per due motivi davvero:

  1. A causa della portabilità, poiché alcuni sistemi non eseguiranno la gestione della memoria virtuale per te.

  2. Dovrai inevitabilmente dividere questo array in blocchi più piccoli perché sia ??utile, quindi tenere traccia di tutti i blocchi, quindi alla fine mentre inizi " liberando " alcuni dei pezzi dell'array che non ti servono più ti colpiranno il problema della frammentazione della memoria .

Tutto sommato finirai per implementare molte funzionalità di gestione della memoria (in realtà praticamente reimplementando il malloc) senza il vantaggio della portabilità.

Da qui i motivi:

  • Portabilità del codice tramite incapsulamento e standardizzazione della gestione della memoria.

  • Miglioramento della produttività personale mediante il riutilizzo del codice.

Altri suggerimenti

con malloc puoi aumentare e ridurre l'array: diventa dinamico, quindi puoi allocare esattamente ciò di cui hai bisogno.

Questo è chiamato gestione della memoria personalizzata, immagino. Puoi farlo, ma dovrai gestire tu stesso quel pezzo di memoria. Finiresti per scrivere il tuo malloc () che vaga su questo pezzo.

Per quanto riguarda:

  

Dopo qualche prova ed errore ho trovato il   sopra è l'array statico più grande   consentito sulla mia macchina Intel a 32 bit   con GCC 4.3. È uno standard?   limite, un limite del compilatore o una macchina   limitare?

Un limite superiore dipenderà dal modo in cui lo spazio di indirizzi virtuali da 4 GB (32 bit) viene suddiviso tra spazio utente e spazio kernel. Per Linux, credo che lo schema di partizionamento più comune abbia un intervallo di indirizzi da 3 GB per lo spazio utente e un intervallo di indirizzi da 1 GB per lo spazio kernel. Il partizionamento è configurabile in fase di compilazione del kernel, sono anche in uso divisioni da 2 GB / 2 GB e 1 GB / 3 GB. Quando l'eseguibile viene caricato, lo spazio di indirizzi virtuali deve essere allocato per ogni oggetto indipendentemente dal fatto che la memoria reale sia allocata per il backup.

Potresti essere in grado di allocare quel gigantesco array in un contesto, ma non in altri. Ad esempio, se l'array è un membro di una struttura e si desidera passare la struttura in giro. Alcuni ambienti hanno un limite di 32 KB per le dimensioni della struttura.

Come accennato in precedenza, puoi anche ridimensionare la memoria per utilizzare esattamente ciò di cui hai bisogno. In contesti critici per le prestazioni è importante non eseguire il paging nella memoria virtuale se può essere evitato.

Non c'è modo di liberare l'allocazione dello stack se non uscire dall'ambito. Pertanto, quando si utilizza effettivamente l'allocazione globale e la VM deve allocare memoria reale reale, viene allocata e rimarrà lì fino allo scadere del programma. Ciò significa che qualsiasi processo aumenterà solo nell'uso della memoria virtuale (le funzioni hanno allocazioni di stack locali e quelle saranno "liberate").

Non puoi " mantenere " la memoria dello stack una volta esaurita la funzione, viene sempre liberata. Quindi devi sapere quanta memoria userai al momento della compilazione.

Il che si riduce a quanti int foo [1 < < 29] puoi avere. Poiché il primo occupa tutta la memoria (a 32 bit) e sarà (diciamo: 0x000000) il secondo si risolverà in 0xffffffff o nei dintorni. Quindi il terzo si risolverebbe in cosa? Qualcosa che i puntatori a 32 bit non possono esprimere. (ricorda che le prenotazioni dello stack vengono risolte parzialmente in fase di compilazione, in parte in fase di esecuzione, tramite offset, fino a che punto viene spostato l'offset dello stack quando si alloca questa o quella variabile).

Quindi la risposta è praticamente che una volta che hai int foo [1 < < 29] non puoi più avere una ragionevole profondità di funzioni con altre variabili dello stack locale.

Dovresti davvero evitare di farlo a meno che tu non sappia cosa stai facendo. Prova a richiedere solo tutta la memoria di cui hai bisogno. Anche se non viene utilizzato o ostacolato da altri programmi, può incasinare il processo da solo. Ci sono due ragioni per questo. In primo luogo, su alcuni sistemi, in particolare su quelli a 32 bit, può causare l'esaurimento prematuro dello spazio degli indirizzi in rare circostanze. Inoltre, molti kernel hanno una sorta di limite per processo sulla memoria riservata / virtuale / non in uso. Se il tuo programma richiede memoria nei punti in fase di esecuzione, il kernel può terminare il processo se chiede che la memoria sia riservata che superi questo limite. Ho visto programmi che si sono arrestati in modo anomalo o sono usciti a causa di un errore di malloc perché stanno riservando GB di memoria mentre usano solo pochi MB.

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