Domanda

Qualcuno qui recentemente mi ha fatto notare in un mio codice che sto usando

char* name = malloc(256*sizeof(char));
// more code
free(name);

Avevo l'impressione che questo modo di configurare un array fosse identico all'uso di

char name[256];

e che entrambi i modi richiederebbero l'uso di free (). Sbaglio e, in tal caso, qualcuno potrebbe spiegare in termini di basso livello qual è la differenza?

È stato utile?

Soluzione

Nel primo codice, la memoria viene allocata in modo dinamico sull'heap. Quella memoria deve essere liberata con free (). La sua durata è arbitraria: può oltrepassare i limiti delle funzioni, ecc.

Nel secondo codice, i 256 byte sono allocati nello stack e vengono automaticamente recuperati quando la funzione ritorna (o al termine del programma se è al di fuori di tutte le funzioni). Quindi non devi (e non puoi) chiamare gratis () su di esso. Non può perdere, ma non sopravvive oltre la fine della funzione.

Scegli tra i due in base ai requisiti per la memoria.

Addendum (Pax):

Se posso aggiungere a questo, Ned, la maggior parte delle implementazioni in genere fornirà più heap che stack (almeno per impostazione predefinita). Questo in genere non ha importanza per 256 byte a meno che tu non stia già esaurendo lo stack o stia facendo cose fortemente ricorsive.

Inoltre, sizeof (char) è sempre 1 secondo lo standard, quindi non è necessario quel superfluo moltiplicarsi. Anche se probabilmente il compilatore lo ottimizzerà, rende il codice IMNSHO brutto.

Fine addendum (Pax).

Altri suggerimenti

  

e che entrambi i modi richiederebbero l'uso di free ().

No, solo il primo ha bisogno dell'uso gratuito. Il secondo è allocato nello stack. Questo lo rende incredibilmente veloce da allocare. Guarda qui:

void doit() {
    /* ... */
    /* SP += 10 * sizeof(int) */
    int a[10];
    /* ... (using a) */

} /* SP -= 10 */

Quando lo crei, il compilatore al momento della compilazione ne conosce le dimensioni e alloca la giusta dimensione nello stack. Lo stack è un grosso pezzo di memoria continua situato da qualche parte. Mettere qualcosa nello stack aumenterà (o diminuirà a seconda della piattaforma) lo stackpointer. Uscire dall'ambito farà il contrario e l'array viene liberato. Ciò accadrà automaticamente. Pertanto, le variabili create in questo modo hanno una durata di archiviazione automatica .

L'uso di malloc è diverso. Ordinerà un grosso pezzo di memoria arbitrario (da un posto chiamato negozio indipendente ). Il runtime dovrà cercare un blocco di memoria ragionevolmente grande. La dimensione può essere determinata in fase di esecuzione, quindi il compilatore generalmente non può ottimizzarla al momento della compilazione. Poiché il puntatore può uscire dall'ambito o essere copiato, non esiste alcun accoppiamento inerente tra la memoria allocata e il puntatore a cui è assegnato l'indirizzo di memoria, quindi la memoria è ancora allocata anche se la funzione è stata lasciata molto tempo fa . Devi chiamare gratuitamente passandogli l'indirizzo che hai ricevuto da malloc manualmente se è arrivato il momento di farlo.

Alcuni " recenti " la forma di C, chiamata C99, consente di assegnare alle matrici una dimensione di runtime. Ad esempio, puoi fare:

void doit(int n) {
    int a[n]; // allocate n * sizeof(int) size on the stack */
}

Ma questa funzione dovrebbe essere evitata se non hai motivo di usarla. Uno dei motivi è che non è sicuro: se non è più disponibile memoria, tutto può succedere. Un altro è che C99 non è molto portatile tra i compilatori.

Esiste una terza possibilità, ovvero che l'array può essere dichiarato esterno a una funzione, ma staticamente, ad es.

// file foo.c
char name[256];

int foo() {
    // do something here.
}

Sono stato piuttosto sorpreso dalle risposte a un'altra domanda su SO che qualcuno ha ritenuto che ciò fosse inappropriato in C; qui non è nemmeno menzionato, e sono un po 'confuso e sorpreso (come " cosa stanno insegnando ai bambini a scuola in questi giorni? ") su questo.

Se si utilizza questa definizione, la memoria viene allocata staticamente, né sull'heap né sullo stack, ma nello spazio dati nell'immagine. Quindi non deve essere gestito come con malloc / free, né devi preoccuparti che l'indirizzo venga riutilizzato come faresti con una definizione automatica.

È utile richiamare il tutto " dichiarato " vs "definito" cosa qui. Ecco un esempio

/* example.c */

char buf1[256] ;           /* declared extern, defined in data space */
static char buf2[256] ;    /* declared static, defined in data space */
char * buf3 ;              /* declared extern, defined one ptr in data space */
int example(int c) {       /* c declared here, defined on stack */
    char buf4[256] ;       /* declared here, defined on stack   */
    char * buf5 = malloc(256)]   /* pointer declared here, defined on stack */
                           /* and buf4 is address of 256 bytes alloc'd on heap */
    buf3 = malloc(256);    /* now buf3 contains address of 256 more bytes on heap */

    return 0;              /* stack unwound; buf4 and buf5 lost.      */
                           /* NOTICE buf4 memory on heap still allocated */
                           /* so this leaks 256 bytes of memory */
}

Ora in un file completamente diverso

/* example2.c */

extern char buf1[];             /* gets the SAME chunk of memory as from example.c */
static char buf2[256];          /* DIFFERENT 256 char buffer than example.c */
extern char * buf3 ;            /* Same pointer as from example.c */
void undoes() {
     free(buf3);                /* this will work as long as example() called first */
     return ;
}

Questo non è corretto - la dichiarazione dell'array non richiede un libero. Inoltre, se si trova all'interno di una funzione, viene allocato nello stack (se la memoria serve) e viene automaticamente rilasciato con la funzione restituisce - non passare un riferimento ad essa indietro al chiamante!

Suddividi la tua dichiarazione

char* name = malloc(256*sizeof(char)); // one statement
char *name; // Step 1 declare pointer to character
name = malloc(256*sizeof(char)); // assign address to pointer of memory from heap
name[2]; // access 3rd item in array
*(name+2); // access 3rd item in array
name++; // move name to item 1

Traduzione: il nome ora è un puntatore al carattere a cui è assegnato l'indirizzo di memoria sull'heap

char name[256]; // declare an array on the stack
name++; // error name is a constant pointer
*(name+2); // access 3rd item in array
name[2]; // access 3rd item in array
char *p = name;
p[2]; // access 3rd item in array
*(p+2); // access 3rd item in array
p++; // move p to item 1
p[0]; // item 1 in array

Traduzione: Nome è un puntatore costante a un personaggio che punta ad un po 'di memoria nello stack

In C matrici e puntatori sono più o meno la stessa cosa. Le matrici sono costanti puntatori alla memoria. La differenza principale è che quando chiami malloc prendi la tua memoria dall'heap e qualsiasi memoria prelevata dall'heap deve essere liberata dall'heap. Quando si dichiara l'array con una dimensione, gli viene assegnata memoria dallo stack. Non è possibile liberare questa memoria perché la memoria libera viene creata per liberare memoria dall'heap. La memoria nello stack verrà automaticamente liberata quando l'unità di programma corrente ritorna. Nel secondo esempio free (p) sarebbe anche un errore. p è un puntatore alla matrice dei nomi nello stack. Quindi liberando p si sta tentando di liberare la memoria nello stack.

Questo non è diverso da:

int n = 10;
int *p = &n;

liberare p in questo caso sarebbe un errore perché p punta su n che è una variabile nello stack. Pertanto p mantiene una posizione di memoria nello stack e non può essere liberata.

int *p = (int *) malloc(sizeof(int));
*p = 10;
free(p);

in questo caso il free è corretto perché p indica una posizione di memoria sull'heap che è stata allocata da malloc.

a seconda di dove lo stai eseguendo, lo spazio dello stack potrebbe essere a un PREZZO enorme. Se, ad esempio, stai scrivendo il codice BREW per i telefoni Verizon / Alltel, in genere sei limitato agli stack minuscoli ma hai sempre maggiore accesso all'heap.

Inoltre, poiché char [] viene spesso utilizzato per le stringhe, non è una cattiva idea consentire al metodo di costruzione di stringhe di allocare la memoria necessaria per la stringa in questione, piuttosto che sperare che per sempre e sempre 256 (o qualunque numero deciderai) sarà sufficiente.

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