Newsgroup: it.comp.lang.c
Titolo: Chiarimenti  liste con malloc/realloc

 
 
Per esercitarmi con il C, ho scritto il codice riportato sotto. Il 
programmino non fa altro che chiedere il nome e il cognome e memorizzare 
i dati con il metodo dell'allocazione dinamica della memoria. Ad ogni 
ciclo, alloca un blocco di dimensione "dati".
Alla fine del ciclo (termina con l'invio al nome...) stampa tutti i dati 
e relativo indirizzo di memoria...
 
In rete trovo molti esempi  simile al mio programma con la solo 
differenza che utilizzano  le liste-ricorsione  (struct che punta a se 
stesso...).
 
 
 
Quali sono gli inconvenienti del mio metodo?
 
ciao
 
 
 
 
********  CODICE **********
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define LMAX 20 // lunghezza massima + 2 caratteri del nome e cognome
int main(void)
{
     typedef struct
     {
         char nome[LMAX];
         char cognome[LMAX];
     } dati;
 
     dati *impiegato;
     int c = 0;
 
 
     int  nb;
     int i=0;
     int  n=0;
 
 
     int allocati;  // byte allocati
     int dimbloc;   // byte in un blocco
     int dimint;    // byte in un intero
     int usati;     // byte contenenti interi
 
     nb = 1;
 
     dimint = sizeof(dati);
     dimbloc = nb * dimint;
     usati = 0;
 
     impiegato = (dati *)malloc(dimbloc); // allocazione iniziale della 
memoria
     if(impiegato == NULL)
         {
             printf("Memoria insufficiente\n");
             exit(1);
         }
 
     allocati = dimbloc;
 
     printf("  Massimo %d caratteri.\n    Invio per uscire.\n",LMAX-2);
     printf("*************************\n\n");
 
     while(1)
         {
 
 
             usati += dimint;
             if(usati>allocati)
                 {
                     allocati += dimbloc;
                     impiegato = (dati *)realloc(impiegato, allocati); 
// rialloco la memoria
 
                     if(impiegato == NULL)
                         {
                             printf("Memoria insufficiente\n");
                             exit(1);
                         }
                     i++;
                 }
 
             /*   Inizio. Memorizzo i dati nel vettore-struct  */
 
             // memorizzo il nome
             printf("Nome: ", LMAX-2);
             fgets(impiegato[n].nome, LMAX, stdin);
 
             if (  impiegato[n].nome[0]== '\n') break; //esco dal ciclo 
se si da l'invio
 
 
             c = impiegato[n].nome[strlen(impiegato[n].nome) - 1];
             if(c != '\n')
                 {
 
 
                     if (strlen(impiegato[n].nome)> LMAX-2)
                         {
                             impiegato[n].nome[strlen(impiegato[n].nome) 
- 1] = 0;
                             printf("Errore nel nome: sei andato oltre i 
%d caratteri\n",LMAX-2);
                         }
                     while((c = getchar()) != '\n');
 
                 }
             else
                 {
                     impiegato[n].nome[strlen(impiegato[n].nome) - 1] = 0;
                 }
 
 
 
             //memorizzo il cognome
             printf("Cognome: ", LMAX-2);
             fgets(impiegato[n].cognome, LMAX , stdin);
 
 
             c = impiegato[n].cognome[strlen(impiegato[n].cognome) - 1];
             if(c != '\n')
                 {
                     if (strlen(impiegato[n].cognome)> LMAX-2)
                         {
 
impiegato[n].cognome[strlen(impiegato[n].cognome) - 1] = 0;
                             printf("Errore nel cognome: sei andato 
oltre i %d caratteri\n" , LMAX-2);
                         }
                     while((c = getchar()) != '\n');
 
                 }
             else
                 {
                     impiegato[n].cognome[strlen(impiegato[n].cognome) - 
1] = 0;
                 }
 
             n++;
             printf("\n");
             /*   Fine. Memorizzo i dati nel vettore-struct  */
         }
 
 
     printf("\n   UTENTE:\n"); // stampo nome e cognome e relatico 
indirizzo di memoria
 
     for (i = 0; i < n; i++)
         {
             printf("Nome: %s", impiegato[i].nome); // visualizzo i 
numeri memorizzati nel vettore
             printf("  --> %p\n", impiegato[i].nome); // visualizzo 
l'indirizzo dei  numeri memorizzati nel vettore
             printf("Cognome: %s", impiegato[i].cognome); // visualizzo 
i numeri memorizzati nel vettore
             printf("  --> %p\n", impiegato[i].cognome); // visualizzo 
l'indirizzo dei  numeri memorizzati nel vettore
             printf("\n");
 
         }
     printf("\n");
 
     free(impiegato); // libero tutta la memoria allocata
 
     return 0;
}

 ciao
 
 
Non entro in merito al codice
Tu chiedi qual'è la differenza tra un array dinamico ed una lista e ti 
risponderò su questo
 
Un array dinamico ha l' inconveniente che se lo spazio allocato non è 
sufficiente occorre riallocare l' array,usando appunto la realloc.
La realloc non è detto che trovi spazio contiguo dopo la fine dello 
spazio allocato e se non lo trova deve per forza rifare un' altra 
allocazione (usando malloc),copiare i vecchi dati nel nuovo spazio e 
restituire il nuovo puntatore
Infatti quando si fa
n=realloc(p,...)  non è detto che n == p valga sempre
 
Per questioni di efficienza quindi, non si realloca un solo blocco per 
volta ma n blocchi
Un metodo classico e generico, è quello di raddoppiare la grandezza 
dell' array allocato ad ogni realloc
Ovviamente occorre tenere separati il numero di elementi dell' array e 
la sua effettiva grandezza
 
Una lista, al contrario di un array,non ha bisogno di spazio contiguo e 
quindi l' aggiunta di un elemento in coda è a tempo costante  se ho 
l'accortezza di tenere un puntatore sull' ultimo elemento
 
 
 
 
 

Quindi, se ho a disposizione solo due blocchi di memoria libera non 
contigua (ipotizziamo che la memoria del computer è al limite...), con 
l'array dinamico riuscirei solo a fare il primo malloc mentre mi 
restituirebbe NULL il realloc (perchè il secondo blocco di memoria 
libera è altrove...) . Con le liste riuscirei anche a fare il realloc ..
 
Ho capito bene il concetto?
 
Grazie


Se il primo ed il secondo blocco non sono  grandi quanto richiesto 
realloc restituisce NULL
 
Con le liste non è richiesta memoria contigua quindi riusciresti a fare 
una nuova malloc (la realloc non serve per le liste  a contenuto avente 
size costante)
 
 

 Con le liste non Ãš richiesta memoria contigua quindi riusciresti a fare



 
Stavo parlando della realloc, non di malloc
 
Fai conto di avere un puntatore P che punta ad una area di memoria con 
size  S anche se contiene solo elementi che occupano N (con N < S) e tua 
hai fatto una malloc(N)
Se fai una realloc su P con nuova size a N1 (con N1 < S)  realloc non 
va a  cercare un' altra area di memoria ma utilizza quella che è puntata 
da P
 
Se invece  fai una realloc su con nuova size S1 (con S1 > S) allora 
realloc  deve cercarsi o allocare una area di memoria con size S1
Se non la trova o non riesce ad allocarla restituisce NULL
Se invece ci riesce copia i dati di N in S1, libera N usando free, e 
restituisce un puntatore a S1 (o N1 che è la stessa cosa)
 
Tutti questi giochetti interni sono,ovviamente, trasparenti al programmatore
 
Se vuoi sapere se la realloc ha effettuato effettivamente una 
riallocazione basta un test di questo tipo:
 
T p=malloc(N)
T p1=realloc(p,N1)
 
if (p == p1) {
	// ha riutilizzato lo spazio precedentemente allocato
}
else {
	// ha effettivamente riallocato
}