Laurea In Ingegneria dell Informazione Esercitazioni Guidate di Tecniche della Programmazione Note introduttive: 1) Le soluzioni agli esercizi e le versioni di programmi menzionate nel testo delle esercitazioni sono raggiungibili tramite la pagina web del corso. Nel titolo di ogni sezione di questo documento è specificato tra parentesi il nome del (o dei) file in cui è proposta una soluzione (se disponibile...). 2) I programmi che scriveremo dovranno essere in accordo con la definizione standard ANSI C del linguaggio C; perciò, prima di cominciare vogliamo assicurarci che l ambiente di programmazione che usiamo usi anche lui la medesima definizione. 3) Così, se si usa il Dev C++, ad esempio nella versione 4.9.9.2 (lingua inglese) bisogna andare nel menu Tools, selezionare Compiler Options, scegliere Settings e poi C Compiler (selezionare almeno Support all ANSI Standard C Programs ). 4) In questo blocco abbiamo due EG: la 6 sulle strutture, e la 7 relativa all esercizio Gestione Voli 6. Esercitazione Guidata 6 6.1. Uso di struct (punto.c, punto2.c). Scrivere un programma in cui - sia definito il tipo struct punto adatto a rappresentare i punti colorati nel piano cartesiano (due dimensioni); - vengano lette coordinate e colori di due punti e venga costruito il punto intermedio, stampandone i dati (il punto intermedia ha il medesimo colore dei due punti letti, se questi hanno il medesimo colore; altrimenti ha colore NERO ). Scrivere poi il medesimo programma, in cui il tipo dei punti colorati sia TipoPunto, definito mediante typedef ed usato nelle dichiarazioni dei punti usati nel programma. 6.2. Uso di struct (distanze.c). Scrivere un programma in cui viene letta una sequenza di punti colorati (definiti come negli esercizi precedenti) e, per ogni punto letto, dal secondo in poi, viene stampata la distanza colorata tra esso e il precedente. Ci sono dei colori prestabiliti come ammissibili: bianco, rosso, giallo, celeste, blu, NERO Quando viene letto il colore di un punto, se questo e al di fuori dei colori prestabiliti, sara considerato NERO. 1
La distanza colorata tra due punti e definita come la coppia <dl, dc>, dove - dl=distanza lineare tra i due punti e - dc=distanza tra i colori dei due punti (stabilita in base alla definizione dei colori prestabiliti data in precedenza: ad es. la distanza tra bianco e rosso e 1, quella tra bianco e giallo e 2, quella tra giallo e nero e 3). Qualche suggerimento per la soluzione segue: non li leggete tutti d un fiato... Suggerimento 0 Quando il programma compila, probabilmente siamo solo a meta dell opera. Provare il programma anche usando le funzionalita di debugging: debugging? What s debugging?? - la linea rossa che si vede in figura e una linea su cui e stato impostato un breakpoint (basta cliccare accanto alla linea per impostare il breakpoint o per rimuoverlo). - Un breakpoint nel punto indicato e utile: cosi possiamo scegliere l opzione di compilazione con debug (F8) e usare F7 (Next Step) per far avanzare l esecuzione del programma passo passo (un istruzione alla volta). - Durante l esecuzione passo passo e utile tener d occhio il contenuto di alcune variabili significative: in figura si vede che e stata impostata la visualizzazione del contenuto delle variabili p1, p2, dc: e stato usato il comando Add Watch. (Nell aggiungere una nuova watch, puo essere necessario eseguire il passo successivo F7 prima di vederla effettivamente.). - La figura mostra un momento dell esecuzione in cui tutte le istruzione di lettura del punto p1 sono state eseguite (la watch su p1 mostra quei valori: 1, 5, rosso). - Se una watch corrisponde a variabili non nello scope dell istruzione in esecuzione, cio viene indicato (ad esempio nel punto del programma in esecuzione non potremmo vedere il contenuto della variabile i definita nella funzione coloreprestabilito(). - Quando e in esecuzione un istruzione di lettura, bisogna andare nella finestra di esecuzione e inserire il dato da leggere 2
- Se non si vuole piu procedere passo passo, Shift F7 (Continue) procura l avanzamento dell esecuzione fino alla prossima occorrenza di un breakpoint. Suggerimento 1di5 la struttura del TipoPunto e quella gia vista; inoltre potrebbe convenire dare una definizione di un tipo apposito per la DistanzaColorata. (una struct con un campo double per la distanza lineare e uno int per la distanza tra i colori. Inoltre e bene che sia data una chiara definizione dei colori prestabiliti. Questa potrebbe anche non essere la definizione di un tipo vero e proprio. Una possibilita e quella di usare un array top level di stringhe inizializzato opportunamente COLORI[]={bianco, rosso, giallo, celeste, blu, nero}. 3
Suggerimento 2di5 All array COLORI potrebbe essere affiancata una funzione coloreprestabilito che, ricevendo un colore, col, dica se col e o no uno di colori ammessi. Nella soluzione proposta questa e una funzione intera che restituisce 1 se col non e ammissibile, oppure l indice di col nell array COLORI. Suggerimento 3di5 La funzione coloreprestabilito (sempre nella soluzione proposta) viene usata anche per calcolare lo seconda parte della distanza colorata (infatti se il colore di p1 e k1 e il colore di p2 e k2, la distanza tra i colori sara k1-k2 o k2-k1...) Suggerimento 4di5 Schema del programma - lettura primo punto (p1) - ciclo del tipo while (continua!=0) {... } con richiesta finale all utente di inserire 1 se vuole continuare o 0 se vuole terminare (scelta che viene letta in continua). Suggerimento 5di5 4
- durante ogni iterazione del ciclo o si legge il punto p2 (si leggono le sue coordinate e colore) o si calcola la distanza colorata tra p1 e p2 (e la si stampa) o l utente indica la sua scelta (se continuare o meno) e di conseguenza viene assegnata la variabile continua. o Infine viene copiato p2 in p1, così in p1 ci sarà l ultimo punto letto da input, e se ne potrà calcolare la distanza da quello successivamente inserito 6.3. Funzioni che ricevono e/o restituiscono struct (punto3.c). Riscrivere il programma del punto 1.1, definendo ed usando le seguenti funzioni: /*funzione che riceve un punto e lo stampa */ void stampapunto(struct Punto p) /* funzione che riceve due punti e ne restituisce il punto medio */ struct Punto puntomedio(struct Punto pr, struct Punto sec) 6.4. Funzioni che ricevono puntatori a struct (punto4.c). Scrivere un programma che soddisfi i seguenti requisiti: - il programma legge i dati relativi a due punti colorati (definiti come in precedenza) - il programma calcola e stampa i dati relativi al punto medio tra i due letti da input; - poi il programma chiede in input un colore e assegna tale colore a tutti e tre i punti, stampandone successivamente i dati in output. Il programma deve far uso delle funzioni definite nell esercizio precedente e della funzione void cambiacolorea(tipopunto *p, char col[]) che, ricevendo un punto (o meglio, il suo indirizzo), e un colore (col), modifica il punto assegnandogli col come colore. Suggerimento: conviene prendere come punto di partenza il programma fatto per l esercizio precedente, aggiungendo la nuova funzione e modificando opportunamente la main(). 5
6.5. Funzioni che ricevono puntatori a struct (punto5.c). Ripetere l esercizio del 2.3, definendo e utilizzando, per la lettura di ciascun punto, una funzione leggipunto(...) Suggerimento 1di2: la funzione deve ricevere un punto e riempirlo con dati letti da input. Suggerimento 2di2: quindi la funzione deve ricevere l indirizzo del punto da riempire. 6.6. Funzioni che restituiscono puntatori a struct (punto6.c). Rifacendosi a quanto fatto in precedenza, scrivere una funzione creapuntomedio(...) che, ricevendo due punti, restituisce una struct appositamente allocata dinamicamente, contenente la rappresentazione del punto medio tra i due parametri. Suggerimento 1di2: Si tratta di rifare l esercizio precedente, ma sostituendo la funzione puntomedio con quella qui richiesta (che restituisce un puntatore a struct Punto e non una struct Punto. Suggerimento 2di2: ecco uno stralcio del programma PUNTO6.C in cui si vede come viene usata la funzione creapuntomedio: int main(){ TipoPunto p1, p2, *pm; /* puntatore per il punto medio */ printf("primo punto:"); leggipunto(&p1); printf("secondo punto:"); leggipunto(&p2); pm = creapmedio(p1, p2); /* la chiamata a pmedio restituisce il puntatore ad una struct che e' stata allocata appositamente, e che rappresenta il punto medio tra p1 e p2 */ printf(" - adesso i tre punti sono:\n - p1 = "); 6
6.7. Quadrilateri (quadri.c). L oggetto di questo esercizio e un programma capace di gestire quadrilateri dati in input. Un quadrilatero viene specificato come l insieme dei suoi quattro vertici, che sono punti colorati sul piano (cioe variabili del tipo TipoPunto definito precedentemente). Supponiamo che i punti dati in input siano tutti distinti e diano luogo ad una figura in cui i lati sono a 90 gradi. Cio assunto, il programma - costruisce un quadrilatero allocando dinamicamente un array di quattro punti e leggendo i dati relativi ai quattro vertici; - verifica se il quadrilatero e un quadrato - verifica se il quadrilatero e isotetico, cioe i suoi lati sono paralleli agli assi cartesiani; - stampa i dati dei quattro vertici e stampa un messaggio in cui sia specificato se il quadrilatero e o no un quadrato ed e o no isotetico. Se quad e l identificatore usato per il quadrilatero nel programma, si assume anche che i punti siano distribuiti come nel disegno qui sotto: quad[0] quad[1] +-------------+ +-------------+ quad[3] quad[2] Usare nel programma le seguenti funzioni: - void stampapunto(tipopunto ) (per stampare i dati di un punto passato per parametro) - void leggipunto( TipoPunto *) (per leggere un punto) - void leggiquadrilatero(tipopunto q[4]) (per leggere l intero quadrilatero, usando leggipunto); - void stampaquadrilatero(tipopunto q[4]) (per stampare i dati del quadrilatero, ad esempio come sequenza dei punti che ne sono vertici); - double lunghezza(tipopunto primo, TipoPunto secondo) (per calcolare la distanza tra due punti) Suggerimento 1di2: 7
per verificare che il quadrilatero quad sia un quadrato basta verificare che siano uguali le misure dei suoi lati (cioe' delle distanze tra vertici consecutivi: quad[0]--quad[1], quad[1]-- quad[2], quad[2]--quad[3], quad[3]--quad[0],). Per calcolare la lunghezza del lato quad[k]quad[h] si usa la funzione lunghezza(...). Suggerimento 2di2: per verificare che i lati sono paralleli agli assi, bisogna verificare che i lati - quad[0]--quad[1] e quad[2]--quad[3] sian paralleli all'asse delle ascisse - quad[1]--quad[2] e quad[3]--quad[0] sian paralleli all'asse delle ordinate. 8
7. Esercitazione Guidata 7 Qui ci occupiamo di scrivere un programma abbastanza lungo, dedicato alla gestione di una tabella di dati. Una tabella e una struttura dati che rappresenta all interno di un programma, un archivio di dati del mondo reale. 7.1. Cos e una tabella (in breve...) Di solito la tabella viene definita per permettere la gestione automatizzata di una collezione di dati strutturati (dati composti da varie informazioni di tipo disomogeneo e quindi normalmente rappresentati mediante struct). Un esempio di tabella e l elenco telefonico (una collezione di elementi; ciascu elemento e l insieme di dati relativi ad una persona, come il nome, l indirizzo, il numero di telefono, titoli professionali etc...). Un altro esempio e l elenco degli iscritti ad una associazione (anche qui ogli elemento in tabella e una collezione di dati relativi ad una persona, come il nome e indirizzo, ma anche l anzianita d iscrizione, la quota associativa pagata etc...). Le funzioni generiche di gestione di una tabella sono - l aggiunta di un elemento nella tabella, - l eliminazione di un certo elemento, - la modifica dei dati memorizzati in un un certo elemento - e la ricerca di un certo elemento tra quelli memorizzati nella tabella. Di solito uno dei dati memorizzati in ciascun elemento lo caratterizza univocamente e permette di distinguere quell elemento da tutti gli altri: ad esempio il codice fiscale permette di specificare una certa persona; la matricola permette di indicare univocamente uno degli associati al club, etc... Questi dati caratteristici vengono denominati chiavi di ricerca, o piu brevemente, chiavi, e vengono usati quando si stanno effettuando operazioni di ricerca e selezione di un certo elemento nella tabella. 7.2. Gestione voli (primo passo VOLI1.C) Il programma deve permettere la gestione di un archivio contenente informazioni sui voli aerei di un certo giorno. Un volo e' caratterizzato da o codice (5 caratteri alfanumerici) o destinazione (stringa di caratteri) o ora di partenza (ore, minuti: due interi) o numero di posti attualmente liberi (un intero) La tabella dei voli di quel giorno e una collezione di voli (informazioni su voli) su cui e possibile eseguire le seguenti operazioni di gestione: - stampare un volo della tabella, caratterizzato da un certo codice - stampare i voli della tabella - aggiungere un volo alla tabella - eliminare un volo avente caratterizzato da un certo codice - modificare l'ora di partenza di un volo caratterizzato da un certo codice 9
- prenotare k posti in un volo caratterizzato da un certo codice (cioe' modificarne il numero di posti liberi, diminuendolo, se possibile, di k) - memorizzare i dati di una tabella di voli in un file di voli - caricare nella tabella dati da un file di voli Si vede che in questa tabella il campo codice e' la chiave di ricerca per i voli memorizzati. Scrivere un abbozzo del programma che dovra permettere tutte le operazioni sopra indicate. In questo abbozzo di programma ci limitiamo a fornire - la definizione della struttura dati che useremo per rappresentare la tabella dei voli; - la dichiarazione e implementazione (programmazione) delle funzioni necessarie a realizzare le prime tre operazioni elencate sopra; - una funzione main in cui venga stampato un menu di scelte (stampa di un volo, stampa dei voli in tabella, aggiunta di un volo in tabella, termine programma) e vengano conseguentemente usate le funzioni definite. Il programma va scritto e testato. Una proposta di soluzione e in VOLI1.C. Suggerimento 1di3: (sulle strutture dati, tratto da VOLI1.C) tabelle di 10 voli, per scrivere e testare il programma. Dopo potremo cambiare questa dimensione #define MAXVOLI 10 il tipo struct per rappresentare un volo contiene un campo orapartenza. L'ora e' ben rappresentabile con un'altra struct struct ora { int ore, minuti; }; segue il volo 10
struct volo { char codice[6]; char * destinazione; struct ora orapartenza; int postiliberi; }; per i voli, decidiamo di definire un sinonimo di tipo typedef struct volo TipoVolo; /* la tabella di voli consiste di un array di MAXVOLI voli, ma anche di un intero quantivoli che in ogni momento specifica quanti sono i voli effettivamente presenti in tabella. Faremo in modo che, se in un dato momento ci sono k voli in tabella, essi siano i primi k elementi dell'array e quantivoli sia uguale a k. Ogni scansione della tabella si limitera' agli elementi di indice da 0 a quantivoli-1. Quindi una tabella di voli e' una struttura a due campi. */ typedef struct { TipoVolo arrayvoli[maxvoli]; /* sostegno di memoria */ int quantivoli; /* quanti voli presenti nel sostegno */ } TipoTabella; Suggerimento 2di3: ipotesi su alcune funzioni che useremo (solo intestazioni) void stampatabella(tipotabella t); /* stampa tutti i voli della tabella */ /*!!! */ /* per entrambe le precedenti funzioni sara' comodo usare una funzione come la seguente... */ void stampavolo(tipovolo v); /* stampa il volo v */ 11
int aggiungivolo(tipotabella *t); /* aggiunge un nuovo volo nella tabella *t, chiedendo e leggendo opportunamente i dati relativi Restituisce 1 o 0 a seconda della riuscita dell'ahggiunta. Passiamo l indirizzo t della tabella da modificare, in modo che le modifiche vengano fatte direttamente sulla tabella *t (e non su una sua copia). Sia (*t).arrayvoli (nell'elemento di indice dipendente da quantivoli), che (*t).quantivoli saranno modificati. L elemento in cui si inserisce il nuovo volo e (*t).arrayvoli[kkk] dove k deve essere uguale al campo quantivoli della tabella (KKK=(*t).quantiVoli). Poi (*t).quantivoli deve crescere di uno, perche abbiamo aggiunto un volo in tabella. */ Suggerimento 3di3: ecco la main() int main() { TipoTabella tabvoli; int riuscita, scelta; /* scelta nel menu' */ char buffer[40]; /* per leggere stringhe */ tabvoli.quantivoli=0; do { /* STAMPA MENU */ /* inizializzazione del numero di voli presenti in tabella (cosa ci sia effettivamente in tabvoli.arrayvoli e' di poco interesse. Tanto le scansioni che faremo saranno sempre limitate da quantivoli*/ printf(" - scegli -\n"); printf(" - stampa dei voli (1) -\n"); printf(" - stampa di un certo volo (2) -\n"); printf(" - aggiunta di un volo (3) -\n"); printf(" - fine (0) -\n"); scanf("%d", &scelta); /* SCELTA ESPRESSA DALL UTENTE */ 12
switch (scelta) { /* ELABORAZIONE IN BASE ALLA SCELTA */ case 1: printf("- %d voli in tabella:\n", tabvoli.quantivoli); stampatabella(tabvoli); break; case 2: /* funzione non ancora implementata; (rimanadata al prossimo esercizio) printf(" - codice volo? "); scanf("%s", buffer); stampaquelvolo(tabvoli, buffer); */ break; case 3: riuscita=aggiungivolo(&tabvoli); if(!riuscita) printf(" - aggiunta non effettuata -\n"); else printf(" - fatto -\n"); break; case 0: printf(" - USCITA DAL PROGRAMMA\n"); break; default: printf(" - opzione sballata\n"); } /* fine switch */ } while (scelta!=0); /* fine do_while*/ } printf("\nfine\n"); return 0; 7.3. Gestione voli (secondo passo VOLI2.C) Incrementare il programma costruito al passo precedente, con la funzionalita di stampa di un volo, dato il codice. (E poi testarlo...) Suggerimento 1di2: La funzione da implementare e la seguente: void stampaquelvolo(tipotabella t, char cod[]); 13
Suggerimento 2di2: Pero per stampare il volo di codice cod, bisogna cercarlo nella tabella. Questo problema di cercare un volo di codice dato ricorre, verosimilmente spesso nel resto del programma. Ecco come risolvere il problema una volta per tutte, con una funzione: int indicevolo(tipotabella t, char cod[]); /* cerca il volo di codice cod in t; cioe cerca nell array t.arrayvoli, limitando la scansione agli elementi che vanno da indice 0 a indice t.quantivoli-1 restituisce l'indice del volo oppure, se non lo ha trovato, -1 */ 7.4. Gestione voli (terzo passo VOLI3.C) Incrementare il programma costruito al passo precedente, con la funzionalita di eliminazione di un volo di codice dato. (E poi testarlo...). Suggerimento 1di2: Bisogna realizzare la funzione di eliminazione ed estendere la main() in modo che permetta di scegliere anche l opzione di eliminazione. Quando viene scelta questa opzione, il programma chiede il codice del volo da eliminare e poi chiama la funzione di eliminazione. La funzione da implementare e la seguente: int eliminavolo(tipotabella *t, char cod[]); /* elimina il volo di codice cod dalla tabella *t (cambiano arrayvoli e quantivoli) restituisce 0 o 1 a seconda del successo dell'operazione Si cerca l'elemento da eliminare, assegnando ad un indice il valore ritornato dalla funzione di ricerca, ad esempio k=indicevolo(*t, cod); e lo si ricopre copiandoci sopra l'ultimo elemento della tabella; ad esempio t->arrayvoli[k] = t->arrayvoli[ultimo]; (quanto vale l indice ultimo?) 14
poi si decrementa quantivoli ad esempio t->quantivoli-=1; */ Suggerimento 2di2: ultimo=t->quantivoli-1; 7.5. Gestione voli (quarto passo VOLI4.C) Incrementare il programma costruito al passo precedente, con le funzionalita di modifica richieste, cioe cambio dell ora di partenza di un volo di codice dato e prenotazione di un ecrto numero k di posti su un volo di codice dato. (E poi testarlo...). Bisogna realizzare due funzioni apposite, cambiaorapartenza(...) e cambiapostiliberi(...); e poi modificare la main() in modo che anche queste nuove operazioni siano accessibili dal menu. Suggerimento: le funzioni da realizzare potrebbero essere le seguenti: int cambiaorapartenza (TipoTabella *t, char cod[], int nuovaora, int nuovimin); /* restituisce 0 o 1 a seconda del successo dell'operazione */ int cambiapostiliberi(tipotabella *t, char cod[], int k); /* diminuisce di k i posti liberi, SE POSSIBILE restituisce 0 o 1 a seconda del successo dell'operazione */ Il parametro tabella qui deve essere l indirizzo della tabella attuale da modificare... 15
7.6. Gestione voli (quinto passo VOLI5.C) Incrementare il programma costruito al passo precedente, con le funzionalita di salvataggio su file della tabella e caricamento da file della tabella. (E poi testarlo...). Bisogna realizzare due funzioni apposite, void databellainfile(tipotabella t, char *nmf); /* riceve - una tabella da scaricare in memoria secondaria e - il niome del file in cui memorizzare le informazioni; e scarica i dati della tabella nel file di nome nmf*/ void dafileintabella(char *nmf, TipoTabella *t); /* riceve - l indirizzo di una tabella in cui memorizzare i dati contenuti in un file e - il nome del file da cui trarre i dati per riempire la tabella; e carica nella tabella puntata da t (cioe *t) i voli contenuti nel file di nome nmf */ e poi modificare la main() in modo che anche queste nuove operazioni siano accessibili dal menu. 7.7. Gestione voli (sesto passo VOLI6.C) Qui si richiede di risolvere il problema della gestione dei voli con un approccio piu dinamico, cioe facendo in modo che la memoria occupata dall array di voli arrayvoli (campo della tabella TipoVoli) non occupi un numero fissato MAXVOLI di locazioni di tipo TipoVolo (struct) ma modifichi l occupazione di memoria in ragione delle necessita (cioe del numero di voli effettivamente memorizzati nella tabella). Per realizzare questo scopo si suggerisce di adottare il seguente schema: - il tipo TipoTabella viene modificato, in modo che il campo arrayvolo non isa piu un array statico di MAXVOLI voli, ma il puntatore ad un array di voli dinamicamente allocato: TipoVolo * arrayvoli; - l inizializzazione della tabella non si limita piu all azzeramento del campo.quantivoli, ma deve anche procedere ad una prima allocazione del blocco di voli.arrayvoli. Questa allocazione fa in modo che arrayvoli punti ad una blocco di MAXVOLI voli (dove MAXVOLI e un simbolo di costante definito opportunamente, per il quale si consiglia un valore abbastanza basso, es 5 o 6); - la funzione di caricamento dei dati da file a tabella viene modificata, in modo che venga 16
o deallocato il blocco eventualmente puntato da arrayvoli e o allocato (e assegnato ad arrayvoli) un blocco di voli che verra riempito con i dati contenuti nel file; in particolare il primo dato letto dal file dice quanti sono i voli presenti nel file; il blocco viene allocato con un numero di voli di circa il 30% superiore a quelli effettivamente necessari, cosi dopo il caricamento dei dati, ci sara spazio per aggiungere nella tabella nuovi voli, ma senza esagerare con lo spazio occupato; - La funzione di aggiunta di un nuovo volo in tabella viene cambiata in modo che quando l aggiunta non e possibile per mancanza di spazio in arrayvoli, anziche lasciar perdere o si fa in modo che arrayvoli punti ad un blocoo di voli esteso (un po piu grande, ad esempio del 30%) o e poi si aggiunge il volo come si faceva prima Per fare in modo che arrayvoli punti ad un blocco di voli che contiene i voli gia presenti in tabella piu qualche altro spazio per nuovi voli, si possono usare varie soluzioni. La piu immediata consiste nel o salvare la tabella in un file TEMP.TXT o ricaricare subito la tabella, con la funzione di caricamento modificata, discussa in un punto precedente. Si consiglia di procedere facendo una copia del file in cui era stato risolto completamente il punto presedente (la cui proposta di soluzione era in VOLI5.C) e modificare questa copia, seguendo l ordine delle suggerimenti elencati sopra; Durante questa opera ci si potrebbe rendere conto che, aproposito di modifiche alla struttura dati di Tabella dei Voli, non tutto e stato detto fin qui: potrebbe essere necessario aggiungere un altra modifica. La scoperta di questa modifica è bella se viene fatta autonomamente... ma comunque se ne parla nel prossimo suggerimento. Suggerimento: visto che la dimensione del blocco di voli puntato da arrayvoli non e piu fissa (sempre uguale a MAXVOLI) durante l esecuzione del programma, diventa necessario averla memorizzata in una variabile. Questa variabile deve contenere, in qualsiasi momento, il numero di voli effettivamente allocati nel blocco arrayvoli, indipendentemente se occupati o disponibili. Quindi deve essere un valore associato stabilmente alla singola tabella (tab) con cui si sta lavorando. Ad esempio, durante l operazione di aggiunta di un nuovo volo si deve verificare che il numero di voli effettivamente memorizzato (.quantivoli) non sia diventato uguale al numero di strutture allocate nel blocco (altrimenti l aggiunta non si puo fare subito). Ne concludiamo che la variabile contenente il numero di voli allocati nel blocco arrayvoli deve essere un campo della tabella. E sara il terzo campo, che in VOLI6.C abbiamo chiamato.voliallocati. 17