p = realloc(p, newsize);nel caso in cui la realloc non possa ridimensionare il blocco di memoria, corre il rischio di perdere ogni riferimento al blocco inizialmente puntato da
p
. In particolare, dopo un tale errore non si potra' piu' accedere al contenuto del blocco, ne' il blocco potra' essere deallocato.
Nel caso in cui come conseguenza del mancato ridimensionamento del blocco il programma debba necessariamente terminare, quanto appena visto non e' un problema. Se invece, come conseguenza del mancato ridimensionamento il programma deve proseguire provvedendo in modo diverso all'allocazione di nuova memoria, il precedente codice e' un grave errore (errore che e' stato riscontrato anche in sofware commerciali).
La maniera corretta di procedere e' pertanto quella di utilizzare un puntatore di appoggio, verificare se il puntatore e' NULL, e infine, nel caso di ridimensionamento completato con successo, spostare il puntatore nella variabile da usare per il riferimento al blocco.
Questo corretto modo di procedere e' mostrato nell'esempio realloc3.c . L'esempio legge una sequenza di numeri e li memorizza in un vettore che cresce man mano che si leggono i numeri. Per cercare di minimizzare il numero di chiamate alla realloc, si parte da un vettore di dimensione base e, quando necessario, si raddoppia la dimensione del vettore (un altro approccio potrebbe essere quello di aggiungere un blocco di dimensione fissa). La dimensione del blocco iniziale puo' essere scelta in ragione della lunghezza media (o minima) dell'input. In una fase iniziale dello sviluppo, per verificare il corretto funzionamento del programmma, si puo' partire da una dimensione molto piccola che costringa ad eseguire immediatamente il ridimensionamento del vettore.
Per quanto riguarda l'uso della realloc si veda anche:
void g(char *v) { printf("g: %s\n", v); } char *f(char c) { char v[3]; v[0] = c = v[1] = c; v[2] = '\0'; g(v); return v; } int main(void) { char *v = f('a'); printf("main: %s\n", v); }La funzione
f
ha tra le sue variabili locali un array di caratteri v
. Il tempo di vita di questo array e' pari al tempo in cui rimane attiva la funzione f
. In particolare, l'array v
rimane allocato se la funzione f
chiama un'altra funzione. E' pertanto corretto passare alla funzione g
chiamata da f
il puntatore a v
. Invece, al termine di f
il vettore v
viene deallocato ed e' pertanto errato restituire alla funzione che ha chiamato f
un riferimento a tale vettore per comunicare come risultato la stringa in esso contenuto.
Si osservi anche che, siccome l'indirizzo di v
e' un indirizzo valido, il precedente programma e' sintatticamente lecito e molto probabilmente, se lo si compila e lo si esegue, si otterra' come output:
g: aa main: aache sembrerebbe indicare che nonostante tutto il main ha ricevuto come valore di ritorno proprio la stringa "aa". Per pura fortuna questo e' vero (ed e' legato al sistema e al compilatore usati per questo esempio). Il problema e' che il vettore
v
, allocato sullo stack al momento della chiamata della funzione, non viene immediatamente cancellato, e al termine di f
si trova su di una parte dello stack non piu' utilizzata e sulla quale andranno ad allocare le proprie variabili locali (e non solo) le successive chiamate di funzione. Nell'esempio, dato che non c'e' nessuna chiamata di funzione tra il termine di f
e la stampa della stringa ricevuta da f
, questa parte di stack non e' stata riscritta e il valore che si ottiene e' proprio quello che vi ha lasciato la funzione f
. Anche se per fortuna il prgramma si comporta come voluto, basare il corretto funzionamento del programma su considerazioni di questo tipo e' comunque un errore. Infatti, lo stack puo' essere utilizzato per memorizzare valori temporanei anche indipendentemente dalla chiamata di funzioni (ad esempio, nel calcolo di espressioni complesse).
Per verificare esplicitamente che il contenuto della stringa restituita da f
potrebbe cambiare a seconda di quello che segue. Si provi a compilare ed eseguire il seguente main:
int main(void) { char *v = f('a'); char *w = f('b'); printf("main: %s\n", v); }E' molto probabile che l'output sia qualcosa del tipo:
g: aa g: bb main: bbanche se, a seconda del sistema e del compilatore che si sta usando, si potrebbe ottenere un output diverso o verifarsi un errore di esecuzione. Infine, il
gcc
riconosce il potenziale errore legato alla restituzione dell'indirizzo di v
con un warning del tipo:
warning: function returns address of local variableSi osservi comunque che l'assenza di warning di tale tipo non garantisce l'assenza del problema. Infatti, la situazione potrebbe essere piu' complessa e l'indirizzo del vettore
v
potrebbe essere restituito in modo indiretto per mezzo di un puntatore. Ad esempio, compilando questa versione della funzione
char *f(char c) { char v[3]; char *p = v; v[0] = c = v[1] = c; v[2] = '\0'; g(v); return p; }non si hanno warning, ma si ha lo stesso tipo di problema.
char v[3]; char *f(char c) { v[0] = c = v[1] = c; v[2] = '\0'; g(v); return v; } int main(void) { char *v = f('a'); char *w = f('b'); printf("%s %s\n", v, w); }Le stringhe =v= e =w= contengono lo stesso valore, anzi fanno riferimento sempre allo stesso indirizzo di memoria: il valore che si pensava di aver ottenuto in =v= dopo la prima chiamata di =f= sara' sovrascritto dalla seconda chiamata di =f=. Pertanto, se si vuole che =v= e =w= siano stringhe diverse, non resta che allocare dinamicamente la memoria ad ogni chiamata di =f= (o alternativamente copiare in una nuova area di memoria il valore restituito da =f= prima di una nuova chiamata). -- Users.StefanoGuerrini - 02 Apr 2007
I | Attachment | History | Action | Size | Date | Who | Comment |
---|---|---|---|---|---|---|---|
![]() |
realloc1.c | r1 | manage | 0.7 K | 2007-04-03 - 16:45 | StefanoGuerrini | Realloc: esempio di uso improprio |
![]() |
realloc3.c | r1 | manage | 1.5 K | 2007-04-03 - 16:46 | StefanoGuerrini | Realloc: minimizzazione del numero di chiamate |
![]() |
![]() |
Questo sito usa cookies, usandolo ne accettate la presenza. (CookiePolicy)
Torna al Dipartimento di Informatica ![]() |
|
![]() |
![]() |