strutture dati elementari parte 6 vettori e ricerca binaria matrici e triangolo di tartaglia records...
TRANSCRIPT
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/~stefanoCorso B: Prof. Ugo de’ Liguoro http://www.di.unito.it/~deligu
“Un quadrato magico di numeri”
Albert Durer. Melencolia,1514
(dettaglio)
16 3 2 13
5 10 11 8
9 6 7 12
4 15 14 1
Indice Parte 6: i Vettori1. Strutture dati: i vettori.2. Esempi elementari: stampa, somma,
test di uguaglianza, inversione per vettori.
3. Esempi più complessi: ricerca lineare e binaria per vettori.
4. Matrici: il triangolo di Tartaglia.5. 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 interiv[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].
I vettori nella memoria RAM
• Gli elementi di un vettore occupano spazi di memoria consecutivi nella memoria RAM della macchina di Von Neumann. L’indirizzo &v[i] di ogni elemento si calcola dunque con la formula: &v[i] = &v[0] + id, dove d = sizeof (tipo di 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)
d = dimensione di ogni elemento (es.: 4 bytes)
v[i]indirizzo
b+i*d
&v[i] = indirizzo v[i] = b+id
i = indice di v[i] (detto anche “spiazzamento” ). Es.: i=100, indirizzo di v[i] = 1 000 400.
v
Vettori: dichiarazione
• 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: int v[100];
Tipo degli elementi Nome del vettore:
v vale &v[0]
100 = numero degli elementi, o
dimensione di v Invece: sizeof(v) =
100*sizeof(int) = 400
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 fatto di essere dichiarata, ha un valore, ma non si può prevedere quale. Quindi si crea un vettore di dimensione “casuale”, a volte di miliardi di elementi, producendo in quest’ultimo caso un “crash” di programma */
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.
int v[100];
/* dichiarazione */
v[0] = 6;
/* assegnazione, accesso in scrittura */
int n = v[0];
/* assegnazione, accesso in lettura */
Gli interi tra [ ] hanno diverso significato in una dichiarazione e in una assegnazione: nella dichiarazione, 100 è la dimensione, nell’assegnazione, 0 è un indice
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;
i dichiarato entro il for esisterà soltanto
entro il for
Nell’esecuzione del for, i assumerà tutti i valori tra 0 e 99
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
• 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
Guardate l’esempio 6.3 del testo di Hubbard: spiega che la dimensione di a si calcola con la formula
sizeof(a)/sizeof(double)
Attenti a non uscire dai limiti di un vettore
• Se un vettore ha dimensione d ed i<0, oppure i d, allora v[i] non dovrebbe essere definito.
• Invece, in C/C++, v[i] esiste sempre, è per definizione il contenuto dell’indirizzo di memoria calcolato dalla formula:
&v[0] + i sizeof (tipo di v[])
• ossia un valore a caso!! Questa convenzione è introdotta per semplicità di calcolo. Il compilatore non ci avvisa quando utilizziamo un v[i] con i<0, oppure i d, sta a noi evitare che accada.
Questo non accade in PASCAL: vedi Hubbard paragrafo 6.4
2. Alcuni semplici esempi: stampa di un vettore
• 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];
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)
Somma e media di un array di numeri
double v[100];
double somma = 0;
for (int i = 0;
i < 100; i++)
somma = somma + v[i];
double media = somma/100.0; /*dividiamo per un numero reale per evitare l’arrotondamento*/
Per tutte le sommatorie di cui non si conosca a priori nemmeno un termine, il valore iniziale dell’accumulatore è 0, l’elemento neutro della somma
Anche la somma di un vettore deve avvenire “elemento per elemento” (dunque usando ad es. un ciclo for):
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 sono identificati con gli indirizzi dei loro primi elementi, dunque il test == confronta questi ultimi. Dato che v, w sono “allocati” in posizioni diverse della memoria, i loro primi elementi hanno diversi indirizzi, anche quando v, w hanno elementi di valore uguale. 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 trasporta il contatore i al primo indice per cui v[i]!=w[i], se ne esiste uno, altrimenti trasporta i fino ad N */int i = 0; while (i < N && v[i] == w[i]) i++;if (i == N) cout << "v,w hanno elementi uguali per indici uguali"; else /* i < N */ cout << "v, w hanno diverso l’elemento di posto" << i;
Un test di uguaglianza corretto 2
int i;
for (i=0; i < N && v[i] == w[i]; i++){ };
//il corpo del FOR e’ vuoto
if (i == N) cout << "v,w hanno uguali per indici uguali"; else cout << "v, w hanno diverso l’elemento di posto" << i;
i è dichiarata fuori del for perché l’if che segue ne possa fare uso
• 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 la traduzione della soluzione precedente in un ciclo FOR:
Passaggio di un vettore ad una funzione
• Un array viene passato alle funzioni sempre per indirizzo. Attenzione: nella dichiarazione del parametro scriviamo int a[], nella chiamata non ripetiamo il tipo e scriviamo solo a:int sum( int a[], int n) /* prec.:0<=n<=dim.a post.cond:sum(a,n) = somma di a[0..n-1]*/
{int i,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:
void scambia (int v[], int i, int j)// pre: 0 i, j < dimensione di v// post: scambia v[i] con v[j]{ int temp = v[i]; v[i] = v[j]; v[j] = temp; }
Inversione di un vettore 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.void inverti (int v[], int n) /* PRE: 0 n dimensione di v. POST: elementi v[0..n-1] in ordine inverso */{for (int i = 0, int j=n-1; i<j; i++,j--) scambia(v, i, j);} Attenzione: se un vettore v è
un parametro attuale di una funzione (per es. scambia) NON si scrive int v[] ma v
v[0] v[1] … v[n-2]v[n-1]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 dimensione di v e’ >= dim >=0. post: restituiamo true esiste i tale che v[i]==n */{bool trovato = false; /* “trovato” indica se n e’ gia’ stato trovato. “trovato” puo’ cambiare solo da false a true, mai viceversa.*/for(int i=0;i<dim;i++){if(v[i]==n)trovato=true;} return trovato;}
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 sse esiste i t.c. v[i] == n*/{bool trovato = false; //detta variabile “flag” for (int i=0; i<dim && !trovato == true; i++) if (v[i] == n) trovato = true; return trovato;}
Ricerca lineare con interruzione: seconda soluzione
• 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 dimensione di v e’ >= dim >= 0// post: true sse esiste i t.c. v[i] == n{ int i; for (i=0; i < dim && v[i] != n; i++){}; return (i < dim);}
Se i < dim è vero, il for è terminato perchè v[i] == n , altrimenti è terminato perchè il vettore è finito senza trovare n
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:
1. Manteniamo due indici, i e j, a delimitazione della porzione v[i…j] di v in cui cercare
2. Ad ogni passo confrontiamo n con il valore di indice medio in i..j, cioè m=(i+j)/2
3. 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.
Ricerca binaria 1: pre- e post-condizioni
1. Definiamo il problema. Input: il “valore cercato” in v è n. Pre-condizione: il vettore v di dimensione dim è ordinato.
• Post-condizione: restituire l’indice del valore cercato (un i tale che v[i]=n, se esiste), altrimenti la lunghezza dim di v (dim sta per “non trovato”)
0 dim-1=193 5 6 1318212125364349515360727483889195
0 dim-1=193 5 6 1318212125364349515360727483889195
Valore cercato: n = 25. Indice del valore cercato: 7 ( v[7]=25)7
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].
Per es.: se n=25 è in v, allora n è in v[i..j] = v[3..16].
indice 03 5 6 1318212125364349515360727483889195
i=3 j=16 indice 19Cerco n=25 in v[i…j]
v[i…j]
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
Se il valore cercato è n = 43 allora l’ho trovato
Punto intermedio m di i…j
0 dim-1= 193 5 6 1318212125364349515360727483889195
Spazio dove avviene la ricerca di n
Ricerca binaria 3: il funzionamento
Valore cercato: n = 25 Punto intermedio m di i..j0 193 5 6 1318212125364349515360727483889195
Spazio di ricerca
• 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].
3. La ricerca binaria cerca un modo per avvicinarsi alla soluzione mantenendo vero l’invariante
Ricerca binaria 3: il funzionamento
Valore cercato: n = 60 Punto intermedio m di i..j
0 193 5 6 1318212125364349515360727483889195
Spazio di ricerca
• 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] .
3. La ricerca binaria cerca un modo per avvicinarsi alla soluzione mantenendo vero l’invariante
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 ….
Valore cercato (e trovato): n = 43
Punto intermedio di indice m
0 193 5 6 1318212125364349515360727483889195
Spazio di ricerca
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)
Valore cercato (e non trovato): n=23
n=23 dovrebbe essere qui in mezzo, tra 21 e 25: ma questo intervallo è vuoto. Il valore n non viene trovato.
0 193 5 6 1318212125364349515360727483889195
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.
0 193 5 6 1318212125364349515360727483889195
Spazio di ricerca iniziale: v[0..n-1]
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 sottovettore V[i..j], a cui limitiamo la
ricerca, è compreso tra le posizioni i e j incluse
i j
• Il punto medio m ha indice: (i + j) diviso 2
• Se i > j allora il sottovettore V[i..j] è vuoto
0 193 5 6 1318212125364349515360727483889195
Spazio di ricerca in un passo generico della computazione
m
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
int binsearch (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 esiste uno, altrimenti: dim{ int i = 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++
• Una matrice a due dimensioni viene vista come un vettore bidimensionale, o di vettori, ognuno dei quali rappresenta una riga della matrice; la dichiarazione di una matrice è simile a quella vettore, eccetto che richiede 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 valore della // i-esima riga e j-esima colonna di A
Passaggio di un vettore bidimensionale a funzione
• Nei parametri formali di una funzione un vettore V di dimensione 1 può figurare come
• void f(int V[], int n){…}
• senza l’indicazione della lunghezza di V dentro int V[]: la lunghezza n del vettore è a sua volta un parametro, che può cambiare da una chiamata all’altra.
• Nel caso di una matrice A di due o più dimensioni, invece, le dimensioni debbono essere costanti indicate dentro A stesso, come segue:
void g(double A [10][20] ) {…}
Passaggio di un vettore bidimensionale a funzione
Questo tipo di dichiarazione è molto scomoda: una funzione g che stampa una matrice A di 10x20 elementi non può essere utilizzata per stampare una matrice B di 20x20 elementi, perchè double A[10][20] e double B[20][20] sono due tipi diversi.
La seconda dimensione di A, ovvero il numero 20 delle colonne di A, è purtroppo necessaria per ricostruire l’indirizzo della variabile A[i][j] nella macchina di Von Neumann, e non è ricostruibile a partire dalla sola variabile A.
void g(double A [10][20] ) {…}
Un esempio di uso di matrici:il triangolo di Tartaglia
Niccolò Tartaglia1499-1557
Scriveremo ora una funzione che assegna le prime n+1 righe del triangolo diTartaglia a una matrice (n+1)x(n+1). Per definizione, ogni elemento posto ai lati del triangolo di Tartaglia vale 1, e ogni altro elemento è la somma dell’elemento posto sopra e di quello posto sopra e a destra.
Un esempio di uso di matrici:
il triangolo di Tartaglia 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
Righe da 0 a 6 del “Triangolo” in una matrice 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].
0 1 2 3 4 5 6
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
void Tartaglia(int n)// stampa righe da 0 a n del triangolo di Tartaglia{int a[n+1][n+1]; //definisce matrice (n+1)x(n+1) for (int i = 0; i<=n; ++i) // costruzione “riga per riga” {a[i][0] = 1; // assegna 1 a tutta la colonna 0 for (int j = 1; j<i; ++j) // assegna colonne da 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}// stampa righe da 0 a n del triangolo …(vedi pagina seguente per sapere come) … }
Stampa del triangolo di Tartaglia
void Tartaglia(int n){ // definisce righe da 0 a n
… (vedi pagina precedente per sapere come) …
// stampa righe da 0 a n for (int i = 0; i <= n; ++i) { for (int j = 0; j <= i; ++j) cout << setw(4) << a[i][j]; cout << endl;} }L’istruzione setw(4) esegue la prossima stampa con almeno 4 spazi. Richiede di includere la libreria:
#include <iomanip.h>
5. I records (cenni)
Un record è una tupla di valori di tipo possibilmente diverso (è questa la differenza con i vettori) a cui accediamo attraverso etichette anziché 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
…
Possiamo rappresentare i razionali come frazioni, e le frazioni come un record di due campi: numeratore e denominatore. Si tratta solo di un esempio: i razionali non sono una struttura dati abbastanza complessa da giustificare l’uso dei records.
Frazioni rappresentate da un record
struct Ratio {
int num; int den;};
Poiché qui i due campi hanno lo stesso tipo avremmo potuto scrivere:int num, den;
Ratio si aggiunge ai
tipi definiti nel programma
Se r e’ un record che rappresenta una frazione, indichiamo numeratore e denominatore di 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!=0Post-cond.:NewRatio(n,d) restituisce il record che rappresenta 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: restituisce il razionale 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.
prox_libero
v
Se non ci sono indirizzi liberi, il prossimo indirizzo libero 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 { int v[10];
int prox_libero; };
/* prox_libero = indice del prossimo indirizzo libero */
Funzioni su vettori parzialmente riempiti
void Mostra(Array a) /*post: stampa la parte riempita di v, e cioé: (a.v)[0..a.prox_libera-1] */
{ for (int i = 0; i < a.prox_libero; i++)
cout << a.v[i] << " ";
cout << endl;}
Un esempio di una funzione che agisce sui vettori parzialmente riempiti: la stampa di tutti gli elementi del vettore effettivamente in uso (dunque fino alla prossima posizione libera esclusa).
Funzioni su vettori parzialmente riempiti
bool Aggiungi(int n, Array& a)
/* post: se a.prox_libera < 10, aggiunge n in ultima pos. e restituisce true; restituisce false altrimenti */
{if (a.prox_libero < 10)
{a.v[a.prox_libero] = n; a.prox_libero++; return true;}
else return false;}
Un altro esempio di una funzione che agisce sui vettori parzialmente riempiti: come aggiungere un elemento a quelli effettivamente in uso
Riepilogo
• Vi sono due strutture dati predefinite per rappresentare collezioni finite di valori: vettori (array) e record (struct)
• I vettori hanno dimensione fissa, elementi omogenei, e sono passati per riferimento
• Vettori a due dimensioni rappresentano una matrice.
• I record hanno un numero fisso di campi individuati da nomi, hanno tipi eventualmente diversi di valori, e sono passati per valore.