PDA

Visualizza la versione completa : [C] Puntatore a char* e rilascio della memoria occupata


XWolverineX
28-10-2006, 14:10
salve.
Mi stavo chiedendo, se io faccio


char *testo = "Ciao";


Alla fine del programma, devo liberare la memoria che ha occupato?

andbin
28-10-2006, 14:56
Originariamente inviato da XWolverineX
salve.
Mi stavo chiedendo, se io faccio


char *testo = "Ciao";


Alla fine del programma, devo liberare la memoria che ha occupato? No, perché quel "Ciao" è una stringa literal cioè una stringa costante che è conosciuta a tempo di compilazione. Il compilatore la posiziona in un segmento di dati (tipicamente read-only).

Marcoski
29-10-2006, 13:23
Devi liberare la memoria solo se fai una malloc.

Parlando di teoria molto spicciola, in un linguaggio di programmazione (C nel tuo caso) esistono due "tipologie" di memoria:

1. Lo stack
2. Lo heap

Lo stack è la memoria che viene allocata ogni volta che richiami una funzione. Per esempio se ho un programma del genere:



int main(){
x(3);

return 0;
}

int x(int i){
j(++i);
}

int j(int i){
return;
}



lo stack al momento della chiamata a j() nella funzione x() sarà:

j() --> int i = 4;
x() --> int i = 3;
main()

Ora ogni frame dello stack ha una zona di memoria che ti permette di memorizzare variabili locali e valori locali nel tuo caso

char *str = "ciao";

avresti avuto il frame main:

main() --> char *str --> 'ciao'

dove il valore del puntatore str sarebbe l'indirizzo di memoria interno al frame main dello stack.

Ora ogni volta che una funziona ritorna, il frame viene distrutto e quindi la memoria viene rilasciata. Infatti nel tuo caso proprio per questo motivo non serve liberare la memoria occupata dalla stringa. In pratica quando il tuo programma termina o più precisamente quando il main ritorna la memorica occupata da *str viene automaticamente liberata perchè il frame main dello stack viene liberato.

Qual'è il problema di questo meccanismo?
Non puoi allocarti strutture globali, a meno che non le allochi nello stack del main con ovvi problemi di gestione per le funzioni superiori al main, e inoltre allocare grandi strutture nello stack non è un modo efficente di gestione delle risorse in quanto è meglio mantenere uno stack piccolo che permette di liberare facilmente le risorse locali e di ritornare velocemente al chiamante (la funzione che alloca un frame nello stack).
Per questo motivo esiste lo Heap, una zona di memoria libera in cui allocare quello che si vuole in modo dinamico (nel senso che la memoria viene rilocata e assegnata al processo chiamante dal sistema operativo di volta in volta che se ne ha bisogno).

In C per allocare memoria nello Heap usi la funzione malloc (o uno dei sui cugini) la quale non fa null' altro che chiedere al kernel del tuo sistema operativo di assegnare una specifica quantità di memoria per il processo chiamante: infatti ti ritorna un puntatore a void che non significa puntatore a nulla, ma puntatore a memoria generalizzata.
In Java che è un linguaggio più evoluto del C si usa l' operatore new che serve a creare un oggetto. La JVM in automatico e nascosto ai nostri occhi farà una malloc per allocare la giusta dimensione della struttura da noi creata.

Il problema che sorge nell' uso di questa memoria è la frammentazione e l' uso sconsiderato che se ne può fare, cioè il dimenticarsi di liberare la memoria. In C esiste la funzione free(void *ptr) che non fa nientaltro che dire al Sistema operativo che la memoria allocata dall' indirizzo puntato da *ptr può essere riassegnata per un altro processo o sempre per lo stesso processo in una futura chiamata malloc.
In Java o comunque in tutti i linguaggi più moderni C# per esempio, è stata tolta la possibiltà al programmatore di liberare la memoria e si è introdotto un sistema automatico di liberazione della mamoria chiamato garbage collection. La garbage collection è una funzionalità, simile ad un crontab, di un linguaggio che si occupa di liberare autonomamente la memoria non più utilizzata utilizzando particolari algoritmi il più diffuso è il "mark and sweep".
I linguaggi moderni tolgono la possibilità di liberare la memoria manualmente per svariati motivi:
1. Per rendere il linguaggio più semplice.
2. Per rendere i programmi scritti in quel linguaggio più sicuri, perchè è cattiva norma di quasi tutti i programmatorii dimenticarsi da qualche parte del codice una free, il che è sintomo di cattiva programmazione e soprattutto tende a rendere programmi complessi insicuri
3. Rendere la programmazione più snella

Il problema del garbage collection è che poi i nostri programmi saranno più lenti. Vedi programmi scritti in Java.

Spero di essere stato abbastanza esaustivo.
Ciao

oregon
29-10-2006, 14:27
In tutto questo discorso, non so se ho capito un punto ...

Intendi dire che la stringa "ciao" del nostro esempio e' allocata nello stack?

andbin
29-10-2006, 14:46
Originariamente inviato da oregon
In tutto questo discorso, non so se ho capito un punto ...

Intendi dire che la stringa "ciao" del nostro esempio e' allocata nello stack? Nessun compilatore sarebbe così stupido da allocare i caratteri di una stringa literal sullo stack. :zizi:

oregon
29-10-2006, 14:59
Sicuramente ... ma dato che quello che si capiva dal discorso precedente era proprio questo, volevo sapere se avevo capito male io ... :madai!?: ... o Markosky non e' stato preciso ...

Marcoski
29-10-2006, 16:52
Originariamente inviato da oregon
puntatore chIn tutto questo discorso, non so se ho capito un punto ...

Intendi dire che la stringa "ciao" del nostro esempio e' allocata nello stack?

In effetti non sono stato preciso in quanto ho detto che "ciao" sta nel frame del main. Ovviemente è sbagliato, "ciao" è un valore costante che verrà messo nel segmento dati dal compilatore al momento della compilazione.

Rimane il fatto che il puntatore *testo rimane nello stack.
Quindi nello stack avrai memorizzato l'indirizzo di memoria del segmento dati dove è memorizzato "ciao0", in quanto altrimenti il compilatore non riuscirebbe a gestire lo scope di quella variabile.

oregon
29-10-2006, 16:59
Originariamente inviato da Marcoski
In effetti non sono stato preciso in quanto ho detto che "ciao" sta nel frame del main. Ovviemente è sbagliato, "ciao" è un valore costante che verrà messo nel segmento dati dal compilatore al momento della compilazione.

Rimane il fatto che il puntatore *testo rimane nello stack.
Quindi nello stack avrai memorizzato l'indirizzo di memoria del segmento dati dove è memorizzato "ciao0", in quanto altrimenti il compilatore non riuscirebbe a gestire lo scope di quella variabile.

Ecco ... questo era esattamente quello che intendevo ...

XWolverineX
29-10-2006, 19:59
Grazie per la delucidazione.
Il fatto che fosse un puntatore mi dava certi dubbi.
Credevo che alla fine del blocco fosse liberato il puntatore, ma non quello a cui punta.

Inoltre mi stavo anche chiedendo perchè



char *c = "Ciao"; // Ok
int *i = 10; //No


Ci ho riflettuto è credo che sia perchè
"Ciao"; è proprio un const char *
mentre 10 è un int e non un int *
Giusto?

andbin
29-10-2006, 20:45
Originariamente inviato da XWolverineX
Inoltre mi stavo anche chiedendo perchè



char *c = "Ciao"; // Ok
int *i = 10; //No


Ci ho riflettuto è credo che sia perchè
"Ciao"; è proprio un const char *
mentre 10 è un int e non un int *
Giusto? Qui c'è da fare attenzione:

char *c = "Ciao";
significa assegnare alla variabile 'c' (puntatore a char) l'indirizzo del primo carattere della stringa literal. E questo è ok.

int *i = 10;
significa assegnare alla variabile 'i' (puntatore a int) l'indirizzo 10!!
A parte il fatto che se proprio si volesse fare una cosa del genere, si dovrebbe mettere un cast esplicito:
int *i = (int*) 10;

infatti 10 viene valutato come valore intero, non come un indirizzo.
Ma comunque, quasi sicuramente, non sarebbe corretto lo stesso. Nei moderni sistemi operativi, l'indirizzo 10 non è di certo una locazione accessibile!!

Loading