Riprendiamo l'esercizio di ieri e cerchiamo di ordinare gli elementi dell'array di strutture.
#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
sì 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
}
// fine ciclo di assegnazione
/*
inizio ciclo di visualizzazione; devo stampare SOLO gli elementi dell'array in cui l'utente ha inserito titolo, autore e collocazione.
Distinguo tali elementi dagli altri sia perché la loro collocazione è diversa da zero sia perché la stringa titolo inizia con un carattere diverso da '\0'
*/
for(i = 0 ; i < 30 ; i ++ )
{
if(
elenco[i].
collocazione == 0) break;
printf("Il titolo del libro e\' : %s \n",
elenco[i].
titolo ) ;
printf("L\'autore del libro e\' : %s \n",
elenco[i].
autore ) ;
printf("La collocazione del libro e\' : %d \n",
elenco[i].
collocazione ) ;
}
return 0;}
Nota: se utilizziamo la notazione a puntatore, gli elementi dell'array
titolo contenuto all'interno della struttura si individuano, come per tutti gli array, incrementando il puntatore al primo elemento dell'array. Se vogliamo raggiungere l'n-esimo elemento:
* (
elenco[i].
titolo + n )
e NON
elenco[i]. * (
titolo + n )
in quanto la variabile
titolo esiste SOLO come membro della struttura
elenco[i]
A questo punto siamo in grado di creare un array di strutture e di inizializzarle. Possiamo ordinarne gli elementi ? (ad esempio per collocazione)
I due ingredienti fondamentali di un algoritmo di ordinamento sono:
(1) criterio di ordinamento
(2) capacità di scambiare gli elementi
(1) criterio di ordinamento
elenco[ i ].
collocazione <
elenco[ i + 1 ].
collocazione
(2) scambio degli elementi ( -> scambio dei valori degli elementi contenuti nella struttura)
Noi sappiamo scambiare tra loro variabili dei tipi primitivi, quindi possiamo implementare questa procedura.
supponiamo di voler effettuare uno scambio tra i valori degli elementi della struttura i-esima e di quelli della struttura (i+1)-esima usando la metodologia adottata fino ad ora.
Essendoci 3 elementi ho bisogno di tre variabili temporanee.
int temp_i;
char temp_s[100];
char *temp_p;
temp_i =
elenco[ i ].
collocazione ;
elenco[ i ].
collocazione =
elenco[ i + 1 ].
collocazione ;
elenco[ i + 1 ].
collocazione = temp_i ;
strcpy(temp_s ,
elenco[ i ].
titolo ) ;
strcpy(
elenco[ i ].
titolo ,
elenco[ i + 1 ].
titolo ) ;
strcpy(
elenco[ i + 1 ].
titolo , temp_s
) ;
temp_p =
elenco[ i ].
autore ;
elenco[ i ].
autore =
elenco[ i + 1 ].
autore ;
elenco[ i + 1 ].
autore = temp_p ;
Domanda: è possibile applicare il metodo utilizzato per l'elemento autore all'elemento titolo ?
Qual è la differenza tra i tre scambi ? Nei primi due vengono modificati i valori delle aree di memoria cui si fa riferimento (ovvero un intero ed un array di caratteri); nel terzo caso le aree di memoria non vengono alterate, si fa semplicemente uno scambio di indirizzi.
Prima dello scambio:
Dopo lo scambio

La stessa cosa potevamo farla usando l'operatore di assegnazione (che esegue una copia membro a membro)
libro temp;
temp = elenco[ i ] ;
elenco[ i ] = elenco[ i + 1 ];
elenco[ i ] = temp;
Scriviamo ora il codice completo di algoritmo di ordinamento; nel farlo supporremo che il numero di strutture inizializzate dall'utente sia num_init (dove num_init <= 30)
Scriviamo inizialmente il ciclo di controllo:
libro temp;
... // restante parte del codice
for( i = 0 ; i < num_init - 1 ; i++)
{
if(elenco[i].collocazione < elenco[i+1].collocazione)
{temp = elenco[i];
elenco[i] = elenco[i+1];
elenco[i+1]=temp;}
}
NOTA: prima di effetture questo tipo di scambio la variabile temp.autore non è stata inizializzata. Perché ?
Il ciclo di ordinamento completo risulta:
int scambio = 1;
libro temp;
... // istruzioni
while(scambio)
{ scambio = 0;
for( i = 0 ; i < num_init - 1 ; i++)
{
if(elenco[i].collocazione < elenco[i+1].collocazione)
{temp = elenco[i];
elenco[i] = elenco[i+1];
elenco[i+1]=temp;
scambio = 1; }
}
}
Lo scambio è stato effettuato utilizzando, di volta in volta, l'operatore di assegnazione e le variabili di tipo libro. Proviamo ora ad effettuare la medesima cosa usando i puntatori a struttura.
int scambio = 1;
libro temp;
libro* punt1;
libro* punt2;
... // istruzioni
while(scambio)
{ scambio = 0;
for( i = 0 ; i < num_init - 1 ; i++)
{
punt1 = & elenco[i] ;
punt2 = & elenco[i] ;
if( punt1 -> collocazione < punt2 -> collocazione)
{temp = *punt1;
*punt1 = *punt2;
*punt2 = temp;
scambio = 1; }
}
}
Ora, quindi, siamo in grado di effettuare lo scambio, in particolare, servendoci dei puntatori a struttura. Noi sappiamo come passare un puntatore ad una funzione, quindi possiamo delegare lo scambio ad una funzione. (risulta chiara, a questo punto, la necessità di dichiarare il prototipo di struttura all'esterno del corpo di main())
void scambia_libri (libro* primo, libro* secondo)
{libro temp;
temp = *primo;
*primo = *secondo;
*secondo = temp;
}
Il codice quindi diventa :
int scambio = 1;
libro temp;
libro* punt1;
libro* punt2;
... // istruzioni
while(scambio)
{ scambio = 0;
for( i = 0 ; i < num_init - 1 ; i++)
{
punt1 = & elenco[i] ;
punt2 = & elenco[i] ;
if( punt1 -> collocazione < punt2 -> collocazione)
{scambia_libri( punt1 , punt2 );
scambio = 1; }
}
}
Ora manca l'ultimo passaggio:
all'atto della dichiarazione di un vettore con allocazione statica della memoria il nome del vettore diventa automaticamente il puntatore al primo elemento del vettore.
int vettore[40];
vettore risulta
essere un puntatore ad intero avente come valore: & (vettore [0] )
Applichiamo questa regola per snellire ulteriormente il codice:
libro elenco[30]; // allocazione statica di memoria
elenco è automaticamente una variabile di tipo libro * che contiene l'indirizzo del primo elemento del vettore di strutture, ovvero l'indirizzo di elenco[0]. Conoscendo tale indirizzo e sapendo utilizzare l'aritmetica dei puntatori possiamo muoverci all'interno dell'array di strutture.
elenco[i].collocazione diventa (elenco + i) -> collocazione
Ovvero, invece di accedere all'elemento della struttura mediante la variabile di tipo struttura, accedo all'elemento mediante il puntatore a quella struttura.
In questo modo il codice, a parte l'uso dell'operatore di accesso alla struttura, ricorda molto il codice scritto a suo tempo per ordinare un array di char, int, ...
int scambio = 1;
libro temp;
... // istruzioni
while(scambio)
{ scambio = 0;
for( i = 0 ; i < num_init - 1 ; i++)
{
if( (elenco + i) ->collocazione < (elenco + i + 1) -> collocazione)
{scambia_libri( elenco + i , elenco + i + 1 );
scambio = 1; }
}
}
Un quesito per coloro a cui piacciono i puntatori (ma non solo). Supponiamo per assurdo che l'operatore di assegnazione non sia applicabile alle strutture: riuscireste ad implementare il compito svolto da tale operatore (ovvero copiare i valori dei membri di una struttura nei corrispondenti membri di una struttura ad essa identica) usando solo i puntatori alle due strutture e l'aritmetica dei puntatori (ed ovviamente l'operatore di assegnazione, a patto di non applicarlo alle strutture) ?