540 likes | 723 Views
Strutture dati elementari. Parte 6 Vettori e ricerca binaria Matrici e triangolo di Tartaglia Records (cenni). Corso A: Prof. Stefano Berardi http://www.di.unito.it/ ~stefano Corso B: Prof. Ugo de’ Liguoro http://www.di.unito.it/ ~deligu. Albert Durer. Melencolia,1514 (dettaglio).
E N D
Strutture dati elementari • Parte 6 • Vettori e ricerca binaria • Matrici e triangolo di Tartaglia • Records (cenni) Corso A: Prof. Stefano Berardihttp://www.di.unito.it/~stefano Corso B: Prof. Ugo de’ Liguorohttp://www.di.unito.it/~deligu
Albert Durer. Melencolia,1514 (dettaglio) “Un quadrato magico di numeri”
Indice Parte 6: i Vettori Strutture dati: i vettori. Esempi elementari: stampa, somma, test di uguaglianza, inversione per vettori. Esempi più complessi: ricerca lineare e binaria per vettori. Matrici: il triangolo di Tartaglia. Records e vettori parzialmente riempiti (cenni).
1. Strutture dati: I Vettori • In generale, le strutture dati sono un modo per rappresentare insiemi di informazioni nella memoria di un computer • Una semplice variabile è un caso banale di una struttura dati, un modo per rappresentate una singola informazione. • In questa parte del corso studieremo strutture dati per rappresentare insiemi di dati di tipo omogeneo, i vettori e le matrici, e per rappresentare dati di tipo eterogeneo, i record.
Vettori (o “array”) in C++ • Un vettore v (array) è una sequenza di n oggetti tutti del medesimo tipo, detti elementi del vettore, per qualche n>0 detto la dimensione del vettore. • Gli elementi del vettore sono indicizzati utilizzando interi positivi, da 0 fino a n-1, immaginati disposti come segue: v[0], v[1], … , v[n-1]
Notazioni per i vettori • Le notazioni che seguono NON sono codice C/C++, ma sono la notazione che usiamo quando parliamo dei vettori: i..j = {kZ| i k j} intervallo di interi v[i..j] = {v[i], v[i+1], … , v[j]} • Quindi, se i > j allora i..j = lista vuota; se un vettore v ha n elementi questi formano l’insieme v[0..n-1].
v[i] indirizzo b+i*d I vettori nella memoria RAM • Glielementidi un vettoreoccupanospazidimemoriaconsecutivinellamemoria RAM dellamacchinadi Von Neumann. L’indirizzo &v[i] diognielementosicalcoladunque con la formula: &v[i] = &v[0] + i×d,doved = sizeof (tipodi v[]) d = dimensione di ogni elemento (es.: 4 bytes) &v[i] = indirizzo v[i] = b+i×d v v[0] ind. b v[1] ind. b+d v[2] ind. b+2d b = indirizzo base = &v[0] =indirizzo v[0] (es.:byte n. 1 milione) i = indice di v[i] (detto anche “spiazzamento” ). Es.: i=100, indirizzo di v[i] = 1 000 400.
Nome del vettore: v vale &v[0] 100 = numero degli elementi, o dimensione di v Invece: sizeof(v) = 100*sizeof(int) = 400 Vettori: dichiarazione Tipo degli elementi • Un vettore v in C++ è una costante di tipo indirizzo. Viene identificato con &v[0], l’indirizzo del suo primo elemento. La dichiarazione di v consiste nell’indicazione del nome e del tipo degli elementi del vettore, e nell’indicazione del loro numero, detto la dimensione del vettore. Con sizeof(v) si indica invece il numero di bytes occupati dal vettore: intv[100];
Vettori: l’errore più comune • La dimensione di un vettore deve essere una costante o una variabile già assegnata. • Quindi le seguenti righe sono errate (ma purtroppo il compilatore non lo segnala!): int dim; double w[dim]; /* ERRORE: la variabile dim, per il solo fattodiesseredichiarata, ha un valore, ma non sipuòprevederequale. Quindisicrea un vettoredidimensione “casuale”, a volte dimiliardidielementi, producendo in quest’ultimocaso un “crash”diprogramma */
Accesso agli elementi di un vettore • Per accedere ai singoli elementi di un vettore si usano gli indici: attenti a non confondere gli indici con la dimensione che compare nella dichiarazione. Gli interi tra [ ] hanno diverso significato in una dichiarazionee in una assegnazione: nella dichiarazione,100 è la dimensione, nell’assegnazione, 0 è un indice int v[100]; /* dichiarazione */ v[0] = 6; /* assegnazione, accesso in scrittura */ int n = v[0]; /* assegnazione, accesso in lettura */
idichiaratoentroilforesisteràsoltantoentroilfor Nell’esecuzione del for, i assumerà tutti i valori tra 0 e 99 Inizializzazione attraverso un ciclo • Il programma che segue inizializza a 0 tutti gli elementi del vettore v: int v[100]; for (int i = 0; i < 100; i++) v[i] = 0; Prima dell’inizializzazione, tutti gli elementi di v, avendo un indirizzo di memoria, hanno comunque un valore, ma un valore “casuale”: provate a stamparli.
Inizializzazione attraverso una dichiazione Guardate l’esempio 6.3 del testo di Hubbard: spiega che la dimensione di a si calcola con la formula sizeof(a)/sizeof(double) • Il C++ consente di definire (quindi anche di inizializzare) un vettore elencandone gli elementi. Non è necessario indicare il numero degli elementi, che viene calcolato: double a[] = {22.2, 44.4, 66.6}; // alloca un vettore a di 3 float con // a[0] = 22.2, a[1] = 44.4, a[2] = 66.6
Questo non accade in PASCAL: vedi Hubbard paragrafo 6.4 Attenti a non uscire dai limiti di un vettore • Se un vettore ha dimensione d edi<0, oppurei d, allorav[i] non dovrebbeesseredefinito. • Invece, in C/C++, v[i] esistesempre, è per definizioneilcontenutodell’indirizzodimemoriacalcolatodalla formula: &v[0] + i×sizeof (tipodi v[]) • ossia un valore a caso!! Questa convenzione è introdotta per semplicitàdicalcolo. Il compilatore non ciavvisaquandoutilizziamo un v[i] con i<0, oppurei d, sta a noievitarecheaccada.
2. Alcuni semplici esempi: stampa di un vettore Se si scrive invece: cout << v; dato che v è identificato con l’indirizzo di v[0] si stampa l’indirizzo di v[0] (in esadecimale) • La stampa di un vettore deve avvenire “elemento per elemento” (dunque usando ad es. un ciclo for): int v[100]; for (int i = 0; i < 100; i++) cout << v[i];
Somma e media di un array di numeri Per tutte le sommatoriedi cui non siconosca a priori nemmeno un termine, ilvaloreinizialedell’accumulatore è 0, l’elementoneutrodellasomma Anche la sommadi un vettoredeveavvenire“elemento per elemento” (dunqueusando ad es. un ciclo for): double v[100]; doublesomma = 0; for (inti = 0; i < 100; i++) somma = somma + v[i]; double media = somma/100.0; /*dividiamo per un numeroreale per evitarel’arrotondamento*/
Un test di uguaglianza errato • Se applichiamo il test == a due vettori distinti otteniamo risposta costantemente uguale a false: int v[100], w[100]; if (v == w) … /* v, w sonoidentificati con gliindirizzideiloroprimielementi, dunqueil test == confrontaquestiultimi. Datoche v, w sono “allocati” in posizioni diverse dellamemoria, iloroprimielementihannodiversiindirizzi, anchequando v, w hannoelementidivaloreuguale. Dunque v==w vale sempre false. */
Un test di uguaglianza corretto • Per decidere se due array di egual tipo e egual dimensione N hanno valori uguali per indici uguali, si devono confrontare tutti gli elementi usando un’iterazione. Ecco una soluzione con un WHILE: /* Il ciclo while trasportailcontatorei al primo indice per cui v[i]!=w[i], se ne esisteuno, altrimentitrasportaifino ad N */ inti = 0; while (i < N && v[i] == w[i]) i++; if (i == N)cout << "v,whannoelementiuguali per indiciuguali"; else/* i < N */ cout << "v, w hannodiversol’elementodiposto" << i;
i è dichiarata fuori del for perché l’if che segue ne possa fare uso Un test di uguaglianza corretto 2 • Per decidere se due array diegualtipo e egualdimensione N hannovaloriuguali per indiciuguali, sidevonoconfrontaretuttiglielementiusandoun’iterazione. Ecco la traduzionedellasoluzioneprecedente in un ciclo FOR: inti; for (i=0; i < N && v[i] == w[i]; i++){ }; //ilcorpo del FOR e’ vuoto if (i == N)cout << "v,whannouguali per indiciuguali"; elsecout << "v, w hannodiversol’elementodiposto" << i;
Passaggio di un vettore ad una funzione • Un array vienepassatoallefunzionisempre per indirizzo.Attenzione: nelladichiarazione del parametroscriviamoint a[],nellachiamata non ripetiamoiltipo e scriviamo solo a: int sum( int a[],int n) /* prec.:0<=n<=dim.apost.cond:sum(a,n) = sommadi a[0..n-1]*/ {inti,s; for (i=0,s=0; i<n; i++) s=s+a[i]; return s;} int main(){ int a[] = { 11, 33, 55, 77 }; int size = sizeof(a)/sizeof(int); //num.el. di a cout <<"sum(a,size) ="<<sum(a,size)<<endl;}
Modifica di un vettore • Poiché un vettore viene passato per riferimento (indirizzo) se una chiamata di funzione modifica gli elementi del vettore, queste modifiche sono permanenti: voidscambia (int v[], inti, int j) // pre: 0 i, j < dimensionedi v // post: scambia v[i] con v[j] { int temp = v[i]; v[i] = v[j]; v[j] = temp; }
v[n-2] v[n-1] v[0] v[1] … Inversione di un vettore v Attenzione: se un vettore v è un parametroattualediunafunzione (per es. scambia) NONsiscriveint v[]ma v • Idea. Poniamo due indici i, j agli estremi di v e muoviamo i, j uno verso l’altro. Scambiamo tra loro gli elementi di posto i e j, fino a che tutti gli elementi alla sinistra di v sono scambiati con tutti gli elementi alla destra di v. voidinverti (int v[], int n) /* PRE: 0 n dimensionedi v. POST: elementi v[0..n-1] in ordineinverso */ {for (inti = 0, int j=n-1; i<j; i++,j--) scambia(v, i, j);} i=0 i=1 j=n-2 j=n-1
3. Ricerca Lineare e Binaria • Si vuole decidere se un intero n appartiene alla sequenza rappresentata dal vettore v. Il metodo più semplice è la ricerca lineare: confrontiamo n con v[0], v[1], v[2], … in quest’ordine. bool Member (int n, int v[], int dim) /* pre: la dimensionedi v e’ >= dim >=0. post: restituiamo true esistei tale che v[i]==n */ {booltrovato = false; /* “trovato” indica se n e’ gia’ statotrovato. “trovato” puo’ cambiare solo da false a true, mai viceversa.*/ for(inti=0;i<dim;i++){if(v[i]==n)trovato=true;} returntrovato;}
Ricerca lineare con interruzione: uso di una variabile “flag” • Per efficienza, potremmo voler interrompere la ricerca di n non appena troviamo n. A tal fine, definiamo un valore booleano “trovato” che parte da “vero”, e non appena “trovato” vale vero usciamo. Il test del ciclo quindi deve essere: continuiamo se “!trovato” è vero. Una variabile booleana che ci avvisa quando uscire da un ciclo è detta una “flag”. bool Member (int n, int v[], int dim) /* pre: la dim. di v e’ >= dim >=0. post: true sseesisteit.c. v[i] == n*/ {booltrovato = false;//dettavariabile “flag” for (inti=0; i<dim && !trovato == true; i++) if (v[i] == n) trovato = true; returntrovato;}
Ricerca lineare con interruzione: seconda soluzione Se i < dim è vero, il for è terminato perchè v[i] == n , altrimenti è terminato perchè il vettore è finito senza trovare n • Possiamo eliminare la “flag” trovato, spostando (la negazione di) v[i] == n nel test del for. In tal caso, dobbiamo dichiarare i fuori dal for: bool Member (int n, int v[], int dim) // pre: la dimensionedi v e’ >= dim >= 0 // post: true sseesisteit.c. v[i] == n { inti; for (i=0; i < dim && v[i] != n; i++){}; return (i < dim);}
Ricerca binaria Se un vettore (per es. di interi) è ordinato in senso crescente, la ricerca di un elemento nel vettore può essere enormemente accelerata sfruttando il metodo della Ricerca Binaria: • Manteniamo due indici, i e j, a delimitazione della porzione v[i…j] di v in cui cercare • Ad ogni passo confrontiamo n con il valore di indice medio in i..j, cioè m=(i+j)/2 • Se v[m] != n allora cerchiamo in v[i..m-1] o in v[m+1..i] a seconda che n < v[m] oppure v[m] < n. Daremo ora una descrizione dettagliata del funzionamento della ricerca binaria, in 5 tappe.
95 53 95 91 88 18 60 74 21 25 36 43 49 51 60 21 72 13 51 83 53 91 83 74 72 13 88 21 21 25 36 43 49 18 3 3 5 6 5 6 Ricerca binaria 1: pre- e post-condizioni 7 1. Definiamoilproblema. Input: il “valorecercato” in v è n.Pre-condizione:ilvettore v didimensione dim è ordinato. • Post-condizione:restituirel’indice del valorecercato (un i tale che v[i]=n, se esiste), altrimenti la lunghezza dim di v (dimsta per “non trovato”) 0 dim-1=19 0 dim-1=19 Valorecercato: n = 25. Indice del valorecercato:7 (v[7]=25)
43 72 60 91 88 83 13 18 95 21 25 36 74 49 21 51 53 5 3 6 Ricerca binaria 2: una proprietà invariante • 2. Individuiamo una proprietà “invariante” della ricerca binaria, cioè una proprietà significativa che resta vera durante tutta la durata della ricerca binaria: • Propr. Invariante: durante la ricerca binaria considero solo dei segmenti v[i…j] di v tali che: se n è in v, allora n è in v[i…j]. v[i…j] indice 0 indice 19 i=3 Cerco n=25 in v[i…j] j=16 Per es.: sen=25 è in v, allora n èin v[i..j] = v[3..16].
21 51 91 88 74 72 60 53 83 18 21 95 25 36 43 49 13 6 3 5 Ricerca binaria 3: il funzionamento • 3. La ricerca binaria cerca un modo per avvicinarsi alla soluzione mantenendo vero l’invariante • Passo generico: dividiamo il sottovettore in due parti (quasi) uguali. Caso 1.Se n si trova nel punto intermedio: restituisco m Spazio dove avviene la ricerca di n 0 dim-1= 19 Se ilvalorecercato è n = 43alloral’hotrovato Punto intermedio m di i…j
36 72 60 95 88 83 13 18 91 21 25 74 43 49 51 53 21 3 5 6 Ricerca binaria 3: il funzionamento 3. La ricerca binaria cerca un modo per avvicinarsi alla soluzione mantenendo vero l’invariante • Passo generico: dividiamo il sottovettore in due parti (quasi) uguali. Caso 2.Se il valore n cercato è < di quello nel punto intermedio, allora, dato che il vettore è ordinato, n si trova nella parte sinistra di v[i…j]. Spazio di ricerca 0 19 Punto intermedio m di i..j Valorecercato: n = 25
36 72 60 95 88 83 13 18 91 21 25 74 43 49 51 53 21 3 5 6 Ricerca binaria 3: il funzionamento 3. La ricerca binaria cerca un modo per avvicinarsi alla soluzione mantenendo vero l’invariante • Passo generico: dividiamo il sottovettore in due parti (quasi) uguali. Caso 3.Se il valore cercato n è > di quello nel punto intermedio, allora n si trova nella parte destra di v[i…j] . Spazio di ricerca 0 19 Punto intermedio m di i..j Valorecercato: n = 60
21 51 91 88 74 72 60 53 83 18 21 95 25 36 43 49 13 6 3 5 Ricerca binaria 4: la fine della computazione 4. Definiamo in quale momento la computazione si deve fermare • Quando si sia trovato il valore nel punto intermedio di indice m, oppure …. Spazio di ricerca 0 19 Valorecercato (e trovato): n = 43 Punto intermedio di indice m
21 21 13 18 91 25 36 43 95 51 53 60 72 74 83 88 49 3 6 5 Ricerca binaria 4: la fine della computazione 4. Definiamo in quale momento la computazione si deve fermare • …. oppure quando il sottovettore cui limitiamo la ricerca sia ridotto al vettore vuoto (cioe’ al vettore v[i…j] con i>j) 0 19 n=23 dovrebbeessere qui in mezzo, tra 21 e 25: ma questointervallo è vuoto. Il valore n non vienetrovato. Valorecercato (e nontrovato): n=23
25 72 60 91 88 74 13 18 83 21 95 36 43 49 51 53 21 3 6 5 Ricerca binaria 5:l’inizio della computazione 5. Definiamo le condizioni iniziali per la ricerca binaria • All’inizio, il segmento v[i…j] di vettore in cui cercare n e’ l’intero vettore n. Spazio di ricerca iniziale: v[0..n-1] 0 19
21 95 91 83 74 72 60 51 88 13 18 21 53 25 49 36 43 3 5 6 Ricerca binaria: i dettagli della codifica dei dati Stabiliamo ora i dettagli della codifica del segmento di vettore V[i..j] che usiamo durante la ricerca. • Il sottovettoreV[i..j], a cui limitiamo la ricerca, è compresotra le posizionii e jincluse Spazio di ricerca in un passo generico della computazione m 0 i j 19 • Il puntomedio m ha indice: (i + j) diviso 2 • Se i > jallorailsottovettoreV[i..j] è vuoto
Ricerca binaria: l’implementazione • Scriviamo ora una funzione C++ che implementa la ricerca binaria, usando pre- e post-condizioni e l’invariante come commenti. La funzione ottenuta è decisamente breve rispetto a tutta la discussione che è servita a presentarla.
Ricerca binaria: l’implementazione intbinsearch (int n, int v[], int dim) // pre: la dim. di v e’ >= dim e v[0..dim-1] è ordinato // post: i tale che v[i] == n se ne esisteuno, altrimenti: dim { inti = 0, j = dim - 1, m; while (i <= j) //inv. se n in v[0..n-1] allora n in V[i..j] {m = (i+j)/2; if (v[m] == n) return m; else if (n < v[m]) j = m - 1; else/* (v[m] < n) */i = m + 1;} return dim; // dim sta per “non trovato”}
4. Matrici in C++ • Unamatrice a due dimensioniviene vista come un vettorebidimensionale, o divettori, ognunodeiqualirappresentaunarigadellamatrice; la dichiarazionediunamatrice è simile a quellavettore, eccettocherichiede due dimensioni: double A[10][20]; // matrice 10righe x 20colonne di double A[i][j] = 7.23; // se 0 i < 10 e 0 j < 20 allora // scrive 7.23 come valoredella // i-esimariga e j-esima colonna di A
Passaggio di un vettore bidimensionale a funzione • Neiparametriformalidiunafunzione un vettore V didimensione 1 puòfigurare come • void f(intV[], intn){…} • senzal’indicazionedellalunghezzadi V dentroint V[]: la lunghezzan del vettore è a suavolta un parametro, chepuòcambiaredaunachiamataall’altra. • Nelcasodiunamatrice A di due o piùdimensioni, invece, le dimensionidebbonoesserecostanti indicate dentro A stesso, come segue: void g(double A [10][20] ) {…}
Passaggio di un vettore bidimensionale a funzione void g(double A [10][20] ) {…} Questotipodidichiarazione è molto scomoda: unafunzione g chestampaunamatrice A di 10x20 elementi non puòessereutilizzata per stampareunamatrice B di 20x20 elementi, perchèdouble A[10][20] e double B[20][20] sono due tipi diversi. La secondadimensionedi A, ovveroilnumero 20 dellecolonnedi A, è purtropponecessaria per ricostruirel’indirizzodellavariabile A[i][j] nellamacchinadi Von Neumann, e non è ricostruibile a partiredalla sola variabile A.
Un esempio di uso di matrici:il triangolo di Tartaglia Scriveremooraunafunzionecheassegna le prime n+1 righe del triangolodiTartaglia a unamatrice (n+1)x(n+1). Per definizione, ognielementopostoailati del triangolodiTartaglia vale 1, e ognialtroelementoè la sommadell’elementopostosopra e diquellopostosopra e a destra. Niccolò Tartaglia 1499-1557
Un esempio di uso di matrici:il triangolo di Tartaglia 0 1 2 3 4 5 6 Righeda 0 a 6 del “Triangolo” in unamatrice A di 7x7. Per definizione: 1 = A[0][0] = A[1,0] = A[2][0] = … =A[i,0] = … e 1 = A[0][0] = A[1,1] = A[2][2] = … = A[i,i] = … e per 0<j<i: A[i][j] = A[i-1][j-1]+A[i-1][j]. 1 1 1 1 2 1 1 3 3 1 1 4 6 4 1 1 5 10 10 5 1 1 6 15 20 15 6 1 0 1 2 3 4 5 6 Per es.: 6 = A[4][2] = A[3][1] + A[3][2] = 3 + 3.
Il triangolo di Tartaglia usando una matrice voidTartaglia(int n) // stamparigheda 0 a n del triangolodiTartaglia {int a[n+1][n+1]; //definiscematrice (n+1)x(n+1) for (inti = 0; i<=n; ++i) // costruzione “riga per riga” {a[i][0] = 1; // assegna 1 a tutta la colonna 0 for (intj = 1; j<i; ++j) // assegnacolonneda 1 a i-1 a[i][j] = a[i-1][j-1] + a[i-1][j]; a[i][i] = 1; // assegna 1 a tutta la diagonale} // stamparigheda 0 a n del triangolo …(vedipaginaseguente per sapere come) … }
Stampa del triangolo di Tartaglia voidTartaglia(int n) { // definiscerigheda 0 a n … (vedipaginaprecedente per sapere come) … // stamparigheda 0 a n for (inti = 0; i <= n; ++i) { for (int j = 0; j <= i; ++j) cout << setw(4) << a[i][j]; cout << endl;} } L’istruzionesetw(4) esegue la prossimastampa con almeno 4 spazi. Richiedediincludere la libreria: #include <iomanip.h>
5. I records (cenni) Un record è unatupladivaloriditipopossibilmentediverso (è questa la differenza con ivettori) a cui accediamoattraversoetichetteanzichéindici: struct <nome struttura> { <tipo1> <etichetta campo1>; ... <tipok> <etichetta campok>;} Come concetto matematico, un record corrisponde a un prodotto cartesiano: tipo1 x … x tipon mentre un vettore corrisponde a un insieme potenza: tipon
I record nella memoria • I record nella memoria di una macchina di Von Neumann sono rappresentati con celle adiacenti, ma di diversa dimensione. E’ necessario individuare ogni cella assegnadole un nome: Nome_1 … Nome_k Possiamorappresentareirazionali come frazioni, e le frazioni come un record di due campi: numeratore e denominatore. Si tratta solo di un esempio: irazionali non sonounastrutturadatiabbastanzacomplessadagiustificarel’usodei records.
Poiché qui i due campi hanno lo stesso tipo avremmo potuto scrivere: int num, den; Frazioni rappresentate da un record Ratio si aggiunge ai tipi definiti nel programma struct Ratio { int num; int den;}; Se r e’ un record cherappresentaunafrazione, indichiamonumeratore e denominatoredi r con r.num e r.den
Record come valori di funzioni • Diversamente dagli array, le struct in C++ sono passate (e restituite) per valore: dunque ogni chiamata costruisce una copia del record e la assegna al parametro formale della funzione Ratio NewRatio(int n, int d) /* Pre-cond.: d!=0 Post-cond.:NewRatio(n,d) restituisceil record cherappresenta n/d */ { Ratio r; r.num = n; r.den = d; return r;} int main() { Ratio a = NewRatio(2,3); // a=2/3 }
Record come valori di funzioni • Un altro esempio, la somma di frazioni. Le strutture in C++ sono passate per valore, creando delle copie dei valori passati. Ratio SumRatio(Ratio a, Ratio b) // post: restituisceilrazionale a + b { Ratio r; r.num = a.num * b.den + a.den * a.num; r.den = a.den * b.den; return r;} int main() { Ratio a, b; …; Ratio c = SumRatio(a,b);} // c = a + b
Vettori parzialmente riempiti Di un vettore occorre ricordare la dimensione; se poi se ne usa solo una parte, come abbiamo fatto nell’esercizio sui numeri primi, bisogna sapere sin dove è “riempito”, ovvero, quale è il prossimo indirizzo libero. v prox_libero Se non cisonoindirizziliberi, ilprossimoindirizzolibero per definizioneè la dimensione dim del vettore v.
Vettori parzialmente riempiti come record • Possiamo definire un record per rappresentare vettori parzialmente riempiti. Usiamo un record con due campi: un vettore e il primo indirizzo ancora libero del vettore (se esiste) struct Array { intv[10]; intprox_libero; }; /* prox_libero = indice del prossimoindirizzolibero */