Puntatori a struttura




Riprendiamo l'esercizio della scorsa settimana.



#include <stdio.h>     // per printf()
#include <string.h>    // per le funzioni di gestione delle stringhe
#include <stdlib.h>    // per malloc()

struct temp{char titolo[100];
                 char *autore;
                 int collocazione;};


typedef struct temp libro;

int main()
{int i=0;
 char temp[4];
 libro elenco[30];

// ciclo di inizializzazione
 for(i = 0;i < 30 ; i++)
  {
   elenco[i].collocazione = 0 ;
   elenco[i].titolo[0] = '\0' ;
   // o, in modo analogo
   * ( elenco[i].titolo ) = '\0' ;
   // allocazione di memoria
     elenco[i].autore = (char *) malloc ( sizeof(char) * (50 + 1) );
  }
// fine ciclo di inizializzazione

// ciclo di assegnazione
for( i = 0 ; i < 30 ; i ++ )
  {
    printf(" Vuoi inserire autore e titolo di un libro ? (s/n)");
    scanf("%s",temp);
    if( strncmp(temp,"s",1) == 0 ) break;    // in questo modo anche viene accettato
    printf("\n Inserisci il titolo del libro (max 99 car.)");
    gets( elenco[i].titolo ) ;
    printf("\n Inserisci nome e cognome dell'autore del libro (max 50 car.)");
    gets( elenco[i].autore ) ;
    printf("\n Inserisci la collocazione del libro");
    scanf("%d", & ( elenco[i].collocazione ) ); // uso & perché il parametro è un int
  }

return 0;}









La scorsa settimana avevamo visto che:

  1. si possono passare alle funzioni dei puntatori a struttura
  2. dopo la dichiarazione di un array di strutture, il nome dell'array diventa automaticamente una variabile di tipo puntatore a struttura e fa riferimento al primo elemento dell'array (la stessa cosa succedeva per gli array formati da variabili appartenenti ai tipi fondamentali)

Da questo segue che: come succedeva per le variabili appartenenti ai 4 tipi fondamentali, tutti gli elementi di un vettore di strutture possono essere inizializzati / letti utilizzando una funzione



Cerchiamo di svolgere le operazioni viste prima (inizializzazione ed inserimento dei valori) utilizzando delle funzioni.


... // istruzioni

 libro elenco[30];

// ciclo di inizializzazione
 for(i = 0;i < 30 ; i++)
  {
   elenco[i].collocazione = 0 ;
   elenco[i].titolo[0] = '\0' ;
   // o, in modo analogo
   * ( elenco[i].titolo ) = '\0' ;
   // allocazione di memoria
     elenco[i].autore = (char *) malloc ( sizeof(char) * (50 + 1) );
  }
// fine ciclo di inizializzazione

... // istruzioni
















Una funzione adatta a svolgere lo stesso compito è:

/* non prevedo la restituzione di alcun valore in quanto la funzione deve modificare gli elementi di un'area di memoria (l'array) già allocata */

void inizializza(libro*,int);


void inizializza(libro* vettore,int dimensione)
{int i=0;
 for(i=0; i < dimensione ; i++)
   {
    vettore[i].collocazione = 0;
    vettore[i].titolo[0] = '\0';
    vettore[i].autore = (char *) malloc ( sizeof(char) * (50 + 1) );     
   }
}
























La versione equivalente a questa, che fa uso dell'aritmetica dei puntatori, è:


void inizializza(libro*,int);


void inizializza(libro* vettore,int dimensione)
{int i=0;
 for(i=0; i < dimensione ; i++)
   {
    ( vettore + i ) -> collocazione = 0;
    * (( vettore + i ) -> titolo )= '\0';
    ( vettore + i ) -> autore = (char *) malloc ( sizeof(char) * (50 + 1) );     
   }
}

















Allo stesso modo, ci possiamo servire di una funzione per svolgere le istruzioni relative alla parte del codice in cui l'utente doveva inserire i parametri.


... // istruzioni

for( i = 0 ; i < 30 ; i ++ )
  {
    printf(" Vuoi inserire autore e titolo di un libro ? (s/n)");
    scanf("%s",temp);
    if( strncmp(temp,"s",1) == 0 ) break;    // in questo modo anche viene accettato
    printf("\n Inserisci il titolo del libro (max 99 car.)");
    gets( elenco[i].titolo ) ;
    printf("\n Inserisci nome e cognome dell'autore del libro (max 50 car.)");
    gets( elenco[i].autore ) ;
    printf("\n Inserisci la collocazione del libro");
    scanf("%d", & ( elenco[i].collocazione ) ); // uso & perché il parametro è un int
  }


... // istruzioni





















Una funzione che ci permette di svolgere il medesimo compito è:

void assegna(libro*,int);

void assegna(libro* vettore, int dimensione)
{int i=0;
 char temp[4];
 for(i = 0 ; i < dimensione ; i++)
   {
    printf(" Vuoi inserire autore e titolo di un libro ? (s/n)");
    scanf("%s",temp);
    if( strncmp(temp,"s",1) == 0 ) break;    // in questo modo anche viene accettato
    printf("\n Inserisci il titolo del libro (max 99 car.)");
    gets( vettore[i].titolo ) ;
    printf("\n Inserisci nome e cognome dell'autore del libro (max 50 car.)");
    gets( vettore [i].autore ) ;
    printf("\n Inserisci la collocazione del libro");
    scanf("%d", & ( vettore[i].collocazione ) ); // uso & perché il parametro è un int
   }
}

















La versione che fa uso dell'aritmentica dei puntatori è:


void assegna(libro*,int);

void assegna(libro* vettore, int dimensione)
{int i=0;
 char temp[4];
 for(i = 0 ; i < dimensione ; i++)
   {
    printf(" Vuoi inserire autore e titolo di un libro ? (s/n)");
    scanf("%s",temp);
    if( strncmp(temp,"s",1) == 0 ) break;    // in questo modo anche viene accettato
    printf("\n Inserisci il titolo del libro (max 99 car.)");
    gets( ( vettore + i ) -> titolo ) ;
    printf("\n Inserisci nome e cognome dell'autore del libro (max 50 car.)");
    gets( ( vettore + i ) -> autore ) ;
    printf("\n Inserisci la collocazione del libro");
    scanf("%d", & ( ( vettore + i ) -> collocazione ) ); // uso & perché il parametro è un int
   }
}













Un'ultima nota sul passaggio delle strutture alle funzioni. Fino ad ora il passaggio di strutture alle funzioni è sempre stato effettuato mediante puntatore. Facendo questo è palese il fatto che, ricevendo l'indirizzo della locazione di memoria in cui risiede una data struttura, la funzione è in grado di modificare il valore di tutti gli elementi della struttura stessa.


Nel caso in cui il passaggio venga effettuato per valore, ci aspettiamo che questo non accada, ovvero che la funzione non sia in grado di alterare i valori contenuti nella struttura che passiamo alla funzione, in quanto quest'ultima può alterare solo la copia locale della struttura di cui passiamo il valore. Ma questo non sempre è vero.










struct temp{char titolo[100];
                 char *autore;
                 int collocazione;};



typedef struct temp libro;

void modifica(libro);

int main()
{libro volume;
 strcpy(volume.titolo,"Manuale di C");
 
 volume.autore= (char*) malloc ( 100 * sizeof(char));
 strcpy(volume.autore,"Pinco Pallino");

 volume.collocazione = 1234;
 
 modifica(volume);

 return 0;}




void modifica(libro copia)
{
 strcpy(copia.titolo,"Manuale di C, seconda edizione");
 
 strcpy(copia.autore,"Mario Rossi");
}













All'atto del passaggio della struttura alla funzione, alla variabile locale copia viene assegnato il valore della struttura volume; l'operazione viene effettuata membro a membro. Quindi, la variabile locale copia.autore possiede lo stesso valore della variabile volume.autore, ovvero entrambe possiedono l'indirizzo della locazione di memoria in cui si trova la stringa contenente il nome dell'autore del libro. Perciò la funzione è in grado di alterare la variabile volume. Il passaggio per valore della struttura ha "mascherato" il passaggio per riferimento dell'array in essa contenuto.