Le strutture /1 Le strutture rappresentano un tipo di variabile che, diversamente dagli array, consente di gestire in modo compatto, mediante un'unico identificatore, un insieme di valori che non possiedano necessariamente il medesimo tipo. Nell'esercizio che segue (e nelle prossime lezioni sulle strutture) svilupperemo un programma che permetta di implementare le funzionalità necessarie per la gestione del database di una biblioteca. Prima di iniziare lo sviluppo del codice, è necessario stabilire quale sia il prototipo di struttura da utilizzare per rappresentare nel programma la singola unità del database, ovvero un libro. Gli attributi di tale oggetto che utilizzeremo per sviluppare il prototipo di struttura sono: il suo titolo, il nome ed il cognome dell'autore ed il numero di pagine. struct temp {char titolo[200]; char autore[100]; int pagine; } ; typedef struct temp libro; In alternativa, per usare una notazione più compatta, si può scrivere: typedef struct {char titolo[200]; char autore[100]; int pagine; } libro; Le istruzioni appena scritte permettono di creare un nuovo TIPO di variabile, un tipo composto da due array di char e da un intero; a tali istruzioni NON CORRISPONDE ALCUNA ALLOCAZIONE DI MEMORIA. Grazie allo specificatore typedef, a partire da struct temp, per questioni di comodità e per marcare l'analogia con gli altri tipi fondamentali del C, è possibile modificare il nome del nuovo tipo di variabile. Alcune note su typedef Lo specificatore typedef, oltre che a venir utilizzato per definire un nuovo tipo di dato, serve anche ad aumentare la portabilità del codice e non è detto debba essere per forza utilizzato solo su nuovi tipi. Ad esempio /* inizio implementazione */ #define NUM 40 typedef int newtype; void ordina(newtype *vettore) {newtype temp; int i=0,scambi=1; while(scambi) {scambi=0; for(i=0;i<NUM;i++) {if(vettore[i]>vettore[i+1]) {temp=vettore[i]; vettore[i]=vettore[i+1]; vettore[i+1]=temp; scambi=1; } } } } /* fine implementazione */ Sostituendo al tipo int il tipo double, dopo lo specificatore typedef, posso automaticamente applicare l'ordinamento ad un nuovo tipo di variabile. Usando la direttiva #ifdef (ovvero la compilazione condizionale), la potenzialità del codice aumenta ulteriormente. /* inizio implementazione */ #ifdef INTERO typedef int newtype; #endif #ifdef FLOAT typedef float newtype; #endif #ifdef DOUBLE typedef double newtype; #endif #define NUM 40 void ordina(newtype *vettore) {newtype temp; int i=0,scambi=1; while(scambi) {scambi=0; for(i=0;i<NUM;i++) {if(vettore[i]>vettore[i+1]) {temp=vettore[i]; vettore[i]=vettore[i+1]; vettore[i+1]=temp; scambi=1; } } } } /* fine implementazione */ Prima di iniziare a scrivere il codice, alcune precisazioni; si farà sempre riferimento al tipo di struttura definito sopra: typedef struct {char titolo[200]; char autore[100]; int pagine; } libro; 1. Per individuare uno dei caratteri della stringa titolo, ad esempio il quarto /* dichiaro una variabile di tipo libro */ libro my_var; char temp; printf("Inserisci il titolo di un libro"); gets(my_var.titolo); /* titolo è una variabile di tipo char *, contenuta all'interno della struttura. Per scrivere dei caratteri nella corrispondente area di memoria devo perciò individuarla, specificando che essa è uno dei membri della struttura my_var */ temp = my_var.titolo[3]; /* ora temp ha il valore del quarto carattere della stringa */ temp = *(my_var.titolo +3); /* stessa cosa, usando l'aritmetica dei puntatori */ temp = *(my_var.(titolo +3)); /* GRAVE ERRORE ! */ /* la variabile titolo, da sola, non esiste; essa esiste SOLO come membro della struttura di tipo libro */ 2. Le stesse funzionalità possono essere implementate utilizzando un puntatore a struttura /* dichiaro una variabile di tipo libro */ libro my_var; libro* Punt_libro; char temp; printf("Inserisci il titolo di un libro"); gets(Punt_libro->titolo); temp = Punt_libro->titolo[3]; temp = *(Punt_libro->titolo +3); /* stessa cosa, usando l'aritmetica dei puntatori */ temp = *(Punt_libro->(titolo +3)); /* GRAVE ERRORE ! */ /* la variabile titolo, da sola, non esiste; essa esiste SOLO come membro della struttura di tipo libro */ o anche, usando l'allocazione dinamica libro* Punt_libro; Punt_libro = (libro*) malloc(sizeof(libro)); char temp; printf("Inserisci il titolo di un libro"); gets(Punt_libro->titolo); temp = Punt_libro->titolo[3]; temp = *(Punt_libro->titolo +3); /* stessa cosa, usando l'aritmetica dei puntatori */ temp = *(Punt_libro->(titolo +3)); /* GRAVE ERRORE ! */ /* la variabile titolo, da sola, non esiste; essa esiste SOLO come membro della struttura di tipo libro */ IMPORTANTE: per determinare la dimensione in byte di una variabile di tipo struttura utilizzare l'operatore sizeof(). NON è sempre vero che la somma delle dimensioni in byte delle variabili contenute nella struttura equivalga alla dimensione in byte della struttura stessa. 3. Alle variabili di tipo struttura NON possono essere applicati tutti gli operatori. Gli unici operatori utilizzabili sono quello di assegnazione, =, e l'operatore &. E' perciò possibile fare la COPIA di una struttura, membro a membro, e passare una struttura ad una funzione per valore.