Allocazione dinamica C, malloc e free

abaco90
Ciao a tutti,

sto cercando di capire il funzionamento dell'allocazione dinamica della memoria in C, in particolare lo svuotamento della memoria.

Ad esempio, ho scritto il seguente codice che stampa una stringa allocata dinamicamente composta da 10 caratteri 'a'. La memoria allocata viene poi liberata.

char* stampa_stringa() {

    int i = 0;

    char *stringa;

    stringa = (char*) malloc(10 * sizeof(char));

    while (i < 10) {

        stringa[i] = 'a';

        i++;
    }

    return stringa;
}

int main() {

    char *stringa;

    stringa = stampa_stringa();

    printf("%s\n", stringa);

    free(stringa);

    return 0;
}


Ho un dubbio però... è giusto scrivere la funzione free nel main o va scritta nella funzione stampa_stringa?

Come faccio a capire se l'allocazione e lo svuotamento della memoria sono andati a buon fine?

Risposte
vict85
C'è un errore: stai allocando 10 elementi per una stringa di 10 elementi. Devi allocarne 11 e porre l'undicesimo elemento uguale a '\0'.

Il resto va relativamente bene. Puoi chiamare malloc dove ti pare, non mi è quindi chiaro il tuo dubbio. La funzione comunque ha un nome discutibile dato che non stampa nulla.

Malloc ritorna NULL in caso di problemi. Per quanto riguarda free puoi dare per scontato che sia andata a buon fine.

Nota comunque che [inline]sizeof(char)[/inline] è sempre uguale a 1.

abaco90
C'è un errore: stai allocando 10 elementi per una stringa di 10 elementi. Devi allocarne 11 e porre l'undicesimo elemento uguale a '\0'.

In effetti hai ragione, ma se eseguo il codice da terminale viene stampata la stringa "aaaaaaaaaa", che è composta da 10 caratteri, com'è possibile?

Puoi chiamare malloc dove ti pare, non mi è quindi chiaro il tuo dubbio.

Il mio dubbio era relativo alla funzione free.
Ad esempio, ho una funzione chiamata dal main nella quale alloco dinamicamente un array, ma la funzione ritorna un valore intero. Per liberare l'area di memoria allocata per l'array non posso più fare dal main free(array), come ho fatto nel codice in alto, dato che array è dichiarato solo nella funzione; come devo fare?

Grazie

oki2
se eseguo il codice da terminale viene stampata la stringa "aaaaaaaaaa", che è composta da 10 caratteri, com'è possibile?


La funzione printf invia al terminale i caratteri della stringa fornita come argomento, uno dopo l'altro, fino a che non ne viene trovato uno uguale a \0. È possibile che in quelle particolari condizioni di compilazione o esecuzione l'undicesimo carattere valga proprio \0, benché il tuo programma non faccia nulla per assicurarlo. Perciò penso che il risultato del programma sia indeterminato.

Ad esempio, ho una funzione chiamata dal main nella quale alloco dinamicamente un array, ma la funzione ritorna un valore intero. Per liberare l'area di memoria allocata per l'array non posso più fare dal main free(array), come ho fatto nel codice in alto, dato che array è dichiarato solo nella funzione; come devo fare?


Nel tuo programma la funzione alloca memoria dinamicamente perché vuole consegnare alla funzione chiamante una stringa il cui tempo di vita supera quello della funzione che l'ha creata. Se, come dici, anziché una stringa fosse richiesto restituire un intero, la tua funzione potrebbe fare a meno di allocare memoria dinamicamente, e nessuno dovrebbe invocare alcuna free.

Se la tua funzione dovesse restituire sia un intero sia un indirizzo di memoria allocata dinamicamente, potrebbe farlo restituendo una struct contenente entrambe le variabili (int e char*).
In alternativa potrebbe essere la funzione chiamante ad allocare (dinamicamente o automaticamente) la memoria per la funzione chiamata, quindi passarne l'indirizzo a quest'ultima tramite un parametro formale. Dopo che la funzione sia stata eseguita il controllo di quella memoria tornerebbe alla chiamante, che invocherebbe free nel caso in cui tale memoria sia stata allocata con malloc (o una delle sue varianti).

Ci sono anche altri modi per fare ciò che dici, ma non penso sia il caso di anticipare ora ulteriori costrutti.

abaco90
Grazie della risposta.

In alternativa potrebbe essere la funzione chiamante ad allocare (dinamicamente o automaticamente) la memoria per la funzione chiamata, quindi passarne l'indirizzo a quest'ultima tramite un parametro formale. Dopo che la funzione sia stata eseguita il controllo di quella memoria tornerebbe alla chiamante, che invocherebbe free nel caso in cui tale memoria sia stata allocata con malloc (o una delle sue varianti).


Ho provato un po' ed ho visto che posso anche dichiarare nel main un array di tipo **array, passarlo alla funzione che lo alloca dinamicamente e lo modifica. Finita la funzione posso accedere all'array dal main chiamando *array.
Ma penso che sia la stessa cosa che hai detto tu, solo che alloco nella funzione e non nel main.

oki2
OK.

Solo una raccomandazione: non assumere che un'allocazione dinamica abbia sempre successo.

Anche se il tuo programma alloca dinamicamente soltanto poca memoria, il sistema ospite potrebbe trovarsi in condizioni di scarsità di memoria già prima di lanciarlo, quindi anche la più piccola allocazione dinamica può fallire.

abaco90
Grazie mille!

Rispondi
Per rispondere a questa discussione devi prima effettuare il login.