Tipi strutturati
• I tipi considerati finora hanno la caratteristica comune di
non essere strutturati: ogni elemento è una singola
entità.
• Se il programma deve trattare collezioni di dati, anche se
sono dello stesso tipo, a ognuno deve essere associato un
identificatore.
• Supponendo di dover gestire le paghe in una ditta di
3000 dipendenti sarebbe necessario definire 3000
variabili diverse, del tipo: operaio1, operaio2, ....,
impiegato1, impiegato2, ....., ecc.
© Piero Demichelis
1
Tipi strutturati
• I linguaggi ad alto livello permettono di ovviare a questo
inconveniente con i tipi strutturati, caratterizzati sia dal
tipo dei loro componenti che dai legami strutturali tra i
componenti stessi, cioè dal metodo di strutturazione.
• Il linguaggio C, anche in questo caso, si presenta
ambivalente: permette di creare dati aggregati, senza
peraltro che questi costituiscano dei tipi nell'accezione
classica.
• Infatti l'organizzazione strutturale dei dati e le modalità di
accesso ai singoli elementi che costituiscono la struttura
non vengono nascoste all'utente, che invece può
interagire con esse in piena libertà.
© Piero Demichelis
2
Vettori
• Il vettore è una collezione di variabili tutte dello stesso
tipo (detto appunto tipo base) di lunghezza prefissata.
• Questa collezione di variabili è individuata da un unico
nome, il nome appunto del vettore.
• Ogni elemento del vettore è detto componente ed è
individuato dal nome del vettore seguito da un indice
posto tra parentesi quadre.
• L'indice può essere solo di tipo intero o enumerato e
determina la posizione dell'elemento nel vettore.
© Piero Demichelis
3
Vettori
• L'intervallo dei valori assunti dall'indice determina la
dimensione del vettore, che deve essere limitata.
• Definizione generale di vettore:
tipo_componente nome_vettore [numero_componenti];
• tipo_componente può essere un qualunque tipo semplice,
• nome_vettore è il nome da attribuire al vettore,
• numero_componenti, racchiuso tra parentesi quadre, è il
numero di elementi che costituiscono il vettore e pertanto
deve essere un intero o un'espressione costante di tipo
intero.
© Piero Demichelis
4
Vettori
• L'indice del vettore può assumere valori compresi tra 0 e
numero_componenti – 1.
• L'indice corrisponde pertanto alla posizione nel vettore
dell’elemento a cui è associato, rispetto al primo.
• Gli elementi del vettore sono memorizzati in celle di
memoria contigue (successive)!
Vettore a (di 5 elementi):
a[0]
© Piero Demichelis
a[1]
a[2]
a[3]
a[4]
5
Vettori
• Esempi di definizioni di vettore:
#define NUM_MATERIE 20
char s[8];
double giornate[167];
int voti_ottenuti[NUM_MATERIE];
• La variabile s è un vettore di 8 elementi: equivale alle 8
variabili di tipo char: s[0], s[1], s[2], s[3], s[4], s[5],
s[6], s[7].
• La variabile giornate è un vettore di 167 elementi di tipo
double il cui indice può variare tra 0 e 166.
• La variabile voti_ottenuti è un vettore di 20 elementi di
tipo intero il cui indice può variare tra 0 e 19.
© Piero Demichelis
6
Inizializzazione di un vettore
• E’ possibile assegnare un valore iniziale ad un vettore al
momento della sua definizione.
• L’operazione consiste nell’indicare i valori degli elementi
del vettore separati tra loro da virgola.
• Sintassi (per un vettore di N elementi):
= {<valore_0>, <valore_1>, ..., ,<valore_N-1>};
• Esempio:
int lista[4] = { 2, 0, -1, 5 };
© Piero Demichelis
7
Inizializzazione di un vettore
• NOTA: se vengono specificati meno di N elementi,
l’inizializzazione comincia comunque a partire dal primo
valore e lascia non assegnati i rimanenti.
• Esempi:
int s[4] = {2, 0, -1}; /*
s[0]=2, s[1]=0, s[2]=-1, s[3]=?
char p[5] = {‘a’, ‘b’, ‘c’};
/*
p[0]=‘a’, p[1]=‘b’, p[2]=‘c’, p[3]=?, p[4]=?
double d[2] = {2.56};
/* d[0]=2.56, d[1]=? */
© Piero Demichelis
*/
*/
8
Vettori e indici
• L’indice, che definisce la posizione di un elemento di un
vettore, DEVE essere rigorosamente un intero!
• Può ovviamente anche essere un’espressione, più o meno
complessa, purché con risultato intero.
• Esempio:
double x, a[30];
/* a vettore di double */
int i, j, k;
.............
x = a[2*i+j-k]; /* espressione aritmetica per l’indice */
© Piero Demichelis
9
Vettori e cicli
• I cicli sono particolarmente utili per “scandire” un vettore.
• Utilizzo tipico: applicazione iterativa di un’operazione sugli
elementi di un vettore.
• Schema:
int data[10], ind;
.............
for (ind=0; ind<10; ind++)
{
elaborazione dell’elemento data[ind]
}
• Ad ogni ciclo è interessato l’elemento individuato
dall’indice ind.
© Piero Demichelis
10
Vettori
• Non ci sono operatori che agiscono sul vettore nel suo
complesso: non è lecito pertanto l'assegnamento di un
vettore ad un altro vettore!!!!!
• Se vett_x e vett_y sono vettori, l'istruzione:
vett_x = vett_y;
è errata anche se vett_x e vett_y sono dello stesso tipo.
• Per trasferire un vettore in un altro occorre
copiare un elemento per volta. !!!!!
© Piero Demichelis
11
Vettori
• Esempio: copia il vettore vett_iniz nel vettore vett_fin
#include <stdio.h>
#define NUMDATI 5
int vett_iniz[NUMDATI] = {11, -2, -63, 4, 15};
int vett_fin[NUMDATI], indice;
main()
{
for (indice = 0; indice < NUMDATI; indice++)
vett_fin[indice] = vett_iniz[indice];
}
© Piero Demichelis
12
Vettori
• Sugli elementi del vettore agiscono gli operatori previsti
per il tipo_componente. Pertanto è lecito scrivere:
valor_fin = vett_x[m1] + vett_y[m2];
purché, naturalmente, valor_fin, il vettore vett_x e il
vettore vett_y siano dello stesso tipo.
• Il tempo necessario per accedere a un elemento di un
vettore è indipendente dal valore dell'indice: il vettore è
pertanto una
struttura ad accesso casuale
© Piero Demichelis
13
Esempio
• Leggere 10 valori da tastiera e memorizzarli in un
vettore; quindi calcolarne il minimo ed il massimo.
• Pseudocodice:
- Con un indice ind che varia tra 0 e 9:
•legge un dato e lo salva in vettdati[ind];
- Inizializzo la variabile massimo e la variabile minimo col primo
elemento del vettore vettdati[0];
- Con un indice ind che varia tra 1 e 9:
• se vettdati[ind] è più grande di massimo:
massimo
vettdati[ind];
altrimenti se vettdati[ind] è più piccolo di minimo:
minimo
vettdati[ind];
- Visualizza massimo e minimo
© Piero Demichelis
14
Esempio
#include <stdio.h>
#define NUMDATI 10
main()
{
int minimo, massimo, ind;
int vettdati[NUMDATI];
/*
lettura dei dati
*/
for (ind = 0; ind < NUMDATI; ind++)
{
printf (“\nIntroduci vettdati[%d]: ", ind);
scanf ("%d", &vettdati[ind]);
}
© Piero Demichelis
15
Esempio
/*
cerca il massimo e il minimo
*/
massimo = vettdati[0];
minimo = vettdati[0];
for (ind = 1; ind < NUMDATI; ind++)
{
if (vettdati[ind] > massimo)
massimo = vettdati[ind];
else
{
if (vettdati[ind] < minimo)
minimo = vettdati[ind];
}
}
printf (“\nIl massimo è %d e il minimo è %d\n ", massimo, minimo);
}
© Piero Demichelis
16
Esempio
• Scrivere un programma che legga un numero decimale
positivo minore di 1024 e lo converta nella corrispondente
codifica binaria.
• Analisi
Per convertire in binario puro un numero decimale occorre
eseguire una sequenza di divisioni per 2 prendendo i resti (0
oppure 1): occorre dunque un vettore per memorizzare questi
resti.
Poiché i numeri devono essere compresi tra 0 e 1023 sono
sufficienti 10 bit: il nostro vettore sarà pertanto lungo 10 elementi e
in ogni elemento memorizzeremo una cifra.
© Piero Demichelis
17
Esempio
Analisi (continua):
I resti ottenuti dalle divisioni per 2 vanno però letti al contrario,
conviene pertanto riempire il vettore a partire dall’ultimo
elemento.
Per eseguire le divisioni per due è intuitivo che conviene servirsi di
un ciclo il quale, ad ogni iterazione, calcola un nuovo bit (resto
della divisione per 2).
for o while? È pressochè indifferente usare un ciclo for o un ciclo
while: occorre però che le inizializzazioni delle variabili siano
adattate al ciclo prescelto.
Se usiamo il for avremo come “dato-guida” del ciclo l’indice del
vettore;
Se usiamo il while il “dato-guida” sarà il resto delle divisioni per 2.
© Piero Demichelis
18
Esempio (con while)
#include <stdio.h>
main()
{
int ind, numero, num;
int binario[10];
/*
inizializza il vettore risultato con tutti zeri
*/
for (ind = 0; ind < 10; binario[ind++]=0);
/* equivale a : for (ind=0; ind<10; ind++)
binario[ind] = 0;
*/
printf (“\nIntroduci un numero intero positivo minore di 1024: ");
scanf ("%d", &numero);
© Piero Demichelis
19
Esempio (con while)
if ((numero >= 0) && (numero < 1024))
{
num = numero;
/* num è il “dato-guida” del ciclo
*/
ind = 9;
while (num != 0)
/* finché num è diverso da 0! */
{
binario[ind] = num % 2;
/* calcola un nuovo bit */
num /= 2;
/* aggiorna num per il prossimo ciclo */
ind--;
/* aggiorna l’indice del vettore */
}
printf ("\nConversione del numero %d: ", numero);
for (ind=0; ind<10; ind++)
/* Visualizza il vettore: */
printf ("%1d",binario[ind]);
/* un bit per volta */
}
else
printf (“\nNumero non lecito!”);
}
© Piero Demichelis
20
Esempio (con for)
#include <stdio.h>
main()
{
int ind, numero, num;
int binario[10];
/* non è necessario inizializzare il vettore in quanto il ciclo for deve */
/* scrivere comunque tutti gli elementi del vettore
*/
printf (“\nIntroduci un numero intero positivo minore di 1024: ");
scanf ("%d", &numero);
© Piero Demichelis
21
Esempio (con for)
if ((numero >= 0) && (numero < 1024))
{
num = numero;
for (ind = 9; ind >= 0; ind--) /* con un indice che va da 9 a 0 */
{
binario[ind] = num % 2;
/* calcola un nuovo bit */
num /= 2;
/* aggiorna num per il prossimo ciclo */
}
printf ("\nConversione del numero %d: ", numero);
for (ind = 0; ind < 10; ind++)
/* Visualizza il vettore: */
printf ("%1d",binario[ind]);
/* un bit per volta! */
}
else
printf (“\nNumero non lecito!”);
}
© Piero Demichelis
22
Vettori
• Quando si definisce un vettore il compilatore riserva
un’area di memoria sufficiente per contenerlo e associa
l'indirizzo iniziale di quell'area al nome simbolico
(identificatore) da noi scelto per il vettore.
• Pertanto il nome vett_dati non è una vera e propria
variabile, ma piuttosto un puntatore : in pratica vett_dati
è l'indirizzo di memoria del primo elemento del vettore
cioè l'indirizzo di vett_dati[0].
• Ecco perché è errata l'istruzione:
voti_ottenuti = voti_semestre;
© Piero Demichelis
23
Vettori multidimensionali
• Il concetto di vettore come collezione di elementi
consecutivi, può essere esteso immaginando che gli
elementi siano a loro volta dei vettori: si ottiene così un
vettore multidimensionale o matrice.
• La definizione di matrice ricalca pienamente quella del
vettore:
tipo_comp nome [dim1] [dim2].........;
• tipo_comp può essere un qualunque tipo semplice,
• dim1, dim2, ecc.; racchiusi tra parentesi quadre,
definiscono il numero di elementi di ogni dimensione.
© Piero Demichelis
24
Vettori multidimensionali
Esempio:
matrice bidimensionale di numeri interi formata da tre
righe e 5 colonne:
int a[3][5];
a
a[0][0] a[1][0] a[2][0] a[3][0] a[4][0]
a[0]
a[0][1] a[1][1] a[2][1] a[3][1] a[4][1]
a[1]
a[0][2] a[1][2] a[2][2] a[3][2] a[4][2]
a[2]
© Piero Demichelis
25
Vettori multidimensionali
• Accesso ad un elemento:
<nome vettore> [<posizione1>] [<posizione2>].............
• Per esempio
matrix [10][20][15]
individua l'elemento di coordinate rispettivamente 10, 20 e 15 nella
matrice a 3 dimensioni matrix.
• Inizializzazione di un vettore multidimensionale:
- deve essere effettuata per righe!
int vett[3][2] = {
};
{8,1},
{1,9},
{0,3}
© Piero Demichelis
/*
/*
/*
vett[0]
vett[1]
vett[2]
*/
*/
*/
26
Vettori multidimensionali e cicli
• Per un vettore a più dimensioni, la scansione va applicata
a tutte le dimensioni: in questo caso si devono in genere
utilizzare “cicli annidati ”.
• Esempio: elaborazione degli elementi di un vettore
bidimensionale.
int vett [3][5];
…
for (i = 0; i < 3; i++) {
for (j = 0; j < 5; j++) {
... elaborazione su vett[i][j]
}
}
© Piero Demichelis
/*
/*
per ogni riga
*/
per ogni colonna */
27
Scarica

Vettori e Matrici