You are on page 1of 288

Cesare Rota

Programmare con C++

EDITORE ULRICO HOEPLI MILANO


Copyright © Ulrico Hoepli Editore S.p.A. 2009
via Hoepli 5, 20121 Milano (Italy)
tel. +39 02 864871 Ð fax +39 02 8052886
e-mail hoepli@hoepli.it

www.hoepli.it

Tutti i diritti sono riservati a norma di legge


e a norma delle convenzioni internazionali

ISBN 978-88-203-4248-7

Ristampa:

4 3 2 1 0 2009 2010 2011 2012 2013

Realizzazione editoriale
ART Servizi Editoriali S.p.A. - Bologna
www.art.bo.it
Coordinamento editoriale: Monica Monari
Redazione: Barbara Megali
Progetto grafico: Marina Baldisserri
Impaginazione: Sonia Bertusi

Copertina: MN&CG S.r.l., Milano

Stampa: Art Grafiche Franco Battaia S.r.l. - Zibido San Giacomo (MI)

Printed in Italy
Indice

Presentazione 5 4.2 Output formattato 58


Sezione 1 - Premesse 7 4.3 Caratteri di escape 62
4.4 Uso di Ò\\Ó e del carattere Ò@Ó 63
U.D. 1 - Sistemi di numerazione 8 Esercizi 64
1.1 Sistema di numerazione decimale 9
1.2 Notazione polinomiale 9 U.D. 5 - Operatori 66
1.3 Numeri decimali 10 5.1 Operatori aritmetici 67
1.4 Sistema di numerazione binario 11 5.2 Operatori aritmetici composti 73
1.5 Conversione da base 2 a base 10 12 5.3 Operatori relazionali 78
1.6 Somma e sottrazione nel sistema 5.4 Operatori logici 80
binario 13 Esercizi 86
1.7 Sistema di numerazione esadecimale 14 Sezione 3 - Organizzazione degli algoritmi 89
1.8 Conversione da binario a esadecimale U.D. 6 - Algoritmi e pseudocodifica 90
e viceversa 16
6.1 Analisi del problema 91
Esercizi 18
6.2 Primo esempio 92
U.D. 2 - Codifica delle informazioni 20 6.3 Algoritmi 94
2.1 Introduzione 21 6.4 Dati e istruzioni 96
2.2 Rappresentazione dei caratteri 22 6.5 Istruzioni di assegnamento 97
2.3 Rappresentazione interna 6.6 Istruzioni di ingresso e di uscita
dei numeri interi 24 dei dati 99
2.4 Rappresentazione dei numeri reali 28 6.7 Teorema di Jacopini-Bš hm 100
Esercizi 31 6.8 Struttura di alternativa 102
Sezione 2 - Primi elementi 6.9 Struttura di ripetizione 104
di programmazione 33 6.10 Considerazioni sulla pseudocodifica 107
Esercizi 109
U.D. 3 - Introduzione a C++ 34
3.1 Primo programma 35 U.D. 7 - Istruzioni di selezione 112
3.2 Analisi del codice 38 7.1 Istruzione if 113
3.3 Definizione e assegnazione 40 7.2 Istruzione if..else 115
3.4 Tipi di dato 42 7.3 If nidificati 118
3.5 Tipo di dato intero 43 7.4 Istruzione switch
3.6 Tipo char 44 (selezione multipla) 120
3.7 Numeri in virgola mobile 46 7.5 Raggruppamento dei case 122
3.8 Casting 48 Esercizi 125
3.9 Tipo booleano 48 U.D. 8 - Istruzioni di ripetizione 127
3.10 Costanti 49 8.1 Istruzione while 128
Esercizi 51 8.2 Tabulazione 130
U.D. 4 - Visualizzazione e acquisizione 54 8.3 Istruzione do.. while 132
4.1 Acquisizione delle informazioni: 8.4 Istruzione for 134
il metodo cin 55 Esercizi 139

3
Indice

U.D. 9 - Le funzioni 142 12.4 Ereditarietˆ 213


9.1 Le funzioni 143 12.5 Introduzione alle classi 215
9.2 La funzione pi• importante 144 12.6 Terminologia e rappresentazione
9.3 Definizione delle funzioni 147 grafica 216
9.4 Ambito delle variabili 148 12.7 Dichiarazione degli oggetti 218
9.5 Variabili locali e globali 151 Esercizi 222
9.6 Valori di ritorno 152 U.D. 13 - Polimorfismo ed ereditarietˆ 223
9.7 Passaggio dei parametri 13.1 Costruttori 224
per valore 153 13.2 Costruttori parametrizzati 227
9.8 Passaggio dei parametri 13.3 Membri static di una classe 230
per riferimento 157 13.4 Overloading 234
9.9 Ricorsione 161 13.5 Polimorfismo 236
9.10 Funzioni matematiche 163 13.6 Ereditarietˆ 240
Esercizi 165 Esercizi 249
Sezione 4 - Strutture dei dati 167 Sezione 6 - Operare con gli archivi 251
U.D. 10 - Enumerazioni e array 168 U.D. 14 - Archivi 252
10.1 Introduzione 169 14.1 Definizione di archivio 253
10.2 Tipi enumerativi (enumerazioni) 169 14.2 Dati 254
10.3 Tipo vettore 171 14.3 Definizione di record 255
10.4 Vettori in C++ 172 14.4 Operazioni fondamentali
10.5 Caricamento di un vettore sugli archivi 256
in memoria 176 14.5 I/O standard e su memoria
10.6 Array di dimensione variabile 178 di massa 257
10.7 Matrici 180 14.6 Tipi di archivio 259
10.8 Passaggio di un vettore come 14.7 Tipi di accesso 260
parametro a una funzione 187 Esercizi 261
Esercizi 190
U.D. 15 - File di testo 262
U.D. 11 - Stringhe e strutture 192 15.1 Creazione di un file di testo 263
11.1 Definizione di stringa 193 15.2 Lettura di un file di testo 266
11.2 Lunghezza di una stringa 195 15.3 Accodamento 268
11.3 Concatenazione ed estrazione 197 Esercizi 271
11.4 Confronti tra stringhe 200
Sezione 7 - Le eccezioni 273
11.5 Caratteri e stringhe C 201
11.6 Dichiarazione di una struttura 202 U.D. 16 - Gestione delle eccezioni 274
11.7 Metodi costruttori 204 16.1 Concetto di anomalia 275
Esercizi 207 16.2 Eccezioni 277
Esercizi 283
Sezione 5 - Classi e oggetti 209
Appendice A - Riepilogo degli operatori 285
U.D. 12 - Concetti generali 210
12.1 Introduzione alla OOP 211 Appendice B - Sequenze di caratteri
12.2 Incapsulazione 211 escape 286
12.3 Polimorfismo 212 Indice analitico 287

4
Presentazione

Il presente volume espone, in modo chiaro ed efficace, le caratteristiche del linguaggio C++
e ha il duplice scopo di descriverne la sintassi e di evidenziarne le potenzialità.

In particolare il libro:

: si rivolge allo studente come un manuale di facile consultazione per la programma-


zione;
: presenta le basi teoriche per lo sviluppo delle applicazioni informatiche.

Il primo obiettivo si realizza tramite numerosi esempi presenti nel testo, che forniscono
chiare indicazioni sulle caratteristiche sintattiche del linguaggio. Per quanto riguarda le
basi teoriche sono stati messi in rilievo i fondamenti dei cinque argomenti di base per la
programmazione: la rappresentazione dei dati, le strutture di controllo utilizzabili nella
costruzione di un algoritmo, le strutture dei dati, la programmazione orientata agli og-
getti e la gestione dei file.

Il libro è suddiviso in sette sezioni.

1 La sezione Premesse sviluppa gli argomenti della codifica binaria delle informazioni.
2 In Primi elementi di programmazione vengono descritti i concetti di variabile,
costante e tipi di dato, le operazioni di input/output da console e gli operatori arit-
metici.
3 Nella sezione Organizzazione degli algoritmi viene introdotta la nozione di algorit-
mo e sono descritte le principali strutture di controllo sia in pseudocodifica sia in C++.
4 Nella sezione Strutture dei dati vengono definite le principali strutture statiche dei
dati.
5 L’intera sezione Classi e oggetti è dedicata alle nozioni fondamentali della OOP e
vengono presentati i concetti principali della programmazione orientata agli ogget-
ti quali l’incapsulamento, il polimorfismo e l’ereditarietà.
6 La sezione Operare con gli archivi spiega le nozioni di base per la definizione de-
gli archivi di dati.
7 La sezione Le eccezioni descrive gli accorgimenti essenziali per la realizzazione di
applicazioni “robuste”.

Ogni sezione è suddivisa in Unità didattiche le quali contengono un numero limitato di


paragrafi, la cui trattazione è, di norma, contenuta in circa due pagine. Ne risulta un testo

5
Presentazione

di facile lettura, che aiuta lo studente a concentrarsi, di volta in volta, su un singolo ele-
mento del discorso. Tutti i concetti presentati sono accompagnati da un esempio, che
mette in pratica quanto esposto. Ogni esempio contiene un listato di codice, una figura
che illustra una prova di esecuzione del codice proposto e lÕanalisi dettagliata del codice
stesso; questÕultima parte dellÕesempio presenta una descrizione dettagliata degli aspetti
pi• significativi del linguaggio C++ presenti nellÕesempio.

Per ogni sezione sono indicati gli obiettivi generali che si vogliono raggiungere, mentre
nella prima pagina di ogni Unitˆ didattica • specificato per lo studente ÒChe cosa impare-
rai a fareÓ e ÒChe cosa dovrai studiareÓ. In concreto, gli obiettivi generali presentati allÕi-
nizio di ogni modulo descrivono le capacitˆ che lo studente deve acquisire. Le voci ÒChe
cosa imparerai a fareÓ e ÒChe cosa dovrai studiareÓ indicano rispettivamente le compe-
tenze e le conoscenze che devono essere apprese dallÕalunno.

Considerando lÕampiezza della trattazione, il libro include tutti i contenuti dei program-
mi didattici tradizionalmente affrontati nelle classi terze degli istituti tecnici.
In particolare pu˜ essere adottato nella classe terza degli Istituti Tecnici Industriali con in-
dirizzo ABACUS o informatica industriale, in quella degli Istituti Tecnici Commerciali
con indirizzo MERCURIO o programmatori, nonchŽ in quella dei Licei scientifici tec-
nologici.

LÕAutore

6
Sezione 1
Premesse


Obiettivi ◊ Nozioni fondamentali sui sistemi di numerazione
◊ Sistemi di numerazione in base diversa da 10
◊ Introduzione alla codifica delle informazioni

& Questa sezione contiene


U.D. 1
U.D. 2
Sistemi di numerazione
Codifica delle informazioni
1
Unitˆ didattica
Sistemi di numerazione

CHE COSA IMPARERAI A FARE

$ Rappresentare un numero con notazione polinomiale


$ Utilizzare la numerazione in base 2
$ Trasformare un numero da base 10 a base 2 e viceversa
$ Utilizzare la numerazione esadecimale
$ Trasformare un numero da base 10 a base 16 e viceversa
$ Trasformare un numero da base 2 a base 16 e viceversa
$ Eseguire le operazioni aritmetiche con basi diverse da 10

CHE COSA DOVRAI STUDIARE

$ Notazione polinomiale dei numeri


$ Cifre del sistema binario e del sistema esadecimale
$ Procedure di trasformazione da base 10 a base
diversa da 10
$ Metodologia di calcolo aritmetico con basi diverse da 10
Unitˆ didattica
Sistemi di numerazione
1
1.1 Sistema di numerazione decimale
Il sistema di numerazione di pi• largo uso nel mondo • il sistema di numerazione decimale o a base
10. Esso si avvale di dieci simboli o cifre che, come • noto, sono:
0, 1, 2, 3, 4, 5, 6, 7, 8, 9
stabilendo che tali simboli rappresentino, rispettivamente, i primi dieci numeri naturali (lo zero
compreso), cio• i numeri i cui nomi sono:
zero, uno, due, tre, quattro, cinque, sei, sette, otto, nove.
Il successivo di nove si chiama dieci.

Si dice che dieci • la base del sistema di numerazione decimale.

Da quanto convenuto, segue che ogni numero contiene un certo numero di unitˆ semplici, un certo
numero di decine, un certo numero di centinaia ecc., pertanto ogni numero naturale pu˜ essere
scritto come una somma i cui termini sono prodotti di una potenza di dieci per una opportuna cifra.

Esempio..............................................................................................................................
Indicando, per ora, con la lettera d il numero dieci, si ha:
novemilatrecentoquarantasette =
9 migliaia + 3 centinaia + 4 decine + 7 unitˆ =
9 × d3 + 3 × d2 + 4 × d1 + 7 × d0
La scrittura di queste cifre, una dopo lÕaltra, costituisce la scrittura posizionale, in base 10, del nu-
mero dato.
In tal modo, il numero ÒnovemilatrecentoquarantasetteÓ si scrive:
9347
...........................................................................................................................................

1.2 Notazione polinomiale


Il numero dieci si indica con il simbolo 10 (si legga: Òuno, zeroÓ). Il numero novemilatrecentoqua-
rantasette dellÕesempio precedente, si scrive:
9347 = 9 × 103 + 3 × 102 + 4 × 101 + 7 × 100
che • un polinomio ordinato secondo le potenze di 10.
In generale, possiamo dire che un numero naturale viene cos“ scritto:
anan−lanÐ2 ... a2ala0 = an × l0n + anÐl × l0n−l + ... + a2 × l02 + al × l0 + a0
dove i simboli an sono le cifre che compongono il numero.

9
Sezione 1 - Premesse

Ogni cifra di un numero naturale ha un valore intrinseco, che • quello che ha in quanto numero, e un valo-
re che dipende dalla posizione che occupa nel numero.
Per esempio, nel numero:
444
i tre Ò4Ó hanno lo stesso valore intrinseco come cifre singole, ma valori diversi in base alla posizione che
occupano nel numero: infatti, il primo Ò4Ó da destra rappresenta quattro unitˆ semplici, il secondo rap-
presenta quattro decine e il terzo quattro centinaia.
Le cifre, quindi, acquistano maggior significato da destra a sinistra. Per questo motivo la prima cifra a de-
stra • la Òmeno significativaÓ e la prima cifra a sinistra • la Òpi• significativaÓ.

1.3 Numeri decimali


Oltre ai numeri naturali, anche ai numeri frazionari si pu˜ dare la rappresentazione di un polinomio
ordinato secondo le potenze di 10, come rappresentato di seguito.

0,1 = 10Ð1
0,01 = 10Ð2
ÉÉÉÉÉÉ
ÉÉÉÉÉÉ
0,00É1 = 10Ðn
{

n cifre decimali

LÕespressione polinomiale di un numero frazionario •, quindi:


N = an × 10n + an−1 × 10n−1 + ... + a1 × 10 + a0 × 100 + a−1 × 10−1 + a−2 × 10−2 + É
e la scrittura simbolica •:
N = anan−1 É a1a0a−1a−2 É
Invece del segno Ò,Ó (virgola) usato comunemente per separare le cifre della parte intera da quelle
della parte non intera, si userˆ (da qui in avanti) il segno Ò.Ó, detto punto radice o punto decimale.

Esempio..............................................................................................................................
Il numero
N = 7 × 103 + 3 × 10 + 2 × 10−1 + 3 × 10−3
scritto nella forma simbolica •:
N = 7030.203
...........................................................................................................................................

La scelta della base per la scrittura dei numeri • determinata soltanto da ragioni di comodo; un nu-
mero pu˜ essere scritto in una base qualsiasi senza che il suo intrinseco valore cambi perchŽ tale va-
lore • qualcosa che esiste realmente, indipendentemente dal sistema numerico usato. Per esempio,
un sacchetto di caramelle ne contiene una certa quantitˆ e questa quantitˆ • sempre la stessa, qua-
lunque sia il sistema numerico usato per esprimerla. Pi• in generale, possiamo dire che in un sistema
numerico posizionale in base b (con b > 1) un numero naturale pu˜ essere cos“ rappresentato:
Nb = an × bn + an−1 × bn−1 + ... + a1 × b + a0 × b0
Conveniamo di indicare sempre la base, se questa non • 10. Quindi i simboli N2, N5, N rappresen-
tano numeri in base 2, 5, 10, rispettivamente.

10
Unitˆ didattica 1 - Sistemi di numerazione

1.4 Sistema di numerazione binario


Molto interesse riveste, nellÕaritmetica dei calcolatori, il sistema a base due, o sistema binario, le cui
cifre sono:
A = {0, 1}
I numeri 110, 100 possono essere considerati numeri binari. Per distinguerli da quelli decimali • be-
ne contrassegnarli con un piccolo 2 in basso a destra; perci˜ i due numeri scritti precedentemente
vengono cos“ indicati:
1102, 1002
e si leggono rispettivamente:
Òuno, uno, zeroÓ; Òuno, zero, zeroÓ.

Un sistema di numerazione si dice binario quando la base • due.


Esso usa solo i due simboli, 0 e 1 per rappresentare tutti i numeri.
I simboli 0, 1 prendono il nome di bit (contrazione del termine inglese binary-digit, la
cui traduzione • cifra binaria).

Il sistema binario • il pi• semplice sistema di numerazione che si possa considerare, perchŽ • quello
che richiede il minor numero di simboli per esprimere graficamente tutti i numeri. Anche in questo
sistema il valore delle cifre dipende dalla posizione da esse occupata rispetto al punto radice.
Spostando una cifra di una posizione verso sinistra, si moltiplica il suo valore per 2, mentre spostan-
dola verso destra si divide il suo valore per 2. Quindi, anche i numeri binari si possono scrivere nella
forma polinomiale:
N2 = an × 2n + an−1 × 2n−1 + ... + a1 × 2 + a0 × 20 + a−1 × 2−1 + a−2 × 2−2 + ...

e la scrittura simbolica di N2 •:
N2 = anan−1 ... a1a0a−1a−2 ...

dove i coefficienti ak possono assumere solo i valori 0 oppure 1.


Per esempio, il numero binario:
11002 = 1 × 23 + 1 × 22 + 0 × 21 + 0 × 1 = 1210
ha le cifre ak che sono rispettivamente 1, 1, 0, 0.
I numeri 0, 1 del sistema decimale corrispondono rispettivamente alle cifre 0 e 1 del sistema binario.
Il numero 2 non ha un simbolo che lo rappresenti, nella forma binaria: in questo caso, poichŽ due
unitˆ del primo ordine formano una unitˆ del secondo ordine e zero del primo, si ha:
SISTEMA DECIMALE SISTEMA BINARIO
0 = 02
1 = 12
2 =1+1= 102
3 = 2 + 1 =102 + 12 = 112
4 = 1002
5 = 1012
6 = 1102
7 = 1112
8 = 10002
9 = 10012

11
Sezione 1 - Premesse

1.5 Conversione da base 2 a base 10


Per convertire un numero dalla base 2 alla base 10 si calcola il valore del polinomio
ordinato secondo le potenze del 2.

Esempi ................................................................................................................................
1. 1101112 = 1 × 25 + 1 × 24 + 0 × 23 + 1 × 22 + 1 × 2 + 1 = 55
per cui 110111 scritto in base 2 equivale a 55 scritto in base 10.
2. 101.112 = 1 × 22 + 0 × 2 + 1 + 1 × 2Ð1 + 1 × 2Ð2 = 4 + 1 + 1/2 + 1/4 = 5.75
per cui 101.11 scritto in base 2 equivale a 5.75 scritto in base 10.
...........................................................................................................................................

La conversione dalla base 10 alla base 2 si effettua nel seguente modo:


se il numero • naturale, si divide il numero per 2 e il resto rappresenta la cifra meno si-
gnificativa in binario; si divide poi per 2 il quoziente ottenuto e il resto rappresenta la
seconda cifra da destra e cos“ si procede fino ad avere quoziente zero. LÕultimo resto
ottenuto • la cifra pi• significativa del numero in scrittura binaria.

Esempio..............................................................................................................................
Convertire il numero 35 in binario.
35 : 2 = 17 resto 1 1
17 : 2 = 8 resto 1 1
8:2= 4 resto 0 0
4:2= 2 resto 0 0
2:2= 1 resto 0 0
1:2= 0 resto 1 1

Cifre binarie: 1 0 0 0 1 1
Pertanto il numero 35, in base 2 • 100011.
...........................................................................................................................................

Se il numero • frazionario, per la parte intera si procede come indicato nellÕesempio


precedente, mentre le cifre della parte non intera si ottengono nel modo seguente:
1. si moltiplica per 2 la parte frazionaria in base 10;
2. se il numero ottenuto • maggiore o uguale a 1, si sottrae 1 al risultato e si riporta
1 nelle cifre in base 2;
3. in caso contrario, si riporta 0 nelle cifre in base 2;
4. si ripetono le operazioni precedenti fino a ottenere un prodotto uguale a zero.

Esempio..............................................................................................................................
Convertire in base 2 il numero 0,625.
Cifre binarie
0,625 × 2 = 1,25 tolgo 1 1
0,25 × 2 = 0,5 0
0,5 × 2 = 1 tolgo 1 1
0 stop

12
Unitˆ didattica 1 - Sistemi di numerazione

Risulta quindi:
0,62510 = 0,1012
Controprova:
0,1012 = 2−1 + 0 + 2−3 = 1/2 + 1/8 = 5/8 = 0,62510
...........................................................................................................................................

1.6 Somma e sottrazione nel sistema binario


Le operazioni aritmetiche in base 2 (o in qualsiasi altra base) vengono eseguite seguendo i criteri di
calcolo giˆ noti per il sistema di numerazione in base 10. Vediamole singolarmente.
Somma
Per la somma tra due numeri, vale la regola del riporto: per ogni cifra si esegue la somma e, se si rag-
giunge o si supera la base, si addiziona il riporto alle cifre dellÕunitˆ superiore.
In pratica, per la somma di due cifre in base 2 vale la seguente tabella.

SOMMA RISULTATO
0+0=0 0
0+1=1+0 1
1 + 1 = 10 0 con riporto di 1

Una volta definita la somma di due numeri di una sola cifra, • facile eseguire lÕaddizione di due nu-
meri binari, tenendo conto della regola del riporto.
Esempio..............................................................................................................................
Eseguire la somma: 1101002 + 111012

Base 2 Base 10
Riporti 1111 1
110100 + 52 +
11101 = 29 =
1010001 81

...........................................................................................................................................

Per addizionare due o pi• numeri binari si addizionano i primi, al risultato si aggiunge il terzo e cos“ via.
Cos“ facendo, si evitano le complicazioni dei riporti.

Sottrazione
La sottrazione pu˜ essere eseguita con la regola del prestito.
Esempio..............................................................................................................................
Eseguire la sottrazione 1101012 Ð 101102

Base 2 Base 10
Prestiti 1111
110100 Ð 53 Ð
10110 = 22 =
11111 31

...........................................................................................................................................

13
Sezione 1 - Premesse

1.7 Sistema di numerazione esadecimale


Fino ad ora sono stati presentati i sistemi di numerazione con base 10 (sistema decimale) o con base
minore di 10, come il sistema binario. Ricordiamo che questÕultimo • il pi• semplice dei sistemi usati.
Si possono definire anche sistemi di numerazione con base maggiore di 10, come per esempio il siste-
ma esadecimale, o con base 16. Questo sistema usa sedici simboli, sei in pi• di quelli del sistema deci-
male: per la rappresentazione di un valore si possono utilizzare, oltre alle dieci cifre del sistema deci-
male, altri sei simboli, nella fattispecie le prime lettere dellÕalfabeto latino maiuscolo: A, B, C, D, E, F.
Quindi i sedici simboli della numerazione esadecimale sono:
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F
I primi dieci simboli coincidono con quelli del sistema decimale, mentre per gli altri sei si ha la se-
guente corrispondenza:

SISTEMA ESADECIMALE SISTEMA DECIMALE


A16 10
B16 11
C16 12
D16 13
E16 14
F16 15

Se si vuole rappresentare un numero maggiore o uguale a 16 in esadecimale, si ricorre alla rappre-


sentazione posizionale:
16 = 1016, 17 = 1116, 18 = 1216 ... 31 = 1F16, 32 = 2016 ecc. ...
La rappresentazione polinomiale secondo le potenze di 16 di un numero esadecimale •:
N16 = an × 16n + an−1 × 16n−1 + ... + a1 × 16 + a0 × 160 + a−1 × 16−1 + a−2 × 16−2 + ...
dove i coefficienti an possono assumere i valori: 0, 1, 2, ..., 8, 9, A, B, ..., F.
La scrittura simbolica di N16 •:
N = anan−1 É a1a0a−1a−2 É

Conversione da base 16 a base 10 e viceversa


Se un numero • in forma esadecimale, per trovare la sua notazione decimale • sufficiente scrivere il
numero in forma polinomiale.

Esempio..............................................................................................................................
Trasformare in base 10 il numero N16 = 2A.1116
2A.1116 = 2 × 161 + A × 160 + 1 × 16−1 + 1 × 16−2 =
= 2 × 16 + 10 × 1 + 1 × 1/16 + 1 × 1/256 =
= 32 + 10 + 0.0625 + 0.0039062
= 42.0664062
...........................................................................................................................................

Viceversa, per scrivere un numero decimale in base 16 si distinguono due casi.


1. N • un numero intero.
La sua forma esadecimale si ottiene con il metodo delle divisioni successive per 16.

14
Unitˆ didattica 1 - Sistemi di numerazione

Esempio..............................................................................................................................
Trasformare il numero 1970 in esadecimale.
1970 : 16 = 123 resto 2
123 : 16 = 7 resto 11 = B
7 : 16 = 0 resto 7

Cifre esadecimali: 7 B 2
Pertanto 1970 = 7B216.
...........................................................................................................................................
2. N • un numero frazionario.
In tal caso, procedendo come nella numerazione binaria, si converte la parte intera con il meto-
do appena visto e, per la parte non intera, si eseguono moltiplicazioni successive per 16: si toglie
di volta in volta la parte eccedente lÕunitˆ e la si trascrive come cifra della parte frazionaria.

Esempio..............................................................................................................................
Sia N = 18.6206

Cifre esadecimali
0.6206 × 16 = 9.9296 tolgo 9 9
0.9296 × 16 = 14.8736 tolgo 14 E (= 1410)
0.8736 × 16 = 13.9776 tolgo 13 D (= 1310)
0.9776 × 16 = 15.6416 tolgo 15 F (= 1510)
ecc.
Quindi 0.6206 = 0.9EDF... 16
Il calcolo • approssimato alla quarta cifra decimale.
...........................................................................................................................................

Addizione
LÕaddizione tra due numeri esadecimali viene eseguita tenendo conto della regola del riporto, cos“ co-
me in base 10 e in base 2. Quando si eseguono i calcoli in base 16, occorre ricordare che il riporto de-
ve essere calcolato quando si raggiunge o si supera 16, e non 10 (come ci suggerisce lÕabitudine).

Esempio..............................................................................................................................
Eseguire la somma 2A1C16 + 9E316

Base 16
Riporti 1
2A1C +
9E3 =
33FF

Le somme eseguite, a partire da destra, sono, in base 16:

C + 3 = F; E + 1 = F; A + 9 = 13 (scrivo 3, riporto 1); 2 + 1 = 3


...........................................................................................................................................

15
Sezione 1 - Premesse

Sottrazione
Come nel sistema binario, anche in quello esadecimale la sottrazione si esegue con la regola del prestito.

Esempio..............................................................................................................................
Eseguire la sottrazione 3C2E16 Ð 4A516

Base 16
Prestito 1
3C2E Ð
4A5 =
3789

Le operazioni eseguite, procedendo da destra verso sinistra, sono, in base 16:


E Ð 5 = 9; 2 Ð 9 = 8 (con il prestito di 1 da C); B Ð 4 = 7; 3 Ð 0 = 3
...........................................................................................................................................

1.8 Conversione da binario a esadecimale e viceversa


La numerazione binaria, a causa del numero elevato delle cifre che in generale costituiscono N2 e a causa
del monotono ripetersi di 0 e 1, • poco intelligibile allÕuomo e pu˜ portare facilmente a errori. Per ov-
viare a questi inconvenienti si pu˜ ricorrere al sistema esadecimale, perchŽ la scrittura di un numero in
tale sistema • pi• compatta e risulta facile la conversione di N16 verso il sistema binario e viceversa. Os-
servando la tabella seguente.

DECIMALE ESADECIMALE BINARIO


0 0 0000
1 1 0001
2 2 0010
3 3 0011
4 4 0100
5 5 0101
6 6 0110
7 7 0111
8 8 1000
9 9 1001
10 A 1010
11 B 1011
12 C 1100
13 D 1101
14 E 1110
15 F 1111

si vede che a ogni cifra esadecimale corrisponde una quadrupla di cifre binarie. Ne consegue quanto ri-
portato nella regola che segue.

Per passare da un numero in forma binaria, intero o non intero, alla corrispondente
forma esadecimale si suddivide il numero binario in gruppi di quattro bit, dal punto
decimale verso sinistra e dal punto radice verso destra, completando se necessario
lÕultimo gruppo di sinistra e lÕultimo di destra con degli zeri. Quindi, si sostituisce a ogni
gruppo di 4 bit la corrispondente cifra esadecimale, secondo quanto indicato dalla
tabella riportata sopra.

16
Unitˆ didattica 1 - Sistemi di numerazione

Esempio..............................................................................................................................
N2 = 101101110.10111 = 0001 0110 1110 . 1011 1000

{
{
{
{
{
1 6 E B 8
Quindi
101101110.101112 = 16E.B816
...........................................................................................................................................

Vale anche il viceversa.

Dato un numero in forma esadecimale, per ottenere la sua forma binaria si sostitui-
sce ogni cifra della forma esadecimale, con il corrispondente gruppo di quattro ci-
fre binarie determinato sulla base della tabella riportata sopra.

Esempio..............................................................................................................................
N16 = A3516 = 1010 0011 0101 = 1010001101012
{
{
{

A 3 5
...........................................................................................................................................

I numeri esadecimali vengono usati in informatica per rappresentare i dati memorizzati in un elaboratore
e per correggere errori, ma raramente sono usati per lÕinput e lÕoutput di dati. I numeri esadecimali han-
no il vantaggio che con due cifre rappresentano otto bit e, come vedremo pi• avanti, possono esprimere
in forma sintetica tutti i caratteri comunemente usati per trasmettere le informazioni.

17
Sezione 1 - Premesse

Esercizi
Unità didattica 1

1 Per ognuno dei seguenti numeri, indicare il valore posizionale delle cifre 3, 7, 4.
A 37584
B 4735
C 78043

2 Scrivere i seguenti numeri decimali nella rappresentazione polinominale secondo le potenze di 10.
A 7039
B 180041
C 731.03

3 Quale numero indica la rappresentazione polinominale: 5 × 102 + 3 × 10? In quale sistema di numera-
zione?

4 Quali delle seguenti uguaglianze sono vere e quali false?


A 807 = 8 × 10 + 1 × 10 + 7
B 807 = 8 × 102 + 7 × 10
C 807 = 8 × 102 + 7

5 Scrivere tutti i numeri binari formati da:


A due cifre
B tre cifre
C quattro cifre

6 Convertire il numero binario 11101 nel corrispondente numero decimale.

7 Convertire il numero decimale 827 nel corrispondente numero binario.

8 È vera l’eguaglianza: 179 = 101100112?

9 Eseguire le seguenti operazioni nel sistema binario.


A 11101 × 101
B 11101 : 101

q0 Eseguire le seguenti operazioni nel sistema binario.


A 1100111011 + 110101
B 1100111011 – 110101
C 110101 × 1011
D 110101 : 1011

qa Verificare che i tre numeri binari 1001, 111, 1101 abbiano per somma 11101.

qs Verificare nel sistema binario l’eguaglianza: ((11 + 10 + 101) × 11 – 101) /101 = 101.

qd Quale dei seguenti tre numeri esadecimali rappresenta il polinomio A × 162 + B × 16 + 5?


A 16A
B AB5
C AOB5

18
Unità didattica 1 - Sistemi di numerazione

Esercizi
Unità didattica 1

qf Calcolare il numero in base 10 corrispondente a A2516.

qg Convertire il numero decimale 179 nel corrispondente numero esadecimale.

qh Convertire il numero esadecimale 33B16 nel corrispondente numero decimale.

qj Eseguire le seguenti operazioni nel sistema esadecimale.


A 4A7 + BC5
B ABC – 2A
C 46B + 2A

qk Verificare che 36A16 sia la somma dei numeri 33516 e 3516.

ql Convertire i seguenti numeri binari in esadecimali e calcolare il loro valore in decimale.


A 110100101
B 1110011101
C 1000110110

w0 Verificare che il numero decimale 53 sia rappresentato da 110101 nella scrittura binaria e 35 nella scrit-
tura esadecimale.

wa Convertire direttamente i seguenti numeri binari in numeri esadecimali.


A 101111
B 11101
C 1011.1
D 11.101

ws Convertire direttamente i seguenti numeri esadecimali in numeri binari.


A A45
B 5C
C 63D

wd Indicare quali dei seguenti numeri sono pari e quali dispari.


A 1102
B 11002
C 100112
D 101012

19
Unitˆ didattica
2
Codifica
delle informazioni
CHE COSA IMPARERAI A FARE

$ Applicare un codice univoco a un insieme di oggetti


$ Codificare i caratteri che compongono le informazioni
$ Utilizzare il criterio di rappresentazione dei numeri interi
allÕinterno di un elaboratore
$ Eseguire sottrazioni con il metodo del complemento a 2
$ Rappresentare i numeri reali in notazione esponenziale

CHE COSA DOVRAI STUDIARE

$ Caratteristiche dei codici ASCII e UNICODE


$ Procedura per la rappresentazione binaria
dei numeri interi
$ Definizione del complemento a 2
$ Notazione in virgola mobile
$ Mantissa e caratteristica di un numero reale
Unità didattica
Codifica delle informazioni
2
2.1 Introduzione
Nel linguaggio naturale, le informazioni possono essere:

: numeriche, se rappresentate da numeri;


: alfabetiche, se rappresentate da lettere;
: alfanumeriche, se rappresentate da numeri, lettere o caratteri speciali.

Di seguito si riportano alcuni esempi:

: informazioni numeriche: 375; 10111; –32.46;


: informazioni alfabetiche: PREZZO; ALTEZZA; libro;
: informazioni alfanumeriche: MN32756; INT2; COLOR5.

Le informazioni devono essere elaborate e, a seconda del tipo di elaborazione cui


vanno soggette e dei mezzi usati per tale trattamento, si presenta la necessità di dar
loro una rappresentazione simbolica idonea, che si chiama codifica, mentre il siste-
ma dei simboli usati si chiama codice.

Si possono citare, come esempi di codifica, la classificazione dei libri di una biblioteca, l’attribuzione
di un codice alla merce in vendita in un magazzino, l’immatricolazione di autovetture, ecc.
Quando le informazioni sono trattate in modo automatico, è necessario che la loro rappresentazione
simbolica sia intelligibile alla macchina. Per esempio, dovendo trattare informazioni numeriche con
macchine aventi dispositivi di calcolo adatti al sistema decimale, come è il caso delle macchine calco-
latrici e di quelle contabili, le informazioni devono essere presentate in forma numerica decimale e,
quindi, il codice usato è il codice numerico decimale.
Quando le informazioni devono essere trattate dai calcolatori elettronici, poiché i congegni che li
compongono possono esistere solo sotto due stati (passaggio o interruzione di corrente, oppure ma-
gnetizzazione positiva o negativa, tensione positiva o negativa), è evidente che le informazioni de-
vono essere rappresentate da un linguaggio binario il cui alfabeto è riportato di seguito.
A = {0, 1}
Sappiamo che le cifre binarie sono gli elementi fondamentali della tecnica dei calcolatori elettro-
nici e prendono il nome di bit. Ogni “carattere” e ogni “parola” devono essere ridotti a una suc-
cessione di bit, cioè di zero e uno, perché il calcolatore possa riconoscerli, elaborarli e fornire del-
le risposte.
I bit 0 e 1, presi due a due, possono dare quattro diverse configurazioni: 00; 01; 10; 11; quindi tan-
te quante sono le disposizioni con ripetizione di due oggetti presi due a due, ovvero 22 = 4.
Se i simboli binari sono presi quattro a quattro si ottengono 24 = 16 disposizioni con ripetizione e,
quindi, 16 configurazioni diverse.

In generale, i due bit 0 e 1, presi n ad n, danno 2n configurazioni diverse.

21
Sezione 1 - Premesse

Per costruire un codice che rappresenti in forma binaria ogni carattere del linguaggio naturale, è
necessario stabilire una corrispondenza biunivoca tra ciascun carattere e una particolare configura-
zione binaria e, per convenzione, stabilire che tale configurazione rappresenti sempre quel carattere.

Si definisce codice del calcolatore l’insieme delle configurazioni binarie usate dalla
macchina per rappresentare i caratteri. L’insieme dei caratteri che possono essere
codificati è chiamato repertorio o set di caratteri del calcolatore.

I codici adottati per la rappresentazione in forma binaria delle informazioni numeriche e alfanume-
riche possono variare in funzione delle tecniche costruttive del calcolatore.
Quindi ogni tipo di calcolatore ha un proprio codice, detto codice macchina.
Nei paragrafi che seguono verranno illustrati brevemente alcuni di tali codici.
È bene, tuttavia, precisare che la codifica è un problema interno del calcolatore, che non riguarda di-
rettamente l’operatore umano.
I caratteri, sia numerici sia letterali, sono immessi ed estratti nella loro forma consueta.

2.2 Rappresentazione dei caratteri


Per la codifica dei caratteri numerici, alfabetici e speciali (per esempio, i segni di interpunzione) si so-
no costruiti vari codici: tra tutti, presentiamo quelli di uso più comune.

Codice ASCII
Ogni carattere viene rappresentato con otto bit e, quindi, questo codice può rappresentare 28 = 256
caratteri.

Un gruppo di 8 bit prende il nome di byte.

Con un byte si possono rappresentare:


: una lettera dell’alfabeto;
: una cifra numerica;
: un segno speciale.

Per rappresentare cifre, lettere e altri caratteri, come i segni di interpunzione, viene usato un codice
a 7 posizioni: il codice ASCII (American Standard Code for Information Interchange).
Numero Codice Binario Esadecimale Numero Codice Binario Esadecimale
d’ordine ASCII d’ordine ASCII
0 nul 0000000 00 64 @ 1000000 40
1 soh 0000001 01 65 A 1000001 41
2 stx 0000010 02 66 B 1000010 42
3 etx 0000011 03 67 C 1000011 43
4 eot 0000100 04 68 D 1000100 44
5 enq 0000101 05 69 E 1000101 45
6 ack 0000110 06 70 F 1000110 46
7 bel 0000111 07 71 G 1000111 47
8 bs 0001000 08 72 H 1001000 48
9 ht 0001001 09 73 I 1001001 49
10 lf 0001010 0A 74 J 1001010 4A

22
Unitˆ didattica 2 - Codifica delle informazioni

Numero Codice Binario Esadecimale Numero Codice Binario Esadecimale


dÕordine ASCII dÕordine ASCII
11 vt 0001011 0B 75 K 1001011 4B
12 ff 0001100 0C 76 L 1001100 4C
13 cr 0001101 0D 77 M 1001101 4D
14 so 0001110 0E 78 N 1001110 4E
15 si 0001111 0F 79 O 1001111 4F
16 dle 0010000 10 80 P 1010000 50
17 dcl 0010001 11 81 Q 1010001 51
18 dc2 0010010 12 82 R 1010010 52
19 dc3 0010011 13 83 S 1010011 53
20 dc4 0010100 14 84 T 1010100 54
21 nak 0010101 15 85 U 1010101 55
22 syn 0010110 16 86 V 1010110 56
23 etb 0010111 17 87 W 1010111 57
24 can 0011000 18 88 X 1011000 58
25 em 0011001 19 89 Y 1011001 59
26 sub 0011010 1A 90 Z 1011010 5A
27 esc 0011011 1B 91 [ 1011011 5B
28 fs 0011100 1C 92 \ 1011100 5C
29 gs 0011101 1D 93 ] 1011101 5D
30 rs 0011110 1E 94 ^ 1011110 5E
31 us 0011111 1F 95 _ 1011111 5F
32 spazio 0100000 20 96 Ô 1100000 60
33 ! 0100001 21 97 a 1100001 61
34 Ò 0100010 22 98 b 1100010 62
35 # 0100011 23 99 c 1100011 63
36 $ 0100100 24 100 d 1100100 64
37 % 0100101 25 101 e 1100101 65
38 & 0100110 26 102 f 1100110 66
39 Ô 0100111 27 103 g 1100111 67
40 ( 0101000 28 104 h 1101000 68
41 ) 0101001 29 105 i 1101001 69
42 * 0101010 2A 106 j 1101010 6A
43 + 0101011 2B 107 k 1101011 6B
44 Ô 0101100 2C 108 l 1101100 6C
45 - 0101101 2D 109 m 1101101 6D
46 . 0101110 2E 110 n 1101110 6E
47 / 0101111 2F 111 o 1101111 6F
48 0 0110000 30 112 p 1110000 70
49 1 0110001 31 113 q 1110001 71
50 2 0110010 32 114 r 1110010 72
51 3 0110011 33 115 s 1110011 73
52 4 0110100 34 116 t 1110100 74
53 5 0110101 35 117 u 1110101 75

23
Sezione 1 - Premesse

Numero Codice Binario Esadecimale Numero Codice Binario Esadecimale


dÕordine ASCII dÕordine ASCII
54 6 0110110 36 118 v 1110110 76
55 7 0110111 37 119 w 1110111 77
56 8 0111000 38 120 x 1111000 78
57 9 0111001 39 121 y 1111001 79
58 : 0111010 3A 122 z 1111010 7A
59 ; 0111011 3B 123 { 1111011 7B
60 < 0111100 3C 124 | 1111100 7C
61 = 0111101 3D 125 } 1111101 7D
62 > 0111110 3E 126 ~ 1111110 7E
63 ? 0111111 3F 127 1111111 7F

I caratteri stampabili, quelli compresi dal 32 al 126, rappresentano tutti i simboli di cui un computer
pu˜ aver bisogno per le proprie elaborazioni: lettere, segni dÕinterpunzione, simboli matematici, ca-
ratteri numerici e cos“ via.
I caratteri di controllo (quelli da 0 a 31 e il 127) sono caratteri non stampabili, che vengono impie-
gati per segnalare condizioni particolari, per esempio eot per individuare la fine di un testo o ack
per indicare il riconoscimento di un segnale durante una trasmissione dati.
I numeri che figurano nella tabella (i codici dal 48 al 57) non vanno confusi con i valori numerici pro-
priamente detti, che abbiamo studiato in precedenza: in questo caso si tratta semplicemente dei sim-
boli che servono a rappresentare le cifre.

La codifica ASCII • realizzata con 7 bit, ma ciascun carattere occupa, in ogni caso, un byte (8 bit).

Per allargare lÕinsieme dei caratteri rappresentabili, includendo anche le vocali accentate e i segni
diacritici tipici delle lingue latine, ai 128 simboli del codice ASCII a 7 bit sono stati aggiunti altri
128 caratteri. Il codice ASCII a 8 bit che permette di rappresentare 256 caratteri prende il nome di
Codice ASCII esteso.

Codice UNICODE
A differenza delle lingue anglosassoni, come inglese e tedesco, e neolatine, come italiano, francese e
spagnolo, esistono molti linguaggi che non usano lÕalfabeto latino: greco e cirillico ne sono un tipi-
co esempio. Per la loro rappresentazione si ricorre a speciali codici a 8 bit, che descrivono insiemi spe-
cifici di caratteri. Il computer di un utente russo, per esempio, deve poter rappresentare un insieme
di caratteri ben diverso da quello di un utente italiano. Oltre ai vari tipi di codici a 8 bit esistono an-
che codici a 16 bit, che vengono utilizzati per linguaggi ancora pi• complessi come quelli dei paesi
asiatici. Vista la varietˆ delle codifiche, si • cercato uno standard di rappresentazione che comprenda
universalmente tutti i caratteri possibili: nasce con questo scopo il codice UNICODE, che con i suoi
2 byte di rappresentazione consente di descrivere ben 65.536 caratteri diversi.

2.3 Rappresentazione interna dei numeri interi


La rappresentazione dei numeri, cifra per cifra, mediante i sistemi di codifica presentati finora non •
la pi• conveniente, poichŽ non tiene affatto conto della natura dei numeri (naturali, interi, razionali
ecc.) e non permette di eseguire le operazioni aritmetiche.
é, quindi, necessario avvalersi di altri sistemi di codifica sia per rappresentare i numeri interi (negati-
vi e positivi) sia per rappresentare i numeri reali, quando su di essi si devono eseguire calcoli.
Il calcolatore considera i numeri naturali come numeri interi positivi.

24
Unità didattica 2 - Codifica delle informazioni

Le informazioni vengono memorizzate all’interno del calcolatore secondo configu-


razioni binarie, ognuna delle quali è formata dallo stesso numero di bit ed è detta
parola.

La lunghezza della parola dipende dal codice interno del calcolatore e può essere di 8, 16, 32, 64 bit.
Naturalmente, un numero intero non può essere rappresentato da un numero di bit maggiore di
quello della parola del calcolatore, pertanto l’insieme degli interi memorizzabili all’interno della
macchina è un sottoinsieme dell’insieme degli interi. Se n è il numero di bit contenuti in una parola
di un calcolatore, tenuto conto che il primo bit a sinistra rappresenta il segno del numero, i numeri
interi rappresentabili sono compresi tra:
− (2n−1 − 1) e 2n−1 − 1
Per esempio, con parole di 8 bit si possono rappresentare gli interi compresi tra:
−27 − 1 e 27 − 1
Il fatto di operare su un insieme limitato degli interi comporta notevoli conseguenze nell’aritmetica
dei calcolatori. Infatti è indispensabile che i risultati delle operazioni, in fase di esecuzione, appar-
tengano all’intervallo −2n − 1 e 2n − 1, altrimenti si determina una situazione di traboccamento o, in
inglese, di overflow. In questo caso la macchina, in generale, opera un troncamento sul risultato op-
pure interrompe l’esecuzione, segnalando con un messaggio l’errore.
Per esempio, nel caso di n = 8, la semplice espressione:
(90 + 40) + (−20)
risulta uguale a:
130 + (−20)
e, poiché 130 > 27, si dice che “si va in overflow”.
Anche se i calcolatori moderni permettono di operare su un sottoinsieme molto ampio degli interi,
per cui l’overflow si presenta raramente, è buona norma di sicurezza prevedere questi casi di traboc-
camento, soprattutto per quelle operazioni come la moltiplicazione e la potenza, che portano con
più velocità di altre operazioni a valori molto elevati o molto piccoli. Naturalmente, i limiti entro cui
si opera dipendono, come già detto, dal tipo di calcolatore usato.

Rappresentazione in complemento a 2
Viene ora presentato il metodo universalmente riconosciuto come il più valido per la rappresen-
tazione dei numeri interi relativi. La sua conoscenza permette di spiegare alcuni casi di errore di
calcolo imprevisti, dovuti alla mancata segnalazione degli eventi di overflow da parte dell’elabo-
ratore.

La rappresentazione in complemento a 2 di un numero intero x, per parole di n bit è


così definita:
: se il numero x è positivo, allora il primo bit a sinistra è impostato a zero e gli al-
tri n – 1 bit disponibili sono utilizzati per la rappresentazione binaria del nu-
mero x dato;
: se il numero x è negativo, il primo bit a sinistra è 1 mentre il modulo è la rappre-
sentazione binaria di:
2n–1 – |x|
L’espressione precedente prende il nome di complemento a 2 di x.

25
Sezione 1 - Premesse

Esempio..............................................................................................................................
Per la rappresentazione di x = –12 con parole di 8 bit, il modulo da scrivere in binario è:
27 − 12 = 128 − 12 = 116 = 1110 1002
quindi, la rappresentazione su 8 bit di x = −12 è:
1 1 1 1 0 1 0 0

bit del segno
...........................................................................................................................................

Con la rappresentazione in complemento a 2:


: lo zero ha la sola rappresentazione 000 ... 00 (n zeri), quindi la sua codifica è univoca;
: il maggior numero rappresentabile, con parole di n bit, è 2n–1 – 1;
: i numeri negativi rappresentabili sono compresi tra –2n–1 e –1, le cui rappresentazioni in
complemento a 2 sono rispettivamente:
100 ... 00 e 111 ... 11

In base a quanto detto, usando parole di n bit sono rappresentabili in complemento a due tutti i nu-
meri compresi nell’intervallo:
−2n−1 ; 2n−1 − 1
Se nella rappresentazione di un numero negativo cambiamo gli 1 in 0 e gli 0 in 1, si ottiene il valore as-
soluto del numero diminuito di 1.

Esempio..............................................................................................................................
Dall’esempio precedente si ha che la rappresentazione di –12 è 11110100; cambiando i
bit, come detto, si ha 00001011, che rappresenta il numero decimale 11, che è il valore
assoluto di –12 diminuito di 1.
...........................................................................................................................................

Tenendo conto di questa considerazione, è possibile ricorrere alla procedura seguente per calcolare
in modo rapido la rappresentazione in complemento a 2 di un numero negativo.

Per rappresentare in complemento a 2 un numero negativo x:


1. si trova la rappresentazione in valore assoluto e segno dell’opposto di x (cioè –x);
2. si cambiano gli 0 in 1 e gli 1 in 0;
3. si somma 1 al risultato.

Esempio..............................................................................................................................
Rappresentare in complemento a 2 il numero x = –18, su otto bit.
+18 è uguale a 0 0 0 1 0 0 1 0
Scambio gli 0 con 1 e gli 1 con 0 1 1 1 0 1 1 0 1
Aggiungo 1 + 1
–18 è uguale a 1 1 1 0 1 1 1 0











bit del segno 27 – 18 = 110
...........................................................................................................................................

26
Unitˆ didattica 2 - Codifica delle informazioni

Quando si utilizza il criterio del complemento a 2 per rappresentare i numeri negativi, • indispensabile
specificare con chiarezza il numero di bit disponibile per le operazioni aritmetiche di somma o sottrazio-
ne. AllÕinterno di un elaboratore, tale numero • definito dalla lunghezza massima della parola (ovvero del
numero di byte) con cui lÕelaboratore opera. Il numero di bit utilizzati permette di conoscere la posizione
del segno e di controllare eventuali overflow.

Esempio..............................................................................................................................
Supponendo di operare con parole di un byte (cio• con 8 bit), eseguire le seguenti operazioni:
a) 15 − 12;
b) −15 − 12;
c) 100 + 30.
a)
a) 1 5 Ð 1 2

1 5 = 0 0 0 0 1 1 1 12 Gli zeri sulla sinistra non sono significativi,


ma ricordano che i bit sono 8
1 2 = 0 0 0 0 1 1 0 02 Per rappresentare Ð12 si utilizza il complemento a 2

0 0 0 0 1 1 0 0 scambio 0 con 1
1 1 1 1 0 0 1 1 sommo 1
1 1 1 1 0 1 0 0
Ð 1 2 = 1 1 1 1 0 1 0 02

Quindi

Base 10 Base 12
+1 5 + 0 0 0 0 1 1 1 1 +
Ð1 2 = 1 1 1 1 0 1 0 0 =
+3 1 0 0 0 0 0 0 1 1 =+3

Riporto da troncare
b)
b) Ð1 5 Ð 1 2

1 5 = 0 0 0 0 1 1 1 12 Per rappresentare Ð15 si utilizza il complemento a 2


Ð 1 5 = 1 1 1 1 0 0 0 12

1 2 = 0 0 0 0 1 1 0 02 Per rappresentare Ð12 si utilizza il complemento a 2


Ð 1 2 = 1 1 1 1 0 1 0 02

Quindi
Base 10 Base 2
Ð1 5 + 1 1 1 1 0 0 0 1 +
Ð1 2 = 1 1 1 1 0 1 0 0 =
Ð2 7 1 1 1 1 0 0 1 0 1 = complemento a 2 di 27

Riporto da troncare

27
Sezione 1 - Premesse

Verifica

Ð 2 7 = 1 1 1 0 0 1 0 12 complemento a 2

1 1 1 0 0 1 0 1 scambio 0 con 1
0 0 0 1 1 0 1 0 sommo 1
0 0 0 1 1 0 1 1
+ 2 7 = 0 0 0 1 1 0 1 12 ⇑

c)
c) 1 0 0 + 30

PoichŽ i bit disponibili sono 8, lÕintervallo dei numeri interi rappresentabili •


Ð27; 27 Ð 1 cio• Ð128; 127.
La somma 100 + 30 = 130 eccede tale intervallo e il risultato dellÕoperazione •
completamente errato. Infatti
Base 10 Base 2
1 0 0 + 0 1 1 0 0 1 0 0 +
3 0 = 0 0 0 1 1 1 1 0 =
1 3 0 1 0 0 0 0 0 1 0 = Ð1 2 6

Bit del segno
Verifica

1 2 6 = 0 1 1 1 1 1 1 02 complemento a 2

0 1 1 1 1 1 1 0 scambio 0 con 1
1 0 0 0 0 0 0 1 sommo 1
1 0 0 0 0 0 1 0
Ð 1 2 6 = 1 0 0 0 0 0 1 02 ⇑

...........................................................................................................................................

2.4 Rappresentazione dei numeri reali


Mentre per i numeri interi le operazioni aritmetiche eseguite da un calcolatore danno risultati esat-
ti, purchŽ gli operandi e i risultati appartengano allÕintervallo (MININT, MAXINT), dove con
MININT e MAXINT intendiamo indicare il minore e il maggiore degli interi rappresentabili in un
calcolatore, lÕesattezza dei risultati non • pi• possibile quando si opera nellÕinsieme dei reali. In-
fatti, per lÕinsieme dei reali • possibile memorizzare solo valori compresi tra un valore minimo
(MINREAL) e un valore massimo (MAXREAL). Inoltre, della parte non intera di un numero rea-
le • possibile memorizzare solo un numero limitato di cifre, la cui entitˆ dipende dal tipo di mac-
china usata: se un numero reale ha un numero di cifre decimali che supera tale limite subisce un
troncamento, quindi del numero viene memorizzato solo un valore approssimato. Infine, lÕinsie-
me dei numeri reali • continuo e qualsiasi intervallo, per quanto piccolo, di numeri reali ne con-
tiene infiniti.
Il tipo reale dellÕaritmetica dei calcolatori contiene, invece, un insieme discreto di valori e ogni valo-
re, come giˆ detto, • costituito da un numero finito di cifre decimali: per esempio, in un calcolatore

28
Unità didattica 2 - Codifica delle informazioni

la cui aritmetica ammetta solo quattro cifre decimali il numero reale EF 2 sarà rappresentato dal deci-
male finito 1.4142. Ogni decimale finito rappresenta inoltre un sottoinsieme di numeri reali: per
esempio, 1.4142 è la rappresentazione di tutti i reali, razionali o irrazionali, le cui prime quattro ci-
fre frazionarie sono 4, 1, 4, 2 (1.41427, 1.4142333..., 1.41424141..., sono esempi di elementi del-
l’insieme rappresentato dal decimale finito 1.4142). Da quanto esposto si può facilmente compren-
dere che sorgono notevoli problemi, sia di rappresentazione dei numeri reali sia di approssimazione,
quando si tratta dell’aritmetica dei calcolatori.
In molti problemi, soprattutto di carattere scientifico, vengono spesso trattati numeri molto piccoli
o molto grandi, con numerosi zeri, per cui è necessario rappresentare i numeri in modo da evitare la
scrittura ripetuta di molti zeri. In questi casi viene adottato un particolare metodo, detto della vir-
gola mobile (floating point, in inglese), che consente di ridurre notevolmente l’impiego di posizioni
di memoria fisiche del calcolatore.

Con il metodo della virgola mobile i numeri vengono scritti in forma esponenziale, in
modo che la parte intera sia sempre zero e la prima cifra frazionaria sia diversa da ze-
ro: ciò si ottiene moltiplicando o dividendo il numero per un’opportuna potenza di
dieci.

I numeri +32.415; −2130000; +0.0000003715 vengono rappresentati in virgola mobile come se-
gue:

2
+3 2 . 4 1 5 = 0 . 3 2 4 1 5 * 1 0
7
–2 1 3 0 0 0 0 = –0 . 2 1 3 * 1 0
–6
+0 . 0 0 0 0 0 0 3 7 1 5 = +0 . 3 7 1 5 * 1 0

Nella scrittura esponenziale la parte decimale viene detta mantissa e l’esponente


della potenza di 10 è detto caratteristica.

Per esempio, nel numero +32.415 = +0.32415 * 102 il valore 32415 è la mantissa, mentre l’espo-
nente 2 di 10 è la caratteristica.
La scrittura in virgola mobile di un numero viene anche detta forma normalizzata del numero.
In memoria centrale il numero, scritto in forma normalizzata, viene rappresentato secondo conven-
zioni particolari, che possono variare da calcolatore a calcolatore, ma che tendono tutte a rendere mi-
nimo il numero di posizioni che il numero deve occupare in memoria.

Un modo di rappresentazione può essere il seguente:


1. non considerare lo zero della parte intera (che non viene memorizzato) e fissare
un numero costante di cifre per la mantissa;
2. stabilire che il campo di variabilità della caratteristica sia fisso (per esempio –50,
+49: aggiungendo alla caratteristica +50, quest’ultima sarà sempre positiva e non
servirà memorizzare il segno della caratteristica);
3. memorizzare le tre parti rimaste (segno, caratteristica e mantissa) nel seguente
ordine:
segno caratteristica mantissa

I numeri di esempio citati sopra vengono così rappresentati in memoria.

29
Sezione 1 - Premesse

Numero Virgola mobile In memoria


2
+3 2 . 4 1 5 +0 . .3 2 4 1 5 * 1 0 +5 2 3 2 4 1 5
7
–2 1 3 0 0 0 0 +0 . 2 1 3 * 10 –5 7 2 1 3 0 0
+0 . 0 0 0 0 0 . 3 7 1 5 +0 . 3 7 1 5 * 1 0 –6 +4 4 3 7 1 5 0

Da questi esempi si può comprendere come questo tipo di rappresentazione dei numeri in memoria
riduca notevolmente il numero di posizioni occupate, soprattutto quando i numeri contengono
molti zeri.
Esistono rappresentazioni in virgola mobile in cui sia la mantissa sia la caratteristica sono espresse in
esadecimale e la caratteristica viene aumentata di 64: in tal modo, l’esponente convenzionale varia da
0 a 127 e quello effettivo da –64 a +63.
Da quanto esposto si può concludere che l’aritmetica dei dati reali è un’aritmetica finita e discreta e
l’insieme su cui opera è un piccolissimo sottoinsieme dei numeri reali.
In questa esposizione ci siamo avvalsi della scrittura decimale ma, naturalmente, all’interno del cal-
colatore i numeri vengono espressi in forma binaria secondo le potenze di 2.
Una rappresentazione che utilizza quattro byte potrebbe essere così strutturata:

1o byte 2o byte 3o byte 4o byte

|±| caratteristica | mantissa |


dove:

: il primo bit di sinistra rappresenta il segno della mantissa;


: i restanti sette bit la caratteristica, o esponente;
: gli altri 24 bit servono per rappresentare la mantissa.

Poiché il massimo numero rappresentabile con 7 bit è 127, l’esponente convenzionale varia da 0 a
127 mentre l’esponente effettivo, che si ottiene togliendo 64, varia da –64 a 63.
Supposto che l’esponente convenzionale sia 10001002, che in decimale corrisponde al numero 68,
l’esponente effettivo sarà 68 – 64 = 4.

30
Unità didattica 2 - Codifica delle informazioni

Esercizi
Unità didattica 2

1 Rappresentare tutte le configurazioni possibili che si possono ottenere con 4 bit.

2 Quanti bit sono necessari per codificare 20 oggetti diversi?

3 Quanti bit sono necessari per codificare i caratteri con cui vengono rappresentate le informazioni?

4 Nella tabella del codice ASCII un bit resta sempre uguale a 0. Quale? Perché?

5 Rappresentare la codifica ASCII binaria ed esadecimale della parola “casa”.

6 Qual è il numero d’ordine del carattere ASCII “lf”.

7 Completare la tabella seguente:

NUMERO D’ORDINE CARATTERE BINARIO ESADECIMALE


$ 0100100 24
38 0100110 26
41 29
43 +

8 Quanti bit utilizza il codice UNICODE?

9 Dopo aver trasformato gli operandi in base 2, eseguire l’operazione 33 + 71 utilizzando il criterio
del complemento a 2 avendo a disposizione 8 bit.

q0 Dopo aver trasformato gli operandi in base 2, eseguire l’operazione 24 + 62 utilizzando il criterio
del complemento a 2 avendo a disposizione 8 bit.

qa Dopo aver trasformato gli operandi in base 2, eseguire l’operazione 71 – 33 utilizzando il criterio
del complemento a 2 avendo a disposizione 8 bit.

qs Dopo aver trasformato gli operandi in base 2, eseguire l’operazione 62 – 24 utilizzando il criterio
del complemento a 2 avendo a disposizione 8 bit.

qd Dopo aver trasformato gli operandi in base 2, eseguire l’operazione 33 – 71 utilizzando il criterio
del complemento a 2 avendo a disposizione 8 bit.

qf Dopo aver trasformato gli operandi in base 2, eseguire l’operazione 33 – 34 utilizzando il criterio
del complemento a 2 avendo a disposizione 8 bit.

qg Dopo aver trasformato gli operandi in base 2, eseguire l’operazione 133 – 71 utilizzando il criterio
del complemento a 2 avendo a disposizione 8 bit. Dire perché il risultato è da ritenersi errato.

qh Dopo aver trasformato gli operandi in base 2, eseguire l’operazione 64 + 64 utilizzando il criterio
del complemento a 2 avendo a disposizione 8 bit. Dire perché il risultato è da ritenersi errato.

31
Sezione 1 - Premesse

Esercizi
Unità didattica 2

qj Scrivere in base 2 tutti i numeri che possono essere rappresentati con 4 bit quando si utilizza il metodo
del complemento a 2.

qk Scrivere in forma esponenziale i seguenti numeri:


A 0.00012345
B 0.6789
C 1.234
D 5678

ql Per i numeri dell’esercizio precedente indicare la mantissa e la caratteristica.

w0 Scrivere in forma esponenziale i seguenti numeri:


A 0.000005
B 0.00005
C 0.0005
D 0.005
E 0.05
F 0.5

wa Scrivere in forma esponenziale i seguenti numeri:


A 300000
B 30000
C 3000
D 300
E 30
F 3

ws Usando 4 byte rappresentare in binario e in virgola mobile il numero 12.34.

32
Sezione 2
Primi elementi
di programmazione

Obiettivi ◊ Acquisire familiaritˆ con la struttura di un programma
C++
◊ Apprendere i primi elementi del linguaggio
◊ Scrivere, compilare ed eseguire brevi programmi
in C++

& Questa sezione contiene


U.D. 3
U.D. 4
Introduzione a C++
Visualizzazione e acquisizione
U.D. 5 Operatori
Unitˆ didattica
3
Introduzione a C++

CHE COSA IMPARERAI A FARE

$ Riconoscere gli elementi fondamentali di un programma C++


$ Compilare ed eseguire un semplice programma
$ Scegliere i tipi di dato
$ Distinguere tra variabili e costanti

CHE COSA DOVRAI STUDIARE

$ Compilazione ed esecuzione di un programma


$ Definizioni dei tipi di dato
$ Operazione di cast
$ Definizione di una costante
Unità didattica 3
Introduzione a C++

3.1 Primo programma


Prima di entrare nel vivo della programmazione C++ è opportuno prendere un po’ confidenza con
questo linguaggio, scrivendo un piccolissimo programma che ormai fa parte della storia della teoria
della programmazione, a prescindere dal linguaggio che si vuole studiare: si tratta, naturalmente, del
famoso “Ciao mondo”.

Esempio Ciao .....................................................................................................................


Scrivere a video il messaggio di saluto “Ciao, mondo”.
Di seguito è riportato il codice del programma.

Codice
Ciao.cpp
1 #include <iostream>
2 using namespace std;
3 //inizio
4 int main ()
5 {
6 //scrivi il messaggio di saluto
7 cout << “Ciao, mondo” << endl;
8 //arresta l’esecuzione del programma
9 system(pause);
10 //termina il programma
11 return 0;
12 }
...........................................................................................................................................

step by step 쩚 쩚 쩚 쩚 쩚 쩚 쩚 쩚 쩚 쩚 쩚 쩚 쩚 쩚 쩚 쩚 쩚 쩚 쩚 쩚 쩚 쩚 쩚 쩚 쩚 쩚 쩚

1. Copiare il seguente codice, senza i numeri di riga, in Blocco Note.


2. Nominare il file ciao.cpp.

È importante ricordare che si tratta di un file che contiene codice C++, che deve avere l’estensione .cpp.

3. Procedere alla compilazione del programma, creando il file eseguibile Ciao.exe.


4. Provare a eseguire il programma.
쩚쩚쩚쩚쩚쩚쩚쩚쩚쩚쩚쩚쩚쩚쩚쩚쩚쩚쩚쩚쩚쩚쩚쩚쩚쩚쩚쩚쩚쩚쩚
La prima cosa che serve per poter cominciare a creare un programma in C++ è un editor di testi.

Un editor di testi è un software che permette al programmatore di gestire (cioè crea-


re, modificare e salvare) un file contenente caratteri alfanumerici.

35
Sezione 2 - Primi elementi di programmazione

Per programmi scritti in C++ • sufficiente un editor di base come Blocco Note di Windows. LÕedi-
tor di testi permette di scrivere un programma, cio• un insieme finito di istruzioni composte da un
limitato numero di parole chiave relative alla grammatica di un determinato linguaggio di pro-
grammazione.

Il file che contiene il testo del programma viene chiamato file sorgente, o program-
ma sorgente, ed • un semplice file di testo che viene letto dal compilatore; questÕulti-
mo provvede alla codifica delle istruzioni scritte dal programmatore in istruzioni di-
rettamente eseguibili dal processore.

I file sorgente, perlomeno quelli di programmi di una certa dimensione, sono in genere pi• di uno e
tra essi rivestono particolare importanza i file chiamati librerie (anche detti file dÕintestazione), che
tipicamente contengono insiemi di funzioni, procedure e classi utilizzati dal programma principale.
Il meccanismo di inclusione dei file dÕintestazione permette di realizzare programmi indipendenti
dallÕhardware e dal software di sistema, cio• dal compilatore e dal sistema operativo in uso sul com-
puter sul quale il programma viene eseguito: infatti, basterˆ creare librerie di sottoprogrammi conte-
nenti tutte le funzioni dipendenti dal tipo di elaboratore, che saranno richiamate nel codice sorgen-
te del programma tramite il loro file dÕintestazione.
AffinchŽ il programma sia completo deve essere collegato (mediante un apposito software, detto
linker) con altri codici oggetto. I sottoprogrammi utilizzati dal linker per effettuare il collegamento
sono moduli che possono essere forniti dal sistema operativo, scritti dal programmatore e/o forniti
insieme al compilatore. La compilazione e il linking di un codice sorgente producono il file esegui-
bile (di estensione .exe), il quale viene memorizzato su unÕapposita area di memoria di massa atta a
contenere il codice eseguibile.

Un file eseguibile racchiude al suo interno una serie dÕistruzioni in formato binario
(quindi interpretabili dal processore) che devono essere eseguite secondo un ordine
preciso.

Il file eseguibile • strettamente dipendente dal sistema operativo della macchina su cui viene esegui-
to. Per esempio, non • possibile trasferire un file eseguibile da un sistema operativo Unix su una mac-
china Windows: lÕinsieme delle informazioni contenute nel file eseguibile destinato al sistema opera-
tivo Unix, infatti, contiene istruzioni che Windows non • in grado di eseguire.
Il passaggio da codice sorgente a file eseguibile • detto compilazione e viene eseguito da un pro-
gramma detto compilatore.

Per compilazione si intende il processo tramite il quale un programma, scritto in un


qualsiasi linguaggio di programmazione, viene tradotto in istruzioni eseguibili dal
processore della macchina su cui viene compilato.

In ambiente C++ il processo di compilazione • in realtˆ diviso in due parti ben distinte:

1. parsing del codice sorgente;


2. compilazione.

Il parsing del codice sorgente • la prima attivitˆ che viene svolta durante il processo di compilazione.

I file sorgente vengono esaminati da un sottoprogramma, detto parser, che ricerca


eventuali errori sintattici e grammaticali del linguaggio in cui • scritto il file sorgente.

36
Unitˆ didattica 3 - Introduzione a C++

Qualora siano presenti, gli errori di scrittura nel programma sorgente vengono segnalati, sotto for-
ma di errori di compilazione, al programmatore che dovrˆ correggere il suo programma riprenden-
done la fase di editing.
Una volta che il parser ha esaminato lÕintero file e ne ha verificato la correttezza sintattica e gram-
maticale • la volta del compilatore vero e proprio, che deve trasformare le istruzioni in linguaggio
di programmazione contenute nel file sorgente in una sequenza di istruzioni in linguaggio mac-
china.
Ogni linguaggio di programmazione provvede a individuare i propri file sorgente, obbligando il pro-
grammatore a salvarli con una determinata estensione: per esempio nel linguaggio C i file sorgente
hanno estensione .c, in C++ hanno .cpp, in Java .java.
Un file eseguibile, frutto della compilazione di un file sorgente, ha estensione .exe e contiene istru-
zioni direttamente eseguibili dalla CPU. La figura che segue riporta lo schema logico riassuntivo dei
vari passaggi che portano dal programma sorgente al programma eseguibile.

Programmatore

Editor Parser
File sorgente Librerie

S“
Errori

No

Compilatore
Moduli

File eseguibile

LÕesecuzione del programma finale avviene sotto il controllo del sistema operativo, seguendo i pas-
saggi principali indicati di seguito.

1. Trasferimento del contenuto del file eseguibile dalla memoria di massa nella RAM.
2. Inizio dellÕesecuzione del programma, mediante il richiamo della funzione main() da parte
del sistema operativo.
3. Svolgimento dellÕelaborazione.
4. Termine dellÕelaborazione.

LÕinizio dellÕesecuzione di un programma finale scritto in C++ coincide con la chiamata alla funzio-
ne main() da parte del sistema operativo.
Lo schema della figura applicato al primo programma ci mostra i diversi passaggi necessari per arri-
vare dalla scrittura del file sorgente Ciao.cpp fino allÕesecuzione del programma eseguibile
Ciao.exe.
Dopo che il programma • stato compilato, • possibile mandarlo in esecuzione.

37
Sezione 2 - Primi elementi di programmazione

Prova di esecuzione

Come si nota, il programma esegue semplicemente la visualizzazione del messaggio ÒCiao, mondo!Ó.

3.2 Analisi del codice


Riprendiamo il codice dellÕesempio, che riportiamo di seguito: • arrivato il momento di fornire, per
quanto possibile, una spiegazione chiara delle singole istruzioni.
Si tenga comunque presente che alcune di esse introducono argomenti di una certa complessitˆ, che •
prematuro approfondire in queste prime lezioni.

Ciao.cpp
1 #include <iostream>
2 using namespace std;
3 //inizio
4 int main ()
5 {
6 //scrivi il messaggio di saluto
7 cout << ÒCiao, mondoÓ << endl;
8 //arresta lÕesecuzione del programma
9 system(pause);
10 //termina il programma
11 return 0;
12 }

La riga 1 indica al compilatore di leggere la libreria di funzioni iostream e di inserirla nel codice. Ta-
le file contiene la definizione delle funzioni e degli operatori per la gestione dei flussi di immissione e di
emissione. La presenza di questÕistruzione permette lÕesecuzione dellÕistruzione cout << "Ciao,
mondo!" << endl; per questo • necessario includere un riferimento a questa libreria in tutti i pro-
grammi che leggono dati dalla testiera o scrivono dati a video.
La riga 2 indica al compilatore che tutti i nomi utilizzati nel programma appartengono al namespace
standard del C++. Nei programmi di grandi dimensioni • alquanto comune che programmatori diver-
si utilizzino gli stessi nomi per denotare elementi diversi: si possono evitare conflitti di nomi utilizzan-
do namespace separati, tuttavia per i semplici programmi che verranno scritti in questo libro tale misu-
ra non • asolutamente necessaria.
Le righe 3, 6, 8 e 10 non sono istruzioni, bens“ commenti.

I commenti sono utilizzati in genere come una descrizione delle funzionalitˆ di una
istruzione, di un intero blocco di codice o come informazioni sullÕautore del pro-
gramma. In altre parole: tutto ci˜ che serve per informazioni e/o descrizioni, va inse-
rito in un commento.
Il compilatore, allÕatto della generazione del codice eseguibile, ignora i commenti
lavorando solo sul codice del programma.

38
Unità didattica 3 - Introduzione a C++

In molti linguaggi sono supportati due tipi di commenti:

: commenti a riga singola, che iniziano con la combinazione di caratteri // e si estendono solo
fino alla fine della riga;
: commenti delimitati, che iniziano con la combinazione di caratteri /*, terminano con la com-
binazione */ e possono estendersi su più righe, come nell’esempio riportato di seguito.
/*
Questo programma scrive un
messaggio di benvenuto
*/

Il costrutto individuato dalle righe 4, 5, 11, 12 del codice definisce una funzione denominata main.
Una funzione rappresenta una raccolta di istruzioni di programmazione che consente di portare a
termine una determinata operazione.
Ogni programma C++ deve disporre di una funzione main. La maggior parte dei programmi C++
contiene altre funzioni oltre a main, il cui ruolo spiegheremo più avanti. Le istruzioni del corpo del-
la funzione main, vale a dire quelle all’interno delle parentesi graffe {}, vengono eseguite una per
volta, in sequenza.
Tutti i programmi C++ iniziano l’esecuzione con questo costrutto: nel nostro esempio le istruzioni
che vanno da riga 6 a riga 12 sono delimitate dalle parentesi di riga 5 e riga 13, che definiscono un
blocco di codice “appartenente” alla funzione main, a sua volta “appartenente” al programma
Ciao.cpp.
Come vedremo, esistono molti costrutti che utilizzano le parentesi graffe per raggruppare un insie-
me di istruzioni.
La riga 7 contiene l’istruzione che realizza concretamente quanto richiesto dal programma: infatti, il
comando cout richiede che venga scritto a video quanto indicato dopo di esso.
La sequenza di caratteri racchiusa da virgolette “Ciao, mondo!” viene denominata stringa. È
necessario racchiuderne il contenuto all’interno delle virgolette, per fare in modo che il compi-
latore ne comprenda il significato letterale. In questo breve programma non è possibile alcuna
confusione, ma si supponga di voler visualizzare il termine “main” sullo schermo: indicando il ter-
mine tra virgolette, vale a dire specificando la stringa “main” dopo il comando cout, il compi-
latore comprende che si tratta della sequenza di caratteri “main”, non della funzione denominata
main. In C++ la semplice regola di racchiudere tutte le stringhe di testo tra virgolette è sufficien-
te per fare in modo che il compilatore le interpreti come semplice testo e non come istruzioni del
programma.
L’instruzione si conclude con la parola chiave endl (che sta per “end line”), che comunica al siste-
ma operativo che la stringa è terminata e che il cursore dello schermo deve andare a capo dopo aver-
la scritta.
La riga 9 sospende l’esecuzione del programma e comunica all’utente il messaggio

Solo dopo che l’utente ha effettuato quanto indicato, l’esecuzione del programma procede alla riga
successiva. Il comando di arresto system (“pause”); non è indispensabile e può essere omesso
senza alterare la logica del programma, ma è molto utile perché permette all’utente di controllare
quanto è stato realizzato dal programma e quali sono i risultati da esso prodotti.
La riga 12, infine, chiude il codice della funzione main e restituisce il controllo al sistema ope-
rativo.

39
Sezione 2 - Primi elementi di programmazione

Si ricordi sempre che ogni istruzione C++ valida termina con un “;” (il carattere “punto e virgola”), altrimenti
il compilatore genera un errore di sintassi.

3.3 Definizione e assegnazione


Durante la programmazione, è sempre necessario memorizzare dati e valori, sia temporanei sia defi-
nitivi. A tal proposito, vengono utilizzate le variabili.

Una variabile è una zona (locazione) di memoria che memorizza un determinato ti-
po di dato, identificato e accessibile tramite un nome mnemonico.

Attribuire un nome a una locazione di memoria anziché avere a che fare con un incomprensibile in-
dirizzo formato da lunghe sequenze esadecimali solleva il programmatore da un’enorme fatica: si im-
magini un programma che ha al proprio interno centinaia di variabili codificate con il loro indirizzo
di memoria! Probabilmente tutti i linguaggi di alto livello come il C++ non avrebbero avuto motivo
di esistere: si sarebbe continuato a programmare in codice macchina e si sarebbe fatta una fatica enor-
me a progettare le applicazioni visuali che oggi siamo abituati a utilizzare.

Per utilizzare una variabile, è necessario definirla (dichiarazione) all’interno del codi-
ce, nel seguente modo:
nometipo nomevariabile;
dove:
: nometipo specifica il tipo di dato che si intende memorizzare nella variabile (ve-
dremo più avanti i vari tipi di dato);
: nomevariabile identifica la variabile tramite un nome.

Per esempio, l’istruzione


int numero;
dichiara una variabile di nome numero di tipo intero (cioè senza la parte decimale), che possiamo
utilizzare all’interno di un programma. Una volta dichiarata una variabile, per assegnarle un valore si
deve scrivere un’istruzione del tipo numero=122;.
Vediamo un esempio pratico molto semplice.

Esempio Numero ................................................................................................................


Definire una variabile numero di tipo intero e assegnarle il valore 122.

Codice
Numero.cpp
1 #include <iostream>
2 using namespace std;
3 //inizio
4 int main ()
5 {
6 //dichiara la variabile di tipo intero

40
Unitˆ didattica 3 - Introduzione a C++

7 int numero;
8
9 //assegna il valore
10 numero=122;
11
12 //scrivi il valore di numero
13 cout << numero << endl;
14
15 //salta due righe
16 cout << endl << endl;
17 //arresta l'esecuzione del programma
18 system ("pause");
19 //termina il programma
20 return 0 ;
21 }

Prova di esecuzione

Analisi del codice


Il codice • simile a quello del primo programma tranne per il fatto che, anzichŽ visualizzare una se-
rie di caratteri (una stringa), visualizza il valore assegnato a una variabile.
Per prima cosa alla riga 7 viene dichiarata una variabile di tipo intero (int) e di nome numero, men-
tre alla riga 10 viene assegnato un valore a tale variabile, cio• 122. Ora la variabile numero contiene
il valore 122.
Alla riga 13, con la funzione cout si visualizza il contenuto della variabile sullo schermo.

Si noti che dopo il costrutto cout<< non cÕ• pi• una stringa di caratteri, ma una variabile: questo signi-
fica che lÕistruzione riconosce automaticamente che deve essere visualizzato il valore contenuto nella va-
riabile numero. Le operazioni di dichiarazione e di assegnazione di una variabile sono molto semplici, ma
bisogna considerare che il valore da assegnare a una variabile pu˜ essere anche il risultato dellÕesecu-
zione di unÕespressione matematica. LÕimportante • dunque che il valore, comunque sia determinato, ri-
spetti il tipo di dato associato alla variabile.

...........................................................................................................................................

Nomi di variabili
Negli esempi visti fino a questo punto sono state utilizzate poche variabili, quindi non sono sorti pro-
blemi di chiarezza nellÕinterpretazione del codice. é utile, tuttavia, comprendere lÕimportanza di at-
tribuire a una variabile il nome pi• adatto.
Quando, infatti, si scriveranno programmi pi• complessi, con un numero considerevolmente pi• al-
to di variabili, • consigliabile che queste abbiano un nome il pi• possibile autodescrittivo. Per esem-
pio, se si intende scrivere un programma che calcola lÕarea di un triangolo • opportuno attribuire al-
la variabile che contiene il valore della base il nome base e a quella che contiene lÕaltezza il nome
altezza. Ci˜ si traduce in un immediato riconoscimento delle variabili e del loro utilizzo allÕinter-
no del programma.

41
Sezione 2 - Primi elementi di programmazione

Questa regola si deve applicare ai programmi scritti da un singolo programmatore, ma specialmente


ai programmi scritti in collaborazione tra pi• programmatori.
La lettura del codice • molto pi• chiara e agevole, quindi il consiglio • quello di usare sempre questa
semplice prassi.
Il nome di una variabile pu˜ contenere sia caratteri sia cifre, tuttavia altezza1 • una variabile C++
valida, mentre 1altezza non lo •: infatti, tale linguaggio non ammette che il nome di una variabi-
le cominci con una cifra, generando un errore in fase di compilazione.
Il nome della variabile pu˜ anche contenere il carattere Òtrattino bassoÓ (Ò_Ó, anche detto undersco-
re): per esempio, numero_1 • un nome di variabile valido.
Il C++ • un linguaggio case sensitive, ossia distingue tra lettere maiuscole e minuscole, quindi le due
variabili Numero1 e numero1 sono considerate due entitˆ distinte.
Per evitare confusione ed errate interpretazioni, • consigliabile non assegnare mai alle variabili di uno
stesso programma nomi come Numero1 e numero1: • un pessimo stile di programmazione. I bravi
programmatori di C++ usano la convenzione di scrivere i nomi delle variabili utilizzando solamnen-
te caratteri minuscoli, per differenziarle dalle costanti (che vedremo pi• avanti, e che vengono scrit-
te in maiuscolo). In ogni caso ognuno • libero di usare lo stile che preferisce, lÕimportante • che sia
sempre coerente e chiaro.

3.4 Tipi di dato


La dichiarazione di una variabile • il processo tramite il quale il compilatore stabilisce la quantitˆ esat-
ta di memoria da ÒallocareÓ per memorizzare il tipo di dato della variabile.

Allocare significa fornire lÕesatta quantitˆ di ÒspazioÓ di memoria necessaria per


memorizzare una variabile, un file o qualsiasi oggetto che debba essere memoriz-
zato.

In C++ esistono molti tipi di dato, ognuno dei quali necessita di una quantitˆ di memoria idonea a
contenerlo. Per dichiarare una variabile bisogna sapere di che tipo •.
NellÕultimo esempio esaminato • stata dichiarata una variabile di tipo numerico, in particolare di ti-
po intero, denominata int.

I tipi di dato numerici in C++ rientrano nelle seguenti quattro categorie:


1. interi
2. in virgola mobile
3. decimali
4. booleani

Il tipo di dato rappresenta il formato dellÕinformazione.


Ogni informazione deve essere memorizzata rispettandone il tipo: una variabile di tipo intero, per
esempio, deve essere memorizzata assegnando la quantitˆ di byte corretta necessaria per memoriz-
zarne il tipo di dato.
Il C++ • un linguaggio di programmazione in cui • obbligatorio tipizzare le variabili: si dice, quindi,
che il C++ • un linguaggio fortemente tipizzato.
AllÕatto della compilazione del codice sorgente viene generato il codice ottimizzato in base al tipo
delle variabili utilizzate allÕinterno del programma. é compito del programmatore stabilire duran-
te la dichiarazione delle variabili il tipo pi• adatto, affinchŽ non venga allocata inutilmente la me-
moria.
Quindi la buona progettazione di un programma dipende anche dal corretto uso dei tipi.

42
Unitˆ didattica 3 - Introduzione a C++

3.5 Tipo di dato intero


Il tipo di dato intero rappresenta tutti i numeri in cui non compare la parte decimale.
Una variabile dichiarata con uno di questi tipi di dato occupa una determinata quantitˆ di memoria,
di cui parleremo nei paragrafi che seguono.

Il C++ dispone di tre tipi di dato intero, articolati nelle categorie riportate nella tabel-
la che segue.
TIPO C++ MIN MAX
short Ð32768 32767
int Ð2.147.483.648 2.147.483.647
long Ð9.223.372.036.854.775.808 9.223.372.036.854.775.807

Tipo int
Un tipo di dato int occupa 4 byte di memoria, cio• 32 bit. Esso pu˜ contenere un numero intero
con segno compreso tra:
Ð2.147.483.648 e 2.147.483.647
corrispondenti alle espressioni Ð231 e 231 Ð 1.

Esempio Intero ...................................................................................................................


Dichiarare una variabile num di tipo intero e scriverne a video il contenuto.

Codice
Intero.cpp
1 #include <iostream>
2 using namespace std;
3 // inizio
4 int main ()
5 {
6 //dichiara la variabile e assegnale un valore
7 int num = 1234567890;
8
9 //scrivi num
10 cout << "intero = " << num;
11
12 //salta due righe
13 cout << endl << endl;
14 //arresta l'esecuzione del programma
15 system ("pause");
16 //termina il programma
17 return 0 ;
18 }

Prova di esecuzione

43
Sezione 2 - Primi elementi di programmazione

Analisi del codice


La riga 7 assolve a due funzioni. La prima parte (int num) serve a definire una variabile di tipo int
e di nome num; la seconda parte (num = 1234567890) assegna a num un valore. Si noti che tale va-
lore rientra nellÕintervallo Ð 231 e 231 Ð 1.
La riga 10 effettua sul monitor la scrittura del contenuto della variabile num: cout • infatti la
funzione necessaria per visualizzare dati sul video. Dopo il comando cout possono essere indi-
cati nomi di variabili o nomi di costanti, di tipo sia numerico sia stringa.
Nel nostro caso i dati da scrivere sono la stringa Òintero =Ó, che specifica allÕutente che cosa
viene scritto in seguito, e la variabile num, di cui in fase di esecuzione viene scritto il contenuto
(cio• il valore 1.234.567.890).
Ciascun elemento che segue il comando cout • preceduto dai caratteri di redirezione Ò<<Ó.
...........................................................................................................................................

Tipo short
Il tipo di dato intero, come abbiamo visto, occupa 4 byte di memoria, ma spesso • necessario elabo-
rare numeri molto piccoli, che sicuramente non eccedono i valori compresi tra
Ð32.768 e 32.767
cio• Ð215 e 215 Ð1.
In questo potremmo utilizzare agevolmente il tipo di dato short.
Il tipo short occupa 2 byte, cio• 16 bit di memoria, e consente un notevole risparmio di me-
moria.

Tipo long
Se in un programma si devono manipolare numeri interi molto grandi, cÕ• bisogno di un tipo di da-
to capace di contenere tali numeri.
A tale scopo alcuni compilatori C++ (ma non tutti) mettono a disposizione il tipo long che utilizza
ben 8 byte (cio• 64 bit) e pu˜ memorizzare un numero compreso tra:
Ð9.223.372.036.854.775.808 e 9.223.372.036.854.775.807
cio• tra Ð263 e 263 Ð1.

Quando si progetta un programma si deve tener presente il giusto utilizzo del tipo di dato, onde evi-
tare inutili sprechi di memoria; • quindi consigliabile pensare bene a quale tipo di dato si adatti me-
glio allÕelaborazione e al trattamento dei dati, prima di procedere con le fasi successive dello svilup-
po del programma.

3.6 Tipo char


Oltre ai numeri non • insolito, in un programma, dover memorizzare dei caratteri, dove per Òca-
ratteriÓ si intendono lettere come A, B, C, d, g, e, e simboli come !, $, /, ), e cos“ via.
Il C++ interpreta i caratteri in base alla codifica UNICODE, che utilizza 2 byte per memoriz-
zare ciascun carattere. Ogni carattere o simbolo viene identificato con un numero intero com-
preso tra
0 e 65.535

I caratteri UNICODE sono valori a 16 bit che rappresentano gran parte delle lingue
scritte conosciute.

44
Unitˆ didattica 3 - Introduzione a C++

AllÕinterno di un programma, per poter assegnare un carattere a una variabile • necessario racchiu-
derlo tra apici singoli. Di seguito riportiamo alcune righe di esempio.

//dichiara una variabile di tipo char


char car;
//assegnale il carattere ÔaÕ
car=ÔaÕ;

I caratteri possono essere espressi anche sotto forma di codici strettamente UNICODE. Quelli di uso
pi• diffuso, ossia quelli occidentali, rientrano nellÕintervallo di valori che va da 0 a 255 in notazione
decimale, ma il computer li interpreta sempre secondo il sistema esadecimale (quindi non utilizza la
base 10, ma la base 16). Si tenga presente che esiste sempre la possibilitˆ di visualizzare il carattere
corrispondente a un numero.

Esempio Carattere .............................................................................................................


Visualizzare il carattere UNICODE corrispondente a un numero dato.

Codice
Carattere.cpp
1 #include <iostream>
2 using namespace std;
3 //inizio
4 int main ()
5 {
6 /*dichiara una variabile char e assegnale un numero intero
7 compreso tra 0 e 255 */
8 char car=(char)125;
9
10 //scrivi il carattere corrispondente al numero 125
11 cout << car;
12
13 //salta due righe
14 cout << endl << endl;
15 //arresta l'esecuzione del programma
16 system ("pause");
17 //termina il programma
18 return 0 ;
19 }

Prova di esecuzione

Analisi del codice


LÕunica riga che necessita di commento • la riga 8 che, tramite il costrutto (char), comunica al
compilatore che deve effettuare la conversione di un numero decimale Ò125Ó nel carattere corri-

45
Sezione 2 - Primi elementi di programmazione

spondente. Per il momento si segnala che questo tipo di conversione viene chiamato casting espli-
cito, in gergo informatico: pi• avanti se ne parlerˆ ancora.
...........................................................................................................................................

3.7 Numeri in virgola mobile


Nelle nostre applicazioni avremo sicuramente bisogno di gestire, oltre a numeri interi, anche nume-
ri che hanno una parte decimale. Per esempio, per effettuare calcoli scientifici • indispensabile utiliz-
zare variabili numeriche che consentano di memorizzare numeri con la virgola. In C++ il tipo di da-
to numerico in virgola mobile • il double.

double • il primo tipo di dato che il C++ utilizza per memorizzare i numeri con la virgola.
Ogni valore di questo tipo occupa 8 byte di memoria ed • compreso tra i seguenti in-
tervalli di valori:
Ð1,7 ××10308 e Ð5,0 ××10Ð324

5,0 ××10Ð324 e 1,7 ××10308

La precisione di un valore di tipo double • di circa 15 cifre. Per esempio si pu˜ scrivere, senza incor-
rere in errori, lÕistruzione di assegnazione riportata di seguito.

double x=3.145926587412536;

Esempio Double .................................................................................................................


Dichiarare, assegnare e stampare a video una variabile di tipo double.

Codice
Double.cpp
1 #include <iostream>
2 using namespace std;
3 //inizio
4 int main ()
5 {
6 //dichiara la variabile di tipo double
7 double numero;
8
9 //assegna un valore
10 numero=3.145926587412;
11
12 //scrivi il valore
13 cout << numero << endl;
14
15 //salta due righe
16 cout << endl << endl;
17 //arresta l'esecuzione del programma
18 system ("pause");
19 //termina il programma
20 return 0 ;
21 }

46
Unitˆ didattica 3 - Introduzione a C++

Prova di esecuzione

Come si pu˜ vedere, a video non vengono riportate tutte le cifre decimali: per maggiori dettagli
su come gestire la precisione dellÕoutput si rimanda al paragrafo sulla formattazione dellÕUnitˆ di-
dattica 4.
...........................................................................................................................................

Intervalli di definizione e precisione dei numeri


I valori definiti mediante i tipi int e double introducono un problema non indifferente, legato al
fatto che lÕintervallo di numeri interi o a virgola mobile rappresentabili • limitato. Alcuni compilato-
ri C++ ammettono per i dati di tipo int un intervallo di valori particolarmente ridotto, che va da
Ð32.768 a 32.767: ci˜ • dovuto al fatto che i numeri interi sono rappresentati su 16 bit, il che per-
mette di codificare 65.536 valori diversi, metˆ dei quali (da Ð1 aÐ32.768) rappresentano numeri ne-
gativi, mentre per i numeri positivi il massimo valore possibile • 32.767, dato che occorre tenere con-
to della rappresentazione del valore 0.
Legato ai numeri a virgola mobile cÕ• invece il problema della precisione, dato che anche i numeri a
virgola mobile a doppia precisione (cio• di tipo double) non possono memorizzare pi• di quindici
cifre decimali significative. Si consideri il seguente caso: un cliente pu˜ ritenere che il prezzo di tre-
cento triliardi di dollari ($300.000.000.000.000) sia eccessivo per un determinato prodotto e vor-
rebbe ridurlo di 5 centesimi, portandolo alla cifra di $299.999.999.999.999,95.
Se si prova a eseguire il programma indicato di seguito si visualizzerˆ il valore 0,0625, al posto
di 0,05.

#include <iostream>
using namespace std;
int main()
{
double original_price = 3E14;
double discounted_price = original_price - 0.05;
double discount = original_price - discounted_price;
/* la differenza dovrebbe valere 0.05 */
cout Ç discount Ç "\n"; /* viene visualizzato 0.0625 ! */
}

Nella maggior parte dei programmi, come pure in quelli riportati in questo libro, la precisione, nel
trattamento dei numeri di tipo double, non costituisce in ogni caso un problema.

C++ dispone di un altro tipo di numeri a virgola mobile, denominato float, che prevede una pre-
cisione molto pi• limitata, ristretta a circa 7 cifre decimali.
Questo tipo, di norma, non dovrebbe essere utilizzato nei programmi, dato che la sua limitata pre-
cisione pu˜ costituire un problema in molti casi e tutte le funzioni matematiche restituiscono da-
ti di tipo double. Se si memorizzano i risultati di unÕoperazione matematica in una variabile di ti-
po float il compilatore segnala la possibile perdita di informazioni, quindi per evitare questi
messaggi di avvertimento e per mantenere la precisione dei risultati • consigliabile evitare di uti-
lizzare variabili di questo tipo.

47
Sezione 2 - Primi elementi di programmazione

3.8 Casting
Può capitare di memorizzare un valore di un certo tipo in una variabile di tipo differente. Ogni vol-
ta che si corre il rischio di perdere delle informazioni il compilatore lo segnala con un messaggio di
avvertimento. Se, per esempio, si memorizza un valore double in una variabile int, si possono per-
dere informazioni per due motivi:

: si perde la parte frazionaria;


: il valore del numero può essere troppo grande.

L’istruzione int p = 1.0E100; non è corretta, dato che 10100 è un valore superiore all’intero più
grande memorizzabile in una variabile di tipo intero.
A volte, comunque, si rende necessario convenire un valore a virgola mobile in un valore intero. Se
è possibile perdere la parte frazionaria e se il numero a virgola mobile non supera il massimo valore
intero possibile si può evitare il messaggio di avvertimento ricorrendo a un’operazione di casting,
che consiste nella conversione da un tipo (per esempio, double) in un altro (per esempio, int).
L’istruzione riportata di seguito è un esempio di casting in C++.

int n = (int)(y + 0.5);

3.9 Tipo booleano


A volte, ciò che interessa sapere è se un enunciato è vero o falso (true oppure false): in questo ca-
so, si può utilizzare il tipo bool.

Una variabile di tipo bool occupa 1 byte di memoria e la sua sintassi è molto sempli-
ce; per esempio, con:
bool flag=true;
si è dichiarata una variabile di tipo bool con l’assegnazione del valore true (vero).
Questa variabile si potrebbe utilizzare come segnalatore (flag) dell’esito di una ela-
borazione testandone il contenuto, cioè true oppure false.

Esempio Boolean ...............................................................................................................


Dichiarare, assegnare e scrivere a video una variabile di tipo bool.

Codice
Boolean.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //dichiara e inizializza a true la variabile di tipo bool
7 bool flag = true;
8
9 //scrivi il valore
10 cout << "vero = " << flag << endl;
11

48
Unitˆ didattica 3 - Introduzione a C++

12 //cambia il valore di flag


13 flag = false;
14
15 //scrivi il valore
16 cout << "falso = " << flag << endl;
17
18 //salta due righe
19 cout << endl << endl;
20 //arresta l'esecuzione del programma
21 system ("pause");
22 //termina il programma
23 return 0 ;
24 }

Prova di esecuzione

Analisi del codice


Alla riga 7 alla variabile flag viene assegnato true, che • uno dei due valori possibili per il tipo
bool. Quando viene scritto il valore di flag, a video compare 1. Alla riga 13 il valore di flag pas-
sa da true a false e, quando ne viene visualizzato il contenuto (mediante lÕistruzione alla riga 16),
a video compare 0.
Si deve tenere in considerazione che i due valori true e false sono parole riservate del C++ e che,
in quanto tali, non possono essere utilizzate come nomi di variabili.
Si approfondirˆ il concetto quando si studieranno le strutture di controllo, pi• avanti nella tratta-
zione.
...........................................................................................................................................

3.10 Costanti
In queste prime lezioni si • parlato di variabili come di entitˆ utili per memorizzare valori che posso-
no cambiare durante unÕelaborazione.

In tutti i linguaggi di programmazione sono previsti elementi che consentono di ar-


chiviare un valore che resta invariato in tutto il programma: sono le costanti.

La sintassi per definire una costante • la seguente.


const tipo nome = valore;
Un esempio • questo.
const double PiGreco=3.14F;

Con le costanti si • sicuri che il valore memorizzato resterˆ invariato durante la vita dellÕapplicazione: se
si cerca di assegnare un valore a una costante viene infatti generato un errore in fase di compilazione.

49
Sezione 2 - Primi elementi di programmazione

Esempio Const ...................................................................................................................


Utilizzo di una costante.

Codice
Const.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //dichiarazione di una costante di tipo double
7 const double PIgreco = 3.1459;
8
9 //scrivi il valore della costante
10 cout << PIgreco << endl;
11
12 //salta due righe
13 cout << endl << endl;
14 //arresta l'esecuzione del programma
15 system ("pause");
16 //termina il programma
17 return 0 ;
18 }

Analisi del codice


Il listato ha la stessa struttura dei precedenti e serve solo per mostrare lÕutilizzo di una costante di ti-
po double, definita alla riga 7.
...........................................................................................................................................

50
Unità didattica 3 - Introduzione a C++

Esercizi
Unità didattica 3

1 Quanti e quali sono i tipi di commento in C++?

2 A che cosa servono le parentesi graffe?

3 Qual è il carattere che viene usato per terminare un’istruzione?

4 Modificare il codice del listato del programma “Ciao.cpp” affinché scriva il vostro nome e cognome an-
ziché il messaggio di benvenuto.

5 Salvare il codice precedentemente modificato con il nome “Mionome.cpp” ed eseguirlo.

6 Modificare il codice del listato del programma “Ciao.cpp” affinché scriva il messaggio “Benvenuto in la-
boratorio”.

7 Salvare il codice precedentemente modificato con il nome “Benvenuto.cpp” ed eseguirlo.

8 Nel seguente codice c’è un errore; individuare il relativo numero di riga e spiegare di che errore
si tratta.
n.r. Ciao.cpp
1 #include <iostream>
2 using namespace std
3 //INIZIO
4 int main ()
5 {
6 //scrivi il messaggio di saluto
7 cout << "Ciao, mondo!" << endl;
8 //arresta l'esecuzione del programma
9 system ("pause");
10 //termina il programma
11 return 0;
12 }

9 Nel seguente codice c’è un errore; individuare il relativo numero di riga e spiegare di che errore
si tratta.
n.r. Ciao.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //scrivi il messaggio di saluto
7 cout << "Ciao, mondo! << endl;
8 //arresta l'esecuzione del programma
9 system ("pause");
10 //termina il programma
11 return 0;
12 }

51
Sezione 2 - Primi elementi di programmazione

Esercizi
Unità didattica 3

q0 Nel seguente codice c’è un errore; individuare il relativo numero di riga e spiegare di che errore
si tratta.
n.r. Ciao.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //scrivi il messaggio di saluto
7 cout << "Ciao, mondo! " endl;
8 //arresta l'esecuzione del programma
9 system ("pause");
10 //termina il programma
11 return 0 ;
12 }

qa Nel seguente codice c’è un errore; individuare il relativo numero di riga e spiegare di che errore
si tratta.
n.r. Ciao.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //scrivi il messaggio di saluto
7 cout << "Ciao, mondo!" << endl;
8 //arresta l'esecuzione del programma
9 system ("pause");
10 //termina il programma
11 return "0" ;
12 }

qs Nella tabella sottostante sono indicate alcune ipotesi di nomi di variabili; ricordando quali sono le rego-
le per la scelta dei nomi di variabili, seleziona i nomi corretti.

NOME VARIABILE CORRETTO ERRATO


Numero
1numero
numero2
il numero
Pi Greco
angolo

qd Nel segmento di codice riportato sotto è presente un errore di sintassi: individualo e correggilo.
integer numero;
numero=122;
cout << numero << endl;

52
Unità didattica 3 - Introduzione a C++

Esercizi
Unità didattica 3

qf Nel segmento di codice riportato sotto è presente un errore di sintassi: individualo e correggilo.
int numero;
numero= 0.122;
cout << numero << endl;

qg Nel segmento di codice riportato sotto è presente un errore di sintassi: individualo e correggilo.
integer numero;
numero = 122;
cout << numero endl;

qh Indica la parola chiave per definire le variabili di tipo intero corto, il minimo e il massimo valore che es-
se possono contenere.

qj Indica la parola chiave per definire le variabili di tipo intero, il minimo e il massimo valore che esse pos-
sono contenere.

qk Indica la parola chiave per definire le variabili di tipo intero lungo, il minimo e il massimo valore che es-
se possono contenere.

ql Indica la parola chiave per definire le variabili di tipo reale a doppia precisione e gli intervalli dei valori
che esse possono contenere.

w0 Indica la parola chiave per definire le variabili di tipo reale a precisione singola e gli intervalli dei valori
che esse possono contenere.

wa Dopo aver eseguito le istruzioni seguenti, che cosa contiene la variabile n?


y=5;
int n = (int)(y + 0.5);

53
Unitˆ didattica
4
Visualizzazione
e acquisizione
CHE COSA IMPARERAI A FARE

$ Sapere utilizzare le istruzioni di acquisizione


$ Gestire, in generale, una corretta visualizzazione dei risultati
$ Organizzare la formattazione dei dati di output

CHE COSA DOVRAI STUDIARE

$ Istruzioni di acquisizione
$ Formattazione dellÕoutput
$ Caratteri di escape
Unitˆ didattica
Visualizzazione e acquisizione
4
I metodi per la scrittura delle informazioni visti finora sono in grado anche di gestire un sistema di
formattazione dellÕoutput. In questa Unitˆ didattica viene fornita la descrizione di un primo tipo di
formattazione dei risultati dellÕesecuzione dei programmi. Quando si studieranno, invece, gli opera-
tori si avrˆ modo di formattare i risultati in maniera pi• complessa ed efficiente.

4.1 Acquisizione delle informazioni: il metodo cin


Nei programmi scritti nelle Unitˆ didattiche precedenti sono state utilizzate unicamente istruzioni
preposte alla visualizzazione delle informazioni, sia numeriche sia in forma testuale: • arrivato il mo-
mento di arricchire i programmi con istruzioni che consentano di acquisire dati forniti dallÕutente.
Nei prossimi paragrafi verranno illustrate le modalitˆ di acquisizione delle informazioni da console.
Il linguaggio C++ per la visualizzazione delle informazioni mette a disposizione, oltre al comando
cout, anche altre istruzioni, che consentono di acquisire dati dalla tastiera: la prima che illustreremo
• quella denominata cin.

LÕistruzione cin >> ... acquisisce quanto digitato sulla tastiera fino a quando lÕu-
tente non preme il tasto Invio.

LÕesecuzione del metodo cin interrompe momentaneamente il flusso di elaborazione del program-
ma e lascia il sistema in attesa finchŽ lÕutente, dopo aver digitato tutti i caratteri desiderati sulla ta-
stiera, preme il tasto Invio.

Esempio Cin .......................................................................................................................


Leggere un numero inserito da tastiera.

Codice
Cin.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //dichiara una variabile di tipo intero
7 int numero;
8
9 //messaggio per lÕutente
10 cout << "Inserisci un numero => ";
11
12 //assegna a numero il valore letto da tastiera
13 cin >> numero;
14
15 //scrivi la variabile

55
Sezione 2 - Primi elementi di programmazione

16 cout << "hai scritto " << numero << endl;


17
18 //salta due righe
19 cout << endl << endl;
20 //arresta lÕesecuzione del programma
21 system ("pause");
22 //termina il programma
23 return 0;
24 }

Prova di esecuzione

Analisi del codice


Alla riga 7 • stata dichiarata la variabile di tipo intero numero, destinata a contenere il valore intero
letto dalla tastiera.
Eseguendo la riga 10 viene visualizzato il messaggio che richiede allÕutente di inserire un numero: questo
tipo di istruzione • importante, dato che serve per guidare lÕinterazione dellÕutente con il programma.
LÕassegnazione del dato letto alla variabile numero viene effettuata alla riga 13, nella quale compa-
re lÕistruzione cin >> ..., che legge quanto digitato sulla tastiera e lo assegna alla variabile nu-
mero (che conterrˆ anche lÕultimo carattere digitato, quello relativo al tasto Invio).
Infine alla riga 16 viene scritto sul video, a titolo di verifica, il contenuto della variabile numero, va-
le a dire il dato letto dalla tastiera.
...........................................................................................................................................

Naturalmente, lÕistruzione cin >> ... permette anche la lettura di numeri di tipo double.

Esempio Cin_double ..........................................................................................................


Leggere e scrivere un numero di tipo double.

Codice
Cin_double.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //dichiara una variabile di tipo double
7 double numero;
8
9 //messaggio per lÕutente
10 cout << "Inserisci un numero => " ;
11
12 //assegna a numero il valore letto da tastiera

56
Unitˆ didattica 4 - Visualizzazione e acquisizione

13 cin >> numero;


14
15 //stampa della variabile
16 cout << "hai scritto " << numero << endl ;
17
18 //salta due righe
19 cout << endl << endl;
20 //arresta lÕesecuzione del programma
21 system ("pause");
22 //termina il programma
23 return 0;
24 }

Prova di esecuzione

Analisi del codice


LÕunica differenza con lÕesempio precedente • alla riga 7, dove la variabile che acquisisce il valore •
definita di tipo double. Dalla prova di esecuzione si nota che non tutte le cifre vengono visualizza-
te. Per questo problema si rimanda ai prossimi paragrafi.
...........................................................................................................................................

é possibile leggere pi• di un valore contemporaneamente.


Per esempio, lÕistruzione di richiesta di input

cin >> num1 >> num2 >> num3;


legge tre valori digitati sulla tastiera: si ricorda che se tali valori sono disposti su unÕunica riga occor-
re separare ciascun dato dal successivo con uno spazio bianco.

Esempio Cin_multiplo ........................................................................................................


Leggere tre numeri scritti in sequenza e scriverli su tre righe diverse.

Codice
Cin_multiplo.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //dichiara le variabili di tipo double
7 double numero1;
8 double numero2;
9 double numero3;
10

57
Sezione 2 - Primi elementi di programmazione

11 //messaggio per lÕutente


12 cout << "Inserisci 3 numeri => ";
13
14 //assegna alle variabili i valori letti da tastiera
15 cin >> numero1 >> numero2 >> numero3;
16
17 //scrivi le variabili
18 cout << "hai scritto " << endl << numero1 << endl << numero2 << endl
<< numero3 << endl;
19
20 //salta due righe
21 cout << endl << endl;
22 //arresta lÕesecuzione del programma
23 system ("pause");
24 //termina il programma
25 return 0;
26 }

Prova di esecuzione

Analisi del codice


La riga 15 contiene una chiamata del metodo cin simile a quelle degli esempi precedenti, in questo
caso seguita per˜ dai nomi di tre variabili, che hanno il compito di contenere i tre valori digitati dal-
lÕutente. Come si nota dalla prova di esecuzione, i tre valori sono scritti sulla stessa riga, separati da
uno spazio bianco.
LÕistruzione alla riga 18 (che • stata distribuita su due linee solamente per motivi di spazio) serve a
scrivere i tre valori su tre righe diverse: per questo dopo ogni valore • presente il carattere endl.
...........................................................................................................................................

4.2 Output formattato


Quando si visualizzano pi• dati numerici, ciascun dato viene visualizzato con il numero minimo di
cifre necessario per mostrarne il valore. Ci˜ comporta spesso un output poco ordinato.

Esempio Scrivi ....................................................................................................................


Scrivere i numeri seguenti: 0,01 ; 0,6 ; 30.

Codice
Scrivi.cpp
1 #include <iostream>
2 using namespace std;

58
Unitˆ didattica 4 - Visualizzazione e acquisizione

3 //INIZIO
4 int main ()
5 {
6 //dichiara variabili e assegna valori
7 double n1 = 0.01;
8 double n2 = 0.6;
9 double n3 = 30.8;
10
11 //scrivi
12 cout << n1 << endl;
13 cout << n2 << endl;
14 cout << n3 << endl;
15
16 //salta due righe
17 cout << endl << endl;
18 //arresta lÕesecuzione del programma
19 system ("pause");
20 //termina il programma
21 return 0;
22 }

Prova di esecuzione

Che confusione! Come si pu˜ vedere, i numeri non sono incolonnati in modo corretto.
...........................................................................................................................................

Per ovviare allÕinconveniente evidenziato nellÕesempio • necessario formattare lÕoutput, stabilendo,


per esempio, che ciascun numero deve occupare 8 caratteri e definendo una precisione di due cifre
per i numeri decimali.
Il manipolatore di stream setw permette di impostare la larghezza del successivo campo di output;
per esempio, se si vuole scrivere il numero allÕinterno di una colonna larga 8 caratteri occorre utiliz-
zare lÕistruzione riportata di seguito.
cout << setw(8);
Il metodo setw non produce alcun output: elabora semplicemente il flusso dei dati verso lo scher-
mo in modo da modificare la formattazione dellÕoutput del valore che segue. Per utilizzare i mani-
polatori di stream occorre includere nel programma il riferimento allÕheader iomanip, mediante lÕi-
struzione indicata di seguito.
#include <iomanip>
Si utilizza un altro manipolatore, setprecision, per impostare la precisione con cui visualizzare
le cifre decimali dei numeri a virgola mobile. Per esempio, lÕistruzione
cout << setprecision(2);
richiede che a video vengano indicate solo le prime due cifre decimali.

59
Sezione 2 - Primi elementi di programmazione

I manipolatori possono essere usati in contemporanea nelle istruzioni di output, come si vede nellÕi-
struzione che segue.
cout << setprecision(2) << setw(8) << x;
Questo comando visualizza il valore della variabile x entro un campo di larghezza 8 caratteri e con
una precisione di due cifre decimali.
NellÕesempio di output che segue • riportata la formattazione che avrebbe lÕoutput del valore Ð34,95
(ogni carattere Ò¥Ó corrisponde a uno spazio).
¥ ¥ -34.95
LÕimpostazione della precisione non influenza i campi interi. Sfortunatamente il metodo set-
precision non consente di visualizzare gli zeri non significativi: per esempio, il valore 0.1
sarebbe comunque visualizzato come 0.1, e non come 0.10. Per ottenere lÕintroduzione degli zeri
non significativi • necessario impostare il cosiddetto formato di visualizzazione f“sso, mediante il
metodo fixed, da chiamarsi come indicato di seguito.
cout << fixed;
Alcuni vecchi compilatori non supportano il manipolatore fixed: in questo caso si pu˜ utilizzare il
pi• oscuro comando indicato di seguito.
cout << setiosflags(ios::fixed);
La combinazione dei tre manipolatori appena visti riesce finalmente a farci ottenere il risultato di for-
mattazione desiderato. Ecco lÕistruzione di output completa.

cout << fixed << setprecision(2) << setw(8) << x;

Fortunatamente, i manipolatori setprecision e fixed devono essere utilizzati una sola volta: lo
stream, infatti, ricorda le impostazioni di formattazione anche per i successivi output. Al contrario,
la chiamata del metodo setw deve essere ripetuta per ciascuna istanza di cout, cio• per ciascun va-
lore da mostrare a video.

Esempio Tabula ..................................................................................................................


Scrivere i tre numeri dellÕesempio precedente, incolonnandoli a destra.

Codice
Tabula.cpp
1 #include <iostream>
2 #include <iomanip>
3 using namespace std;
4 //INIZIO
5 int main ()
6 {
7 //dichiara variabili e assegna valori
8 double n1 = 0.01;
9 double n2 = 0.6;
10 double n3 = 30.8;
11
12 //imposta numero cifre decimali
13 cout << setprecision(3);

60
Unitˆ didattica 4 - Visualizzazione e acquisizione

14
15 //imposta uso della virgola "fissa"
16 cout << fixed;
17
18 //imposta ampiezza totale
19 cout << setw(8);
20
21 //scrivi il valore
22 cout << n1 << endl;
23
24 //imposta ampiezza totale
25 cout << setw(8);
26
27 //scrivi il valore
28 cout << n2 << endl;
29
30 //imposta ampiezza totale
31 cout << setw(8);
32
33 //scrivi il valore
34 cout << n3 << endl;
35
36 //salta due righe
37 cout << endl << endl;
38 //arresta lÕesecuzione del programma
39 system ("pause");
40 //termina il programma
41 return 0;
42 }

Prova di esecuzione

Analisi del codice


La prima novitˆ si trova alla riga 2, dove viene richiamato lÕheader <iomanip> che contiene i me-
todi capaci di impostare la presentazione dei dati di input e output. UnÕaltra particolaritˆ si trova al-
la riga 13, dove con il metodo setprecision viene indicato il numero di cifre decimali che deve
essere visualizzato al momento della scrittura delle variabili di tipo double, mentre alla riga 15, me-
diante il metodo fixed, si specifica che la scrittura dei dati di tipo double deve avvenire con il cri-
terio della virgola fissa.
Alle righe 19, 25 e 31, con il metodo setw, viene impostata la lunghezza complessiva in caratteri che
i dati devono occupare: nellÕesempio viene impostata una lunghezza di otto caratteri di ingombro
per ogni numero scritto. Si noti che i metodi setprecision e fixed vengono utilizzati una vol-
ta sola allÕinizio del programma, mentre il metodo setw deve essere specificato prima di ogni istru-
zione cout << ....
...........................................................................................................................................

61
Sezione 2 - Primi elementi di programmazione

4.3 Caratteri di escape


A conclusione del discorso sulla formatta- CARATTERE SIGNIFICATO
zione dell’output non si potevano non
\a Segnale sonoro
menzionare quei caratteri particolari che
possono essere utilizzati nelle istruzioni di \r Ritorno a capo
output, e che spesso sono di grande aiuto \v Tabulazione verticale
nella formattazione dell’output: si tratta dei \f Avanzamento modulo (pagina)
caratteri di escape. \b Backspace
Nella tabella a destra ne sono presentati al- \n A capo (nuova riga)
cuni. E \t Tabulazione orizzontale
\\ Back slash
Come si nota dalla tabella ciascuno di que- \’ Virgoletta singola
sti caratteri speciali è preceduto da una bar- \” Virgolette doppie
ra rovesciata (back slash, in inglese), per in-
dicare appunto che si tratta di un carattere
di escape.
Invece di descrivere il significato di ogni singolo carattere, vediamone l’utilizzo con un breve pro-
gramma di esempio.

Esempio Escape .................................................................................................................

Codice
Escape.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //dichiara tre variabili intere e assegna i valori
7 int numero1 = 35;
8 int numero2 = 115;
9 int numero3 = 220;
10
11 /*emetti un suono \a, vai a capo \n e scrivi il valore
12 della variabile numero1*/
13 cout << "\a\nPrimo valore = " << numero1;
14
15 /*vai a capo due volte \n\n, inserisci una tabulazione orizzontale \t
16 e scrivi il valore della variabile numero2*/
17 cout << "\n\n\tSecondo valore = " << numero2;
18
19 /*vai a capo due volte \n\n e scrivi il valore della variabile numero3*/
20 cout << "\n\nTerzo valore = " << numero3;
21
22 //salta due righe
23 cout << endl << endl;
24 //arresta l’esecuzione del programma
25 system ("pause");
26 //termina il programma
27 return 0;
28 }

62
Unitˆ didattica 4 - Visualizzazione e acquisizione

Prova di esecuzione

Analisi del codice


Il codice appena presentato illustra come utilizzare i caratteri di escape nelle istruzioni di visualizzazione.
Nella riga 13 compaiono i due caratteri di escape \a e \n.
Il carattere \a non rappresenta unÕazione direttamente collegabile alla scrittura, ma fa in modo che
il computer emetta un ÒbeepÓche, in questo caso, ha solo scopo illustrativo, ma in un programma pi•
complesso potrebbe servire per segnalare la presenza, per esempio, di unÕincongruenza o di un er-
rore di immissione dei dati.
Il carattere \n ha invece un senso nellÕistruzione, dato che impone di saltare una riga e andare a ca-
po prima di visualizzare il messaggio che segue. Il numero di caratteri \n allÕinterno di unÕistruzio-
ne di stampa dipende da come il programmatore intende effettuare la visualizzazione dei risultati del-
lÕelaborazione.
Un altro carattere interessante compare nella riga 17, ed • il carattere \t, che • il cosiddetto carat-
tere di tabulazione.
Il carattere di tabulazione \t inserisce un certo numero di spazi prima di effettuare la scrittura sul
video.
NellÕesempio, gli spazi inseriti sono otto, come si vede nella prova di esecuzione.
Procedendo nella trattazione si presenterˆ ancora lÕoccasione di utilizzare questo carattere, per otte-
nere la visualizzazione tabellare dei risultati.
...........................................................................................................................................

4.4 Uso di Ò\\Ó e del carattere Ò@Ó


Nel paragrafo precedente abbiamo visto che i caratteri di escape sono sempre preceduti dalla barra
inversa Ò\Ó. A volte, per˜, pu˜ presentarsi il problema di dover scrivere a video una sequenza di ca-
ratteri in cui la barra inversa • parte integrante del testo da scrivere.
Per esempio, se allÕinterno di un programma si scrive
cout << ="c:\esempi\Error.txt";
il compilatore segnala un errore perchŽ interpreta come caratteri di escape le sequenze Ò \eÓ e Ò\EÓ,
che non riconosce come caratteri di escape validi.
Per ovviare a questo problema • sufficiente raddoppiare le barre inverse, quindi il modo corretto di
scrivere lÕistruzione precedente •:
cout << ="c:\\esempi\\Error.txt";
Un secondo metodo, pi• elegante, prevede che la stringa da visualizzare sia preceduta dal carattere
Ò@Ó. La nostra istruzione di esempio assume cos“ la forma indicata di seguito.
messaggio=@"c:\esempi\Error.txt";

63
Sezione 2 - Primi elementi di programmazione

Esercizi
Unità didattica 4

1 Le istruzioni seguenti contengono un errore di sintassi: individualo e correggilo.


int numero;
cout << "Inserisci un numero => ";
cin << numero;

2 Le istruzioni seguenti contengono un errore di sintassi: individualo e correggilo.


int numero;
cout << "Inserisci un numero => ";
cin >> " scrivi il numero " >> numero;

3 Supponendo che a fronte della richiesta del programma “Scrivi due numeri”, l’utente digiti
12 21

che cosa viene scritto a video dopo l’esecuzione delle istruzioni seguenti?

int numero1;
int numero2;
cout << "Inserisci due numeri => ";
cin >> numero1 >> numero2;
cout << numero2 << numero1;

4 Dire se è meglio scrivere:


cout << "Risultato =" << ris;

oppure

cout << "Risultato = " << ris;

e spiegare perché.

5 Dire se è meglio scrivere:


cout << "Risultato =" << ris << "\n";

oppure

cout << "Risultato = " << ris;

e spiegare perché.

6 Quale header bisogna inserire nel programma per usare le formattazioni setw, setprecision e
fixed?

7 Le due istruzioni indicate sotto vengono eseguite in sequenza. Che cosa compare a video?
cout << setw(8)<< 5 << endl;
cout << 5 << endl;

64
Unità didattica 4 - Visualizzazione e acquisizione

Esercizi
Unità didattica 4

8 Che cosa producono in output le seguenti istruzioni?


double num = 0.12345678e3;
cout << "\n" << setw(15)<< fixed << num;

9 Che cosa producono in output le seguenti istruzioni?


double num = 0.12345678e3;
cout << "\n" << setw(15)<< setprecision(5) << num;

q0 Quanti caratteri occupa in memoria la stringa "\n"?

qa Quanti caratteri occupa sul video la stringa "\n"?

qs Che cosa compare a video con la seguente istruzione?


cout << "abcd\aabcd";

qd Che cosa compare a video con la seguente istruzione?


cout << "\nabcd\refg";

qf Che cosa compare a video con la seguente istruzione?


cout << "\nabcd\tabcd";

qg Che cosa compare a video con la seguente istruzione?


cout << "abcd\babcd";

65
Unitˆ didattica
5
Operatori

CHE COSA IMPARERAI A FARE

$ Scrivere espressioni aritmetiche


$ Comporre gli operatori con lÕoperatore di assegnazione
$ Verificare relazioni
$ Creare espressioni logiche

CHE COSA DOVRAI STUDIARE

$ Simboli per gli operatori aritmetici


$ Sintassi degli operatori aritmetici composti
$ Simboli per gli operatori relazionali
$ Tabelle di veritˆ degli operatori logici
Unitˆ didattica 5 Operatori

A conclusione della trattazione sui vari tipi di dato disponibili in C++, vediamo quali operazioni arit-
metiche possono essere eseguite su di essi.

5.1 Operatori aritmetici


In C++ sono presenti (come in tutti i linguaggi) le operazioni fondamentali dellÕaritmetica, riassun-
te nella tabella che segue.

OPERAZIONE C++
Addizione +
Sottrazione Ð
Moltiplicazione *
Divisione /
Modulo (resto) %

Gli operatori riportati nella tabella vengono definiti operatori binari, perchŽ indi-
cano operazioni effettuabili su due operandi, che possono essere variabili o espres-
sioni.

Addizione e sottrazione
Il modo pi• semplice per eseguire la somma • scrivere lÕistruzione che segue.
somma = numero1 + numero2;
Questa istruzione somma il contenuto della prima variabile con il contenuto della seconda e il risul-
tato dellÕoperazione viene memorizzato nella variabile somma.
Fin qui niente di nuovo. Si possono anche sommare una variabile e un numero, come nellÕistruzio-
ne riportata di seguito.
somma = numero1 + 8;
Quanto indicato per la somma vale, chiaramente, anche per la sottrazione.
Bisogna per˜ fare attenzione ai tipi delle variabili che vengono utilizzate in unÕespressione.

Esempio Somma ................................................................................................................


Sommare due variabili di tipo int.

Codice
Somma.cpp
1 #include <iostream>
2 using namespace std;

67
Sezione 2 - Primi elementi di programmazione

3 //INIZIO
4 int main ()
5 {
6 //dichiara e valorizza la prima variabile intera
7 int numero1 = 235;
8
9 //dichiara la seconda variabile intera
10 int numero2 = 265;
11
12 //dichiara la variabile per il risultato
13 int somma;
14
15 //esegui l'operazione
16 somma = numero1 + numero2;
17
18 //scrivi il risultato
19 cout << "Somma = " << somma << endl;
20
21 //salta due righe
22 cout << endl << endl;
23 //arresta l'esecuzione del programma
24 system ("pause");
25 //termina il programma
26 return 0;
27 }

Prova di esecuzione

Analisi del codice


LÕunica riga significativa • la riga 16, nella quale viene eseguita lÕoperazione di somma e il risultato
viene assegnato alla variabile somma.
...........................................................................................................................................

Moltiplicazione
LÕoperazione di moltiplicazione viene effettuata con lÕausilio dellÕoperatore *. Per moltiplicare due
valori si utilizza la sintassi indicata di seguito.
prod = numero1 * numero2;
La variabile prod contiene il prodotto ottenuto dalla moltiplicazione delle due variabili numero1 e
numero2. Vediamo un esempio pratico.

Esempio Prodotto ...............................................................................................................


Moltiplicare due variabili dichiarate di tipo int.

68
Unitˆ didattica 5 - Operatori

Codice
Prodotto.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //dichiara e valorizza la prima variabile intera
7 int numero1 = 150;
8
9 //dichiara e valorizza la seconda variabile intera
10 int numero2 = 20;
11
12 //dichiara la variabile per il risultato
13 int prodotto;
14
15 //esegui l'operazione
16 prodotto = numero1 * numero2;
17
18 //scrivi il risultato
19 cout << "prodotto = " << prodotto << endl;
20
21 / salta due righe
22 cout << endl << endl;
23 //arresta l'esecuzione del programma
24 system ("pause");
25 //termina il programma
26 return 0;
27 }

Prova di esecuzione

Analisi del codice


Il codice • molto simile al precedente; cambia solo la riga 16, che • diventata una moltiplicazione.
...........................................................................................................................................

Divisione
Per effettuare una divisione si utilizza lÕoperatore /. Per esempio, lÕistruzione
val = numero1 / numero2;
memorizza nella variabile val il risultato della divisione indicata a destra dellÕoperatore =. Se i due
operandi sono di tipo intero il risultato della divisione • anchÕesso di tipo int, mentre se uno dei due
operandi • di tipo double il risultato • di tipo double.
Vediamo un esempio pratico.

69
Sezione 2 - Primi elementi di programmazione

Esempio Divisione ..............................................................................................................


Eseguire la divisione tra due variabili di tipo int.

Codice
Divisione.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //dichiara e valorizza la prima variabile intera
7 int numero1 = 150;
8
9 //dichiara e valorizza la seconda variabile intera
10 int numero2 = 20;
11
12 //dichiara la variabile per il risultato
13 int divisione;
14
15 //esegui l'operazione
16 divisione = numero1 / numero2;
17
18 //scrivi il risultato
19 cout << "divisione = " << divisione << endl;
20
21 //salta due righe
22 cout << endl << endl;
23 //arresta l'esecuzione del programma
24 system ("pause");
25 //termina il programma
26 return 0;
27 }

Prova di esecuzione

Analisi del codice


Le uniche modifiche al codice, rispetto a quello precedente, riguardano la sola riga 16, dove viene
utilizzato lÕoperatore di divisione.
...........................................................................................................................................

Come si nota nella figura, il risultato non • quello che ci si aspettava: lÕutilizzo di operandi tutti di ti-
po int ha prodotto infatti un troncamento della parte decimale, visualizzando come risultato Ò7Ó
(anzichŽ il valore corretto Ò7,5Ó). Per ottenere il risultato giusto • indispensabile che la variabile che
contiene il risultato della divisione sia dichiarata di tipo double e che almeno una delle altre due

70
Unitˆ didattica 5 - Operatori

(numero1 e/o numero2) sia dichiarata come di tipo double. Vediamo la dimostrazione pratica di
quanto detto, utilizzando alcune variabili di tipo double.

Esempio Divisione_double .................................................................................................

Codice
Divisione_double.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //dichiara e valorizza la prima variabile intera
7 int numero1 = 150;
8
9 //dichiara e valorizza la seconda variabile intera
10 double numero2 = 20;
11
12 //dichiara la variabile per il risultato
13 double divisione;
14
15 //esegui l'operazione
16 divisione = numero1 / numero2;
17
18 //scrivi il risultato
19 cout << "divisione = " << divisione;
20
21 //salta due righe
22 cout << endl << endl;
23 //arresta l'esecuzione del programma
24 system ("pause");
25 //termina il programma
26 return 0;
27 }

Prova di esecuzione

Analisi del codice


Nel codice compaiono due variabili dichiarate di tipo double, rispettivamente la variabile numero2
e la variabile divisione che contiene il risultato. Alla riga 16, lÕespressione a destra dellÕoperatore
di assegnazione = contiene due variabili di tipo differente: la prima (numero1) • di tipo int e la se-
conda (numero2) • di tipo double. In fase di compilazione, per rendere compatibile il risultato con
il tipo della variabile divisione (che • di tipo double), la variabile numero1 viene per cos“ dire
ÒpromossaÓ al tipo double, dando luogo al calcolo del risultato corretto.
...........................................................................................................................................

71
Sezione 2 - Primi elementi di programmazione

Operatore modulo
Le quattro operazioni appena studiate consentono di effettuare calcoli di qualsiasi genere ma, a
volte, pu˜ essere necessario conoscere solo il resto di una divisione. A questo scopo il C++ mette
a disposizione lÕoperatore modulo, rappresentato dal simbolo %. Esso viene utilizzato come lÕo-
peratore di divisione /, solo che il risultato dellÕoperazione • il resto del quoziente. Per esempio,
lÕistruzione
resto = numero1 % numero2;
consente di memorizzare nella variabile resto il resto della divisione indicata a destra dellÕoperatore =.

Esempio Resto ....................................................................................................................


Calcolare il resto della divisione tra due interi.

Codice
Resto.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //dichiara e valorizza la prima variabile intera
7 int numero1 = 150;
8
9 //dichiara e valorizza la seconda variabile intera
10 int numero2 = 20;
11
12 //dichiara la variabile per il risultato
13 int resto;
14
15 //esegui l'operazione
16 divisione = numero1 % numero2;
17
18 //scrivi il risultato
19 cout << "resto = " << resto;
20
21 //salta due righe
22 cout << endl << endl;
23 //arresta l'esecuzione del programma
24 system ("pause");
25 //termina il programma
26 return 0;
27 }

Prova di esecuzione

72
Unitˆ didattica 5 - Operatori

Analisi del codice


LÕesempio segue la struttura dei precedenti, • cambiata solo la solita istruzione di riga 16 nella qua-
le compare lÕoperatore modulo. Alla variabile numero1 • stato assegnato il valore 150, mentre alla
variabile numero2 • stato assegnato il valore 20. Si sa che il 20 nel 150 sta 7 volte con il resto di 10,
che • proprio quello che viene memorizzato nella variabile resto.
...........................................................................................................................................

5.2 Operatori aritmetici composti


Molto spesso si ha la necessitˆ di sommare una certa quantitˆ a una variabile: per esempio, per mag-
giorare il valore della variabile var di 25 unitˆ si deve scrivere lÕistruzione indicata di seguito, che ov-
viamente • una assegnazione valida.
var = var + 25;
Il significato dellÕistruzione • il seguente: al contenuto della variabile var viene sommato il valore
25. In una istruzione del genere possono essere utilizzati tutti gli operatori aritmetici visti finora.
NellÕesempio viene utilizzato il numero 25, ma al suo posto pu˜ trovarsi una variabile, come nellÕi-
struzione che segue, che significa Òal contenuto della variabile var deve essere sommato il valore
contenuto nella variabile numÓ.
var = var + num;
Il linguaggio C++ consente di scrivere operazioni di questo tipo in maniera pi• sintetica ed elegante,
utilizzando i cosiddetti operatori aritmetici composti.
La prima istruzione viene modificata come segue.
var += 25;
LÕoperatore += • stato posto tra la variabile e la quantitˆ da aggiungere a essa: il suo significato • quel-
lo descritto prima. La stessa cosa avviene per la seconda istruzione, che si trasforma come segue.
var += num;
Nella tabella sottostante sono riassunti tutti gli operatori composti.

OPERATORE ESEMPIO ISTRUZIONE EQUIVALENTE


+= var += 25; var = var + 25;
-= var -= 87; var = var - 87;
*= var *= 3; var = var * 3;
/= var /= 2; var = var / 2;
%= var %= 5; var = var % 5;

Esempio Imponibile ...........................................................................................................


Applicare lÕIVA del 20% allÕimporto, espresso in euro, inserito da tastiera.

Codice
Imponibile.cpp
1 #include <iostream>
2 using namespace std;

73
Sezione 2 - Primi elementi di programmazione

3 //INIZIO
4 int main ()
5 {
6 //definisci la variabile importo
7 double importo;
8
9 //definisci la variabile IVA
10 double IVA;
11
12 //definisci la costante per la percentuale IVA
13 const int PERC=20;
14
15 //richiedi il valore dell'importo
16 cout << "\nInserisci l'importo ";
17
18 //leggi importo
19 cin >> importo;
20
21 //calcola l'IVA sull'importo
22 //N.B.: l'uso delle parentesi in questo caso • facoltativo
23 IVA = (importo*PERC)/100;
24
25 //aggiungi IVA
26 importo += IVA;
27
28 //scrivi l'importo con IVA
29 cout << "\nImporto con IVA = " << importo;
30
31 //salta due righe
32 cout << endl << endl;
33 //arresta l'esecuzione del programma
34 system ("pause");
35 //termina il programma
36 return 0;
37 }

Prova di esecuzione

Analisi del codice


Sono state dichiarate due variabili di tipo double, rispettivamente alla riga 7 e alla riga 10: la prima
contiene lÕimporto in euro inserito da tastiera, la seconda lÕIVA relativa allÕimporto inserito.
Alla riga 13 • stata dichiarata una costante, nella quale viene memorizzata la percentuale dellÕIVA da
utilizzarsi nel calcolo dellÕimponibile.
Alla riga 26 • stato usato lÕoperatore composto +=, per sommare allÕimporto inserito lÕIVA calcolata
nellÕistruzione precedente.
...........................................................................................................................................

74
Unitˆ didattica 5 - Operatori

Operatori unari
Il C++, oltre a mettere a disposizione gli operatori binari visti in precedenza, dispone di particolari
operatori utilizzabili su una singola variabile, i quali permettono lÕincremento (o il decremento) di
una unitˆ del valore contenuto nella variabile: si tratta degli operatori unari.
Per esempio, per incrementare la variabile numero si utilizza lÕoperatore di incremento ++, allÕin-
terno del costrutto
++numero;
che equivale allÕistruzione indicata di seguito.
numero = numero + 1;
Il medesimo concetto • da applicarsi al decremento di una variabile, mediante lÕoperatore di decre-
mento --. LÕistruzione
--numero;
equivale a quella indicata di seguito.
numero = numero - 1;

Esempio Unario ..................................................................................................................


Incrementare e decrementare due variabili intere.

Codice
Unario.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //dichiara due variabili intere
7 //e ponile = 0
8 int num1=0;
9 int num2=0;
10
11 //scrivi i loro valori iniziali
12 cout << "\n\nVALORI INIZIALI\n";
13 cout << "num1 = " << num1 << "\n";
14 cout << "num2 = " << num2 << "\n";
15
16 //incrementa la prima
17 ++num1;
18
19 //decrementa la seconda
20 --num2;
21
22 //scrivi i loro valori finali
23 cout << "\n\nVALORI FINALI\n";
24 cout << "num1 = " << num1 << "\n";
25 cout << "num2 = " << num2 << "\n";
26
27 //salta due righe

75
Sezione 2 - Primi elementi di programmazione

28 cout << endl << endl;


29 //arresta l'esecuzione del programma
30 system ("pause");
31 //termina il programma
32 return 0;
33 }

Prova di esecuzione

Analisi del codice


Nel codice vengono utilizzati gli operatori unari, rispettivamente alla riga 17, per incrementare la va-
riabile num1 di una unitˆ, e alla riga 20, per decrementare la variabile num2 di una unitˆ.
Infine, alle righe 12, 13, 14 vengono scritti i valori iniziali e, per confronto, alle righe 23, 24, 25 ven-
gono visualizzati i valori finali.
...........................................................................................................................................

Operatori unari pre-fissi e post-fissi


LÕuso degli operatori unari • molto pratico, perchŽ consente di scrivere lÕistruzione in modo sinteti-
co. NellÕesempio appena visto sono stati utilizzati operatori che precedono la variabile, e che perci˜
vengono chiamati operatori pre-fissi.
Tali operatori possono essere utilizzati anche allÕinterno di unÕistruzione di assegnazione di unÕaltra
variabile, come mostra la riga di esempio riportata di seguito.
num1 = ++num2;
Una simile istruzione consente di assegnare a num1 il valore della variabile num2 incrementato di una
unitˆ. Lo stesso discorso vale anche per lÕoperatore di decremento (--).
Il C++ permette anche di utilizzare gli operatori unari come operatori post-fissi, da collocare dopo
la variabile su cui devono operare, come nel costrutto num1++;.
Utilizzato cos“, lÕoperatore si comporta in maniera perfettamente identica a quello pre-fisso: infatti, se
nellÕesempio ÒUnarioÓ si prova a cambiare le istruzioni di riga 16 e riga 22, sostituendole rispettiva-
mente con le istruzioni num1++; e num2--;, il risultato del programma non cambia.
Diverso • invece il discorso per le operazioni di assegnazione. NellÕistruzione di esempio precedente, in
cui veniva eseguita lÕassegnazione num1 = ++num2;, lÕincremento viene effettuato prima di assegna-
re il nuovo valore alla variabile num1, mentre nellÕanaloga istruzione di assegnazione con operatore
post-fisso (num1 = num2++;) prima viene assegnato il valore di num2 alla variabile num1 e poi viene
effettuato lÕincremento della variabile num2.
Una tale situazione deve essere presa in seria considerazione in fase di programmazione, in quanto si
potrebbero ottenere risultati non desiderati.
Vediamo un esempio pratico.

76
Unitˆ didattica 5 - Operatori

Esempio Pre_e_post ...........................................................................................................


Verificare la differenza tra operatori pre-fissi e post-fissi.

Codice
Pre_e_post.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //dichiara e inizializza una variabile intera
7 int num = 10;
8
9 //incremento post-fisso
10 cout << "num con incremento post-fisso = " << num++ << "\n";
11
12 //ripristina num
13 num = 10;
14
15 //incremento pre-fisso
16 cout << "num con incremento pre-fisso = " << ++num <<"\n";
17
18 //salta due righe
19 cout << endl << endl;
20 //arresta l'esecuzione del programma
21 system ("pause");
22 //termina il programma
23 return 0;
24 }

Prova di esecuzione

Analisi del codice


Alla riga 10 la variabile num viene prima scritta e poi incrementata; alla riga 16 la variabile num viene
prima incrementata e poi scritta.
...........................................................................................................................................

Alla luce dei risultati dellÕesempio, si pu˜ affermare quanto segue.

Se lÕoperatore di incremento (o decremento) agisce come operatore pre-fisso il va-


lore della variabile cui • applicato viene incrementato (o decrementato) prima di
ogni altra operazione, mentre se agisce come operatore post-fisso il valore viene in-
crementato (o decrementato) dopo ogni altra operazione.

77
Sezione 2 - Primi elementi di programmazione

é importante tenere bene a mente queste differenze operative per non incappare in errori che, a vol-
te, possono risultare difficili da scovare.

5.3 Operatori relazionali


Durante la scrittura di un programma capita spesso di dover eseguire un blocco di codice in base al
risultato di unÕoperazione di controllo eseguita su alcuni valori.

LÕoperazione di controllo mette a confronto due operandi utilizzando i cosiddetti ope-


ratori relazionali, detti anche operatori di confronto.

Il risultato di unÕoperazione di confronto pu˜ assumere solo due valori, quindi • di tipo booleano:
vero o falso (True o False).
Il C++ mette a disposizione gli operatori relazionali indicati nella tabella che segue.

OPERATORE DESCRIZIONE
== Uguale a
< Minore di
> Maggiore di
<= Minore o uguale a
>= Maggiore o uguale a
!= Diverso

Operatore di uguaglianza (==)


LÕoperatore di uguaglianza consente di verificare se i due operandi sono uguali.
Un esempio • lÕistruzione
bool test = num1==num2;
in cui alla variabile test viene assegnato il risultato dellÕoperazione di confronto num1==num2.

Esempio Test.......................................................................................................................
Verificare se due numeri acquisiti da tastiera sono uguali.

Codice
Test.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //definisci due variabili intere
7 int num1;
8 int num2;
9
10 //dichiara una variabile per il confronto
11 bool test;
12

78
Unitˆ didattica 5 - Operatori

13 //chiedi e leggi il primo numero


14 cout << "\nInserisci il primo numero ";
15 cin >> num1;
16
17 //chiedi e leggi il secondo numero
18 cout << "\nInserisci il secondo numero ";
19 cin >> num2;
20
21 //esegui il confronto
22 test = num1==num2;
23
24 //scrivi il risultato
25 cout << "\n\ntest = " << test << "\n";
26
27 //salta due righe
28 cout << endl << endl;
29 //arresta l'esecuzione del programma
30 system ("pause");
31 //termina il programma
32 return 0;
33 }

Prove di esecuzione

Nella prima esecuzione i due numeri inseriti sono diversi e il contenuto della variabile booleana
test risulta essere 0 (che corrisponde a false), mentre nella seconda prova i due numeri digitati
sulla tastiera sono uguali e, quindi, test contiene 1 (true).

Analisi del codice


Alle righe 7 e 8 sono state dichiarate due variabili di tipo int, in cui vengono memorizzati i valori
letti dalla tastiera (righe 15 e 19).

79
Sezione 2 - Primi elementi di programmazione

Alla riga 11 • stata dichiarata la variabile test di tipo bool, che contiene il risultato (booleano) del
confronto tra num1 e num2 (num1==num2;).
LÕistruzione test = num1==num2; verifica, tramite lÕoperatore di uguaglianza, che i due valo-
ri inseriti siano uguali e memorizza il risultato nella variabile booleana test. Infine, alla riga 25
viene visualizzato il risultato del confronto.
...........................................................................................................................................

Operatore minore di (<)


Questo operatore consente di verificare se il primo operando • minore del secondo.
Un esempio • lÕistruzione bool test = num1 < num2;.

Operatore maggiore di (>)


Questo operatore consente di verificare se il primo operando • maggiore del secondo.
Un esempio • lÕistruzione bool test = num1 > num2;.

Operatore minore o uguale a (<=)


Questo operatore consente di verificare se il primo operando • minore o uguale al secondo.
Un esempio • lÕistruzione bool test = num1 <= num2;.

Operatore maggiore o uguale a (>=)


Per questo tipo di operatore vale il discorso inverso rispetto allÕoperatore precedente (<=), in quan-
to esso verifica che il primo operando sia maggiore o uguale al secondo.
Un esempio • lÕistruzione bool test = num1 >= num2;.

Operatore di disuguaglianza (Òdiverso daÓ, !=)


Questo operatore verifica se i due operandi sono diversi.
Un esempio • lÕistruzione bool test = num1!= num2;.

5.4 Operatori logici


Con gli operatori relazionali si • in grado di verificare il risultato di un test applicato a due ope-
randi. Tuttavia, non • rara la necessitˆ di confrontare non due singoli operandi, ma due espressioni
booleane. Le espressioni booleane sono quelle utilizzate nei paragrafi precedenti a proposito degli
operatori relazionali. Una tipica espressione booleana • quella riportata di seguito.
(num1>=num2);
Il confronto, racchiuso tra parentesi tonde, fornisce infatti come risultato true o false.

Gli operatori logici operano su due o pi• espressioni booleane.

C++ mette a disposizione gli operatori logici riportati nella tabella che segue.

OPERATORE DESCRIZIONE
& AND (completo)
&& AND (cortocircuito)
| OR (completo)
|| OR (cortocircuito)
! NOT (negazione)
^ XOR

80
Unitˆ didattica 5 - Operatori

Operatore AND completo (&)


Questo operatore consente di verificare la contemporaneitˆ di due (o pi•) condizioni e solo se en-
trambe le condizioni (operandi o espressioni booleane) risultano true, il risultato • true, come
mostrato nella seguente tabella di veritˆ.
OP1 OP2 OP1 & OP2
false false false
false true false
true false false
true true true

Esempio TestAnd ................................................................................................................


Verificare che un numero letto da tastiera rientri nellÕintervallo che va da 10 a 100.
La prima condizione • che il numero deve essere maggiore o uguale al limite inferiore, cio• 10; tra-
dotto in codice, significa che deve essere vera lÕespressione booleana (num1>=10).
La seconda condizione • che il numero deve essere anche minore o al massimo uguale a 100; lÕe-
spressione tradotta in codice • (num1<=100).
Ora, ricorrendo allÕoperatore logico & • possibile creare lÕistruzione
bool test = (num1>=10) & (num1<=100);
in cui la variabile test contiene il risultato della valutazione della contemporaneitˆ delle due espressio-
ni: test sarˆ true se entrambe le condizioni sono verificate, false se almeno una delle due • false.

Codice
TestAnd.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //dichiara una variabile intera
7 int num;
8
9 //dichiara una variabile per il test
10 bool test;
11
12 //chiedi e leggi un numero
13 cout << "\nInserisci un numero ";
14 cin >> num;
15
16 //esegui il test
17 test = (num>=10) & (num<=100);
18
19 //scrivi il risultato
20 cout << "\n\nTest = "<< test << "\n";
21
22 //salta due righe
23 cout << endl << endl;
24 //arresta l'esecuzione del programma
25 system ("pause");
26 //termina il programma
27 return 0;
28 }

81
Sezione 2 - Primi elementi di programmazione

Prova di esecuzione

Analisi del codice


Alla riga 17 viene eseguito il test con l’istruzione
test=(num1>=10)&(num1<=100);
che esamina l’espressione contenuta nella prima coppia di parentesi tonde, poi quella nella seconda
coppia di parentesi e infine, tramite l’operatore &, le mette in relazione logica tra loro.
Nella figura si vede che il contenuto di test è 1 (true) perché il numero inserito da tastiera è com-
preso nell’intervallo 1-100.
...........................................................................................................................................

Per concludere, come è descritto nella tabella di verità, l’uso dell’operatore & consente di avere co-
me risultato true solo se entrambe le espressioni booleane sono true.
L’operatore logico & viene anche definito operatore logico di valutazione completa, in quanto
può mettere in relazione tutte le diverse espressioni booleane.

Operatore AND di cortocircuito (&&)


Pur essendo fondamentalmente identico all’operatore &, questo operatore si differenzia per un aspet-
to che riguarda la valutazione delle espressioni.
Come si è visto, l’operatore & valuta tutte le espressioni booleane presenti, mentre l’operatore &&
valuta solo la prima e solo se questa risulta verificata procede con la valutazione della seconda e co-
sì via. Quindi, è sufficiente che la prima espressione risulti false per far sì che il controllo non pro-
ceda con le successive valutazioni: infatti, basta che la prima condizione non sia verificata per otte-
nere dall’AND un risultato false (come appare chiaro osservando la tabella di verità dell’opera-
tore &).
Riprendendo il listato dell’esempio precedente, si può agevolmente sostituire l’istruzione di riga 15
(test=(num1>=10)&(num1<=100);) con l’istruzione
test=(num1>=10)&&(num1<=100);
che dà il medesimo risultato a tutto vantaggio della velocità di esecuzione, dato che se il primo test
risulta non verificato non si procede alla valutazione della seconda espressione (il risultato sarebbe in
ogni caso false). Tale tecnica di valutazione viene definita di cortocircuito.

Operatore OR ( | )
Questo operatore valuta due espressioni booleane e re- OP1 OP2 OP1 | OP2
stituisce true se almeno una di esse è true. Dalla ta-
bella di verità riportata a destra si evince che solo se en- false false false
trambe le espressioni risultano non verificate (false) il false true true
risultato del confronto è false, mentre è true in tutti true false true
gli altri casi. E true true true

82
Unitˆ didattica 5 - Operatori

Esempio TestOr ...................................................................................................................


Verificare la terza riga della tabella di veritˆ dellÕoperatore OR.

Codice
TestOr.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //dichiara due variabili booleane e assegna due valori opposti
7 bool OP1 = true;
8 bool OP2 = false;
9
10 //visualizza i valori delle variabili
11 cout << "\nOperatore 1 = " << OP1;
12 cout << "\nOperatore 2 = " << OP2;
13
14 //scrivi il risultato
15 cout << "\n\nOP1 or OP2 = " << (OP1 | OP2);
16
17 //salta due righe
18 cout << endl << endl;
19 //arresta l'esecuzione del programma
20 system ("pause");
21 //termina il programma
22 return 0;
23 }

Prova di esecuzione

Analisi del codice


Alla righe 7 e 8 sono state dichiarate le due variabili di tipo booleano test1 e test2, alle quali ven-
gono loro assegnati due valori booleani opposti.
Alla riga 18 viene visualizzato il risultato del test.
Si noti lÕutilizzo dellÕespressione direttamente nellÕistruzione di stampa, anzichŽ definire unÕaltra va-
riabile.
...........................................................................................................................................

83
Sezione 2 - Primi elementi di programmazione

Operatore OR di cortocircuito (||)


Questo operatore valuta la prima espressione e, se questa risulta true, non ha bisogno di valutare la
seconda perchŽ comunque il risultato sarˆ true; se invece la prima espressione risulta false, allora •
costretto a valutare anche la seconda. Riprendendo il codice precedente, si pu˜ sostituire lÕespressione
test1 | test2
contenuta nellÕistruzione di stampa alla riga 18 con lÕespressione indicata di seguito.
test1 || test2
Il risultato non cambia.
In questo caso, la valutazione del secondo operando test2 non viene effettuata, perchŽ qualsiasi sia
il suo valore il risultato del confronto • sempre true.

Operatore NOT (!)


Questo operatore • definito, in gergo, operatore di negazione. Esso inverte il valore booleano del
suo operando, come risulta chiaro dalla sua tabella di veritˆ.

OP1 OP1
false true
true false

LÕoperando pu˜ essere una singola variabile oppure unÕespressione booleana, come si pu˜ vedere
nelle due istruzioni di esempio riportate di seguito.
bool test1 = !test2;
bool test1= !(num1>=num2);

Operatore XOR (^)


Questo operatore booleano restituisce true se i valori degli operandi sono diversi tra loro; di segui-
to • riportata la sua tabella di veritˆ.

OP1 OP2 OP1 ^ OP2


false false false
false true true
true false true
true true false

Esempio TestXor .................................................................................................................


Verificare la quarta riga della tabella di veritˆ dellÕoperatore XOR.

Codice
TestXor.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //dichiara due variabili booleane e assegna due valori uguali

84
Unitˆ didattica 5 - Operatori

7 bool OP1 = true;


8 bool OP2 = true;
9
10 //visualizza i valori delle variabili
11 cout << "\nOperatore 1 = " << OP1;
12 cout << "\nOperatore 2 = " << OP2;
13
14 //scrivi il risultato
15 cout << "\n\nOP1 xor OP2 = " << (OP1 ^ OP2);
16
17 //salta due righe
18 cout << endl << endl;
19 //arresta l'esecuzione del programma
20 system ("pause");
21 //termina il programma
22 return 0;
23 }

Prova di esecuzione

Analisi del codice


Il risultato della figura conferma quanto evidenziato nella tabella di veritˆ dellÕoperatore XOR: infat-
ti, le due variabili sono state entrambe valorizzate a true e quindi, essendo i valori identici, il risul-
tato dellÕoperazione • false.
...........................................................................................................................................

85
Sezione 2 - Primi elementi di programmazione

Esercizi
Unità didattica 5

1 Scrivere in C++ le espressioni matematiche riportate di seguito.

s = s0 + v0t + –21 gt2


a
G = 4π2 –––––––––––––
p2 (m1 + m2)

2 Scrivere le seguenti espressioni C++ utilizzando la notazione matematica.


dm = m * (1 + v / e) / (l - v / e) ;
volume = 3.14 * r * r * h;
volume = 4 * 3.14* r*r*r/ 3;
p = z/(x * x + y * y)) ;

3 Che cosa c'è di sbagliato nella seguente versione della formula risolutiva dell'equazione di secondo grado?
xl = (-b - sqrt(b*b-4*a*c))/2*a;
x2 = (-b + sqrt(b*b-4*a*c))/2*a;
// sqrt() = radice quadrata

4 Date le istruzioni:
int n;
double x = 5.5;
n = x;

quale segnalazione di errore dà il compilatore?

5 Come possono essere corrette le istruzioni dell’esercizio precedente?

6 Che cosa contiene la variabile z dopo le seguenti istruzioni?


double x = 2.5;
double y = -1.5;
int n = 4 ;
double z;
z=(x+n*y-x+n)*y;

7 Che cosa contiene la variabile z dopo le seguenti istruzioni?


int m = 18;
int n = 4 ;
int z;
z = m/n+m%n;

8 Che cosa contiene la variabile z dopo le seguenti istruzioni?


double x = 2.5;
int n = 4 ;
double z;
z = 5*x-n/5;

86
Unità didattica 5 - Operatori

Esercizi
Unità didattica 5

9 Che cosa contiene la variabile z dopo le seguenti istruzioni?


int n = 4 ;
int z;
z=1 - (1 - (1 - (1 - (1 - n) ) ) )

q0 Scrivere un programma che visualizzi il quadrato e il cubo dei numeri da 1 a 5.

qa Scrivere un programma che richieda all'utente due valori interi, e che calcoli e visualizzi:
: la somma;
: la differenza;
: il prodotto;
: la media.

qs Qual è il risultato della seguente espressione: 15 % 4?

qd Quali sono gli operatori relazionali?

qf Date le istruzioni seguenti:


int numero1;
int numero2;
bool test=(numero1<=numero2);

quanto vale test per i valori di numero1 e numero2 indicati nella tabella?

NUMERO1 NUMERO2 TEST


3 5
3 3
5 3

qg Qual è il carattere che viene utilizzato per l’operazione AND (completa)?

qh Qual è il carattere che viene utilizzato per l’operazione AND di cortocircuito?

qj Qual è il carattere che viene utilizzato per l’operazione OR (completo)?

qk Qual è il carattere che viene utilizzato per l’operazione OR di cortocircuito?

ql Nel seguente codice, che controlla se un numero è compreso in un intervallo tra 25 e 90, c’è un errore
logico: quale?

cout << "\nInserisci il numero ";


cin >> num1;
test=(num1>25)&(num1>90);

w0 Dare il resto. Implementare un programma che aiuti un cassiere a stabilire il resto dovuto. Il programma
prevede due dati in input: la somma dovuta e la quantità di denaro ricevuta dal cliente. Il programma cal-
cola la differenza e stabilisce gli euro da restituire al cliente.

87
Sezione 2 - Primi elementi di programmazione

Esercizi
Unità didattica 5

wa Date le istruzioni seguenti:


int n1;
int n2;
bool test=(numero1==numero2)|( numero1<numero2);

quanto vale test per valori di n1 e n2 indicati nella tabella?

N1 N2 TEST
3 5
3 3
5 3

ws Date le istruzioni seguenti:


int n1;
int n2;
bool test=(numero1==numero2)&( numero1<numero2);

quanto vale test per valori di n1 e n2 indicati nella tabella?

N1 N2 TEST
3 5
3 3
5 3

wd Date le istruzioni seguenti:


int n1;
int n2;
bool test=(numero1==numero2)^( numero1<numero2);

quanto vale test per valori di n1 e n2 indicati nella tabella?

N1 N2 TEST
3 5
3 3
5 3

wf Che cosa scrive in output il frammento di codice seguente (le variabili sono tutte di tipo int)?
x = 2;
y = x + x;
t = y + x;
cout << t;

88
Sezione 3
Organizzazione
degli algoritmi

Obiettivi ◊ Strutturare un algoritmo in modo logicamente corretto
◊ Applicare i principi base della programmazione
strutturata
◊ Trasferire nel linguaggio di programmazione
le strutture fondamentali di un algoritmo

& Questa sezione contiene


U.D. 6
U.D. 7
Algoritmi e pseudocodifica
Istruzioni di selezione
U.D. 8 Istruzioni di ripetizione
U.D. 9 Le funzioni
Unitˆ didattica
6
Algoritmi
e pseudocodifica
CHE COSA IMPARERAI A FARE

$ Analizzare un problema per la costruzione di un algoritmo


$ Realizzare i primi semplici algoritmi
$ Istruzioni di assegnamento, di ingresso e di uscita dei dati
$ Applicare i principi della programmazione strutturata
$ Scrivere un algoritmo utilizzando la pseudocodifica

CHE COSA DOVRAI STUDIARE

$ Definizione generale di algoritmo


$ Forma delle istruzioni di assegnamento, di input
e di output in pseudocodifica
$ Teorema di Jacopini-Bšhm
$ Pseudocodifica delle strutture di sequenza,
alternativa e ripetizione
Unità didattica
Algoritmi e pseudocodifica
6
6.1 Analisi del problema
Ognuno di noi, nel corso della giornata, incontra molti problemi, semplici o complessi, da risolvere.
In questa sede vogliamo trattare solo una categoria di problemi e, precisamente, quelli i cui risultati
si ottengono mediante l’elaborazione di un numero sufficiente di informazioni.
Sono, per esempio, problemi della categoria considerata:
: scomporre in fattori primi un numero naturale;
: stabilire, su una cartina stradale, il percorso più breve tra Mantova e Milano;
: accendere il televisore.

Non sono problemi della classe considerata:

: stabilire qual è la regione più bella d’Italia;


: stabilire quale squadra di calcio della serie A vincerà lo scudetto del prossimo campionato.

Infatti, il primo tipo di problema richiede il possesso di conoscenze intuitive non formalizzabili,
mentre il secondo non fornisce un numero sufficiente di informazioni iniziali.

Risolvere un problema significa compiere delle azioni (elaborazione) su oggetti (di


natura concreta o astratta), detti dati iniziali, modificandone lo stato per raggiungere
lo scopo voluto (ossia i risultati o dati finali).

Colui che si propone di risolvere il problema, cioè il risolutore, deve svolgere un’attività creativa nel-
la ricerca della soluzione al problema e, avvalendosi di un patrimonio di conoscenze precedente-
mente acquisite, deve:
1. interpretare l’enunciato del problema;
2. evidenziare i dati che l’enunciato fornisce (dati iniziali);
3. ricercare tutte le azioni (operazioni) da compiere sui dati e collegarle logicamente in funzione
del raggiungimento dei risultati o dati finali;
4. eseguire nell’ordine le operazioni descritte al punto 3;
5. verificare i risultati.

L’attività che il risolutore svolge per realizzare i punti 1), 2), 3) prende il nome di analisi
del problema. Quando il risolutore realizza il punto 4), cioè quando esegue le azioni
descritte nel punto 3), assume il ruolo di esecutore.

Spesso, il risolutore e l’esecutore sono la stessa persona ma, a volte, non è così: può presentarsi il
caso che il risolutore demandi l’esecuzione della soluzione del problema a un’altra persona oppu-
re a un esecutore automatico quale, per esempio, un calcolatore elettronico. In questo caso, il ri-
solutore deve preparare un’accurata descrizione del procedimento risolutivo costituito da un in-
sieme di comandi da impartire all’esecutore. Tale descrizione è, naturalmente, condizionata dalle
caratteristiche dell’esecutore.

91
Sezione 3 - Organizzazione degli algoritmi

Nella figura è schematizzato il processo di soluzione di un problema descritto poco sopra.

Problema

Risolutore

Procedimento Analisi
risolutivo del problema

Dati iniziali Esecutore Risultati

Verifica dei risultati

Il risolutore deve sapere:

: quale linguaggio comprende l’esecutore;


: quali sono le azioni operative e logiche che l’esecutore sa eseguire.

La descrizione della risoluzione del problema prende il nome di algoritmo.

La risoluzione del problema deve soddisfare le seguenti condizioni:

1. contenere il nome di tutti gli oggetti da manipolare (dati iniziali);


2. contenere la descrizione di un insieme finito delle azioni effettivamente eseguibili che devono
operare sugli oggetti;
3. ogni azione descritta deve svolgersi in un tempo determinato, cioè con un inizio e una fine;
4. le azioni sono descritte in una sequenza logicamente strutturata e, quindi, devono essere spe-
cificate le eventuali condizioni affinché a un’azione ne segua una e non un’altra;
5. tutte le azioni necessarie devono essere presenti e ogni azione deve essere interpretabile in un
unico modo (completezza e univocità).

6.2 Primo esempio


Per chiarire quanto esposto, diamo un esempio di algoritmo che sia familiare a ognuno di noi.

Esempio Televisore.............................................................................................................
Algoritmo per l’uso di un apparecchio televisivo.

Pseudocodifica
1 INIZIO
2 oggetti da manipolare: televisore, telecomando;
3 premere il tasto di accensione;
4 selezionare un canale con il telecomando;

92
Unità didattica 6 - Algoritmi e pseudocodifica

5 se il programma del canale selezionato non è gradito,


6 allora ripetere l’azione 4
7 altrimenti assistere alla trasmissione;
8 se nessun programma è gradito o le trasmissioni sono terminate,
9 allora spegnere il televisore;
10 FINE

Analisi della pseudocodifica


Possiamo affermare che la soluzione del problema è un algoritmo perché sono rispettate
tutte e cinque le condizioni precedentemente elencate.
Infatti:
: contiene l’elenco di tutti gli oggetti da manipolare (dati iniziali): televisore e teleco-
mando;
: contiene la descrizione delle azioni da compiere sugli oggetti: queste azioni sono ese-
guibili e sono in numero finito;
: ogni azione descritta si svolge in un tempo determinato;
: la sequenza delle azioni da compiere è logicamente strutturata;
: l’algoritmo è esaustivo (contempla tutte le possibilità) e ogni istruzione è interpretabi-
le in modo univoco (senza ambiguità).
...........................................................................................................................................

Osserviamo che l’algoritmo descrive il comportamento che un esecutore deve tenere per risolvere il
problema.
Possiamo anche dire che è stata fatta la descrizione di un programma di comportamento, in cui le
azioni sono espresse in modo imperativo e rappresentano dei comandi; inoltre, sono presenti azioni
che vengono eseguite solo se si verificano determinate condizioni come, per esempio:
5 se il programma del canale selezionato non è gradito,
6 allora ripetere l’azione 4
7 altrimenti assistere alla trasmissione;
Frasi come questa descrivono delle azioni, dette di controllo, che impongono delle condizioni (pro-
gramma non gradito) il cui verifìcarsi o meno determina il comando di esecuzione di una sequenza
di azioni piuttosto che di un’altra.

I comandi di un algoritmo prendono anche il nome di istruzioni che vengono fornite


all’esecutore affinché egli sappia che cosa fare.

La frase:
premere il tasto di accensione
descrive, quindi, un comando o istruzione.
Le istruzioni sono state contrassegnate da numeri per poterle individuare meglio.
Le istruzioni contrassegnate dai numeri 1 e 10 avvertono l’esecutore quando inizia e quando termi-
na la sequenza delle istruzioni, rispettivamente.
La descrizione di una esecuzione dell’algoritmo da parte di un esecutore potrebbe essere la seguente:
1. l’esecutore preme il tasto di accensione;
2. seleziona il primo canale con il telecomando;
3. assiste alla trasmissione;
4. la trasmissione è terminata e l’esecutore spegne il televisore.

93
Sezione 3 - Organizzazione degli algoritmi

Dunque, mentre la descrizione dell’algoritmo è unica, l’esecutore può realizzare più esecuzioni e
non è detto che tutte le esecuzioni siano uguali.
Per esempio, l’azione 2 della procedura potrebbe essere così eseguita:
l’esecutore seleziona il secondo canale con il telecomando.
È evidente che l’esecutore deve avere le seguenti competenze:

: conoscere il tasto di accensione ed essere in grado di premerlo;


: saper selezionare i canali con il telecomando;
: saper spegnere il televisore.

6.3 Algoritmi
Nell’algoritmo per l’uso di un apparecchio televisivo appena descritto, gli oggetti da manipolare (ta-
sti, telecomando) sono concreti e non formalizzabili.
Descriviamo ora una procedura in cui gli oggetti da manipolare sono astratti e formalizzabili.

Esempio Area .....................................................................................................................


Calcolare l’area di un triangolo sapendo che b e h sono, rispettivamente, le misure della ba-
se e dell’altezza del triangolo, e che l’area del triangolo si calcola mediante la formula:
b×h
area = —-—
2

Pseudocodifica
INIZIO
dati iniziali: b e h;
moltiplica b per h;
dividi il prodotto per 2 e ottieni l’area: area = (b × h) / 2;
comunica il risultato, area;
FINE
...........................................................................................................................................

Questo algoritmo vale per calcolare l’area di qualsiasi triangolo purché si conoscano le misure della
base e dell’altezza. In questo caso, si dice che l’algoritmo risolve una classe di problemi.

L’insieme di tutti i problemi che vengono risolti dallo stesso algoritmo costituisce una
classe di problemi.

L’algoritmo che descrive l’uso dell’apparecchio televisivo e quello per il calcolo dell’area del triango-
lo presentano una differenza sostanziale: il primo opera su oggetti fisici (il televisore e il telecoman-
do) facilmente individuabili e, quindi, per essi non è necessario dare una descrizione dettagliata; il se-
condo opera su oggetti che non sono fisici ma astrazioni mentali (segmenti, figure piane) dalla cui
rappresentazione simbolica (numeri interi o decimali) non si può prescindere nella descrizione della
procedura risolutiva.
In generale, un algoritmo non serve mai per risolvere un singolo problema ma per risolvere più pro-
blemi che differiscono solo per i dati iniziali.

94
Unitˆ didattica 6 - Algoritmi e pseudocodifica

Una definizione informale di algoritmo • la seguente.

Per algoritmo si intende una successione finita di passi contenenti le istruzioni che
specificano le operazioni da compiere per risolvere una classe di problemi.

Quasi tutti gli algoritmi, tranne forse i pi• semplici, ricevono dei dati, li trasformano e, quindi, li co-
municano allÕutente. Un programma di elaborazione testi riceve i propri dati sotto forma di parole;
tali parole vengono formattate dal programma per dare loro un aspetto gradevole, quindi il pro-
gramma le stampa ordinatamente su carta.
Un archivio anagrafico riceve i propri dati sotto forma di nomi, indirizzi e numeri telefonici, memo-
rizza le informazioni sui propri supporti magnetici e poi visualizza i dati in un formato considerato
utile per lÕutente. Un sistema di guida per missili nucleari riceve i propri dati sotto forma di coordi-
nate del bersaglio.
Qualunque programma che abbia un minimo di funzionalitˆ segue queste tre fasi fondamentali:

1. acquisizione di dati iniziali;


2. elaborazione dei dati;
3. presentazione dei risultati.

La fase di acquisizione dei dati viene anche definita fase di input o di immissione. La
fase di presentazione dei risultati prende il nome di output o di emissione.

In un computer, lo strumento attraverso il quale avviene la maggior parte delle operazioni di input •
la tastiera (standard input) mentre lÕunitˆ pi• utilizzata per la presentazione dei risultati • il video
(standard output); questo processo • schematizzato nella figura seguente.

Output: emissione dei dati

Input: immissione dei dati

Il risultato dellÕelaborazione compiuta dallÕalgoritmo • costituito da un insieme di uno o pi• valori,


detti dati di output, che vengono messi a disposizione allÕesterno visualizzandoli sul monitor o scri-
vendoli su file o su carta. Si tratta della fase in cui il programma comunica i dati finali, ottenuti gra-
zie allÕelaborazione. Quando si descrive un algoritmo, si indica la fase di emissione dei dati di output
con istruzioni del tipo ÒcomunicaÓ, ÒscriviÓ oppure ÒvisualizzaÓ, seguite dal nome dei dati che ven-
gono comunicati allÕutente.
Pu˜ succedere che, per ottenere i dati di output a partire dai dati di input, sia necessario passare at-
traverso risultati intermedi. Tali dati non sono nŽ il frutto di operazioni di acquisizione nŽ sono uti-
lizzati in operazioni di emissione, ma sono dati temporanei che si ottengono nel corso dellÕelabora-
zione. Essi prendono il nome di variabili di lavoro.

I dati di input sono i valori iniziali che servono allÕalgoritmo per elaborare la soluzione
del problema.
Le variabili di lavoro sono dati non provenienti dallÕesterno, bens“ rappresentano ri-
sultati intermedi ottenuti durante lÕelaborazione.
I dati di output costituiscono il risultato delle operazioni effettuate dallÕalgoritmo. Essi
vengono resi disponibili allÕesterno mediante visualizzazione o stampa.

95
Sezione 3 - Organizzazione degli algoritmi

Lo schema seguente illustra i passaggi appena trattati.

Dati Dati
ELABORAZIONE
di input di output

6.4 Dati e istruzioni


Se esaminiamo gli algoritmi precedentemente descritti, notiamo che ogni passo dell’algoritmo è una
proposizione che descrive l’azione da eseguire e gli oggetti su cui questa opera.

Ogni passo dell’algoritmo si compone di due parti distinte:


: la descrizione dell’azione che deve essere eseguita, che prende il nome di
comando o istruzione;
: uno o più oggetti su cui ogni istruzione opera, che prendono il nome di dati o
argomenti.

Sono, per esempio, istruzioni:


acquisisci il numero; moltiplica ...; comunica ...;
mentre sono dati:
b, h, area.
In generale, i dati si indicano simbolicamente con stringhe (cioè successioni di caratteri) formate da
una o più lettere maiuscole o minuscole (ma noi conveniamo di usare lettere minuscole) dell’alfabe-
to inglese e da cifre numeriche, con la condizione che il primo carattere sia una lettera.
Le stringhe:
a, b, bc, area, media1, area;
rappresentano nomi di dati.
Non sono nomi di dati:
3a, paga netta, volume sfera1;
il primo perché inizia con una cifra e gli altri due perché sono formati da due stringhe.
Lo sono, invece:
a3, paganetta, volumesfera1.
Ricordiamo, quindi, che il nome di un dato non deve contenere spazi.

I dati possono essere:


: costanti: il loro valore non cambia nel tempo;
: variabili: durante l’esecuzione dell’algoritmo, può cambiare il loro valore.

Per esempio, nella formula:


2πR
che calcola la lunghezza della circonferenza, π è considerato dato costante; R è considerato dato va-
riabile. Finora, per descrivere algoritmi, ci siamo serviti del linguaggio naturale, cercando di espri-

96
Unitˆ didattica 6 - Algoritmi e pseudocodifica

mere le varie azioni in modo univoco e il pi• possibile conciso. Ma il linguaggio naturale, a causa del-
la sua complessitˆ e ambiguitˆ, non • sempre adatto per descrivere in modo adeguato algoritmi, so-
prattutto quando questi non sono semplici come quelli trattati finora. é, quindi, necessario costrui-
re un linguaggio che consenta una giusta interpretazione delle istruzioni da eseguire da parte dellÕo-
peratore, soprattutto se questo • una macchina, come nel caso di un elaboratore elettronico. DÕora
in avanti, introdurremo gradualmente il linguaggio della pseudocodifica.

La pseudocodifica non • un linguaggio di programmazione, ma viene utilizzato per


descrivere il procedimento risolutivo contenuto in un algoritmo. Un algoritmo in pseu-
docodifica non • pronto per essere utilizzato da un calcolatore, ma serve a produrre
il programma che • scritto in un particolare linguaggio di programmazione e che
viene caricato in un elaboratore per la sua esecuzione.

6.5 Istruzioni di assegnamento


Esaminando lÕalgoritmo dellÕesempio Area, troviamo lÕistruzione:
dividi il prodotto per 2 e ottieni lÕarea: area = (b × h) / 2
Con questa istruzione, lÕesecutore attribuisce alla variabile di nome area la metˆ del prodotto dei va-
lori attribuiti alle variabili b e h con lÕistruzione di acquisizione dei dati iniziali.

LÕistruzione mediante la quale a una variabile viene attribuito, cio• assegnato, un va-
lore appartenente a un dato insieme, detto insieme di definizione della variabile,
prende il nome di istruzione di assegnamento.

In pseudocodifica, lÕistruzione di assegnamento viene denotata secondo la seguente sintassi:


nome della variabile ← valore della variabile
Per esempio, con:
a ← 5;
si intende che alla variabile a viene assegnato il valore 5 e si legge:
a prende il valore 5
oppure:
5 viene assegnato ad a
Se una variabile ha giˆ un valore, questo viene sostituito dal nuovo valore assegnato.
Per esempio, se alla variabile a, a cui • giˆ stato assegnato il valore 5, vogliamo assegnare il valore 7,
il valore 5 viene cancellato. Quindi le istruzioni:
a ← 5;
a ← 7;
si possono visualizzare come nella figura seguente.

5 5 7

A A
A 5 A 7

97
Sezione 3 - Organizzazione degli algoritmi

Una variabile si comporta come una scatola su cui è scritto il nome (o


identificatore) della variabile e che contiene il valore assegnato (che
Contenuto
rappresenta il contenuto, come indicato nella figura a destra). E
5

Nome A

Non si deve mai confondere il contenitore con il contenuto (cioè con il valore assegnato alla variabile).
Elenchiamo alcuni tipi di assegnamento:

1. assegnamento per costante: a ← 9; e b ← 3; sono due esempi;


2. assegnamento per espressione: alla variabile viene assegnato il valore di un’espressione aritme-
tica che deve essere prima valutata;
3. assegnamento per nome: eseguendo l’istruzione
b ← a;
alla variabile b viene assegnato il valore della variabile di nome a;
4. assegnamento per calcolo: eseguendo le istruzioni
a ← 14;
b ← a + 1;
a b viene assegnato il valore 15;
5. assegnamento per ridefinizione:
a ← a + 1;
alla variabile a viene assegnato il suo valore attuale aumentato di 1; quindi, se il valore attuale
di a è 5, dopo l’assegnazione sarà 6.

Si deve sempre tenere presente che se a una variabile viene assegnato un valore, allora il valore prece-
dentemente contenuto viene distrutto.

Una variabile, prima di essere trattata, deve contenere già un valore, ovvero essere
già stata inizializzata.

Esempi ................................................................................................................................
Data la sequenza di istruzioni:
a ← 5;
b ← a + 1;
c ← a + b;
dopo l’esecuzione della terza istruzione il contenuto di ogni variabile è: a = 5; b = 6; c = 11.
Data la sequenza di istruzioni:
a ← 5;
b ← a + c;
c ← a + 1;
dall’esame della sequenza notiamo che nella seconda istruzione non è possibile assegna-
re un valore alla variabile b perché non è conosciuto il contenuto di c: la variabile c non è
stata inizializzata, cioè non le è stato assegnato alcun valore.
...........................................................................................................................................

98
Unitˆ didattica 6 - Algoritmi e pseudocodifica

6.6 Istruzioni di ingresso e di uscita dei dati


LÕistruzione per lÕacquisizione dei dati di ingresso prende il nome di istruzione di lettu-
ra o di input.

In pseudocodifica, si conviene di esprimere tale istruzione con la parola ÒleggiÓ seguita dal nome del-
la variabile racchiusa tra parentesi tonde:
leggi (nome della variabile);
Per esempio, se la variabile • a, si scrive:
leggi (a);
Se le variabili sono pi• di una, aventi per esempio i nomi a, b, c, lÕistruzione di lettura pu˜ essere co-
s“ denotata:
leggi (a, b, c);
LÕistruzione termina con un punto e virgola per far capire allÕesecutore che lÕistruzione • terminata
e ne segue unÕaltra.

Per comunicare i risultati, ci si serve di unÕistruzione che prende il nome di istruzione di


scrittura o di output.

In pseudocodifica, questa istruzione viene descritta con la parola ÒscriviÓ seguita dal nome della va-
riabile contenente il risultato, compresa entro parentesi tonde:
scrivi (nome della variabile);
ma il risultato pu˜ anche essere rappresentato da una o pi• espressioni e, quindi, si ha:
scrivi (espress1, espress2, ..., espressN);
Per esempio, nellÕistruzione:
scrivi (a, a Ð b, 10);
se 15 e 3 sono rispettivamente i valori di a e b, lÕesecutore comunica:
15 12 10

Spesso, per rendere comprensibili i risultati da parte dellÕutente, si usano frasi rac-
chiuse tra apici, dette costanti letterali.

Per esempio, nellÕistruzione:


scrivi (ÒIl prezzo della merce • di euroÓ, p);
se p ha il valore 100, allÕesterno viene comunicato:
Il prezzo della merce • di euro 100

Esempio Rettangolo ...........................................................................................................


Scrivere in pseudocodifica lÕalgoritmo per calcolare lÕarea del rettangolo, note le misure
dei lati.
Per le operazioni di input le variabili sono b e h, mentre il risultato finale viene memorizzato in area,
che • la variabile per lÕoutput.

99
Sezione 3 - Organizzazione degli algoritmi

Pseudocodifica
input : b, h
output : area
INIZIO
leggi (b, h)
area ← b * h
scrivi (“Area del rettangolo = ”, area)
FINE

Dall’esame dell’algoritmo risulta che:


: le istruzioni sono comprese entro le parole INIZIO e FINE e sono scritte leggermente spostate a de-
stra rispetto a tali parole;
: prima della parola INIZIO, vengono definite le variabili, ovvero le “cose” su cui operano le istruzioni
dell’algoritmo;
: le variabili sono divise in variabili di input e variabili di output: le prime sono argomento di istruzione
di tipo leggi, mentre le seconde risultano all’interno di istruzioni di output.

...........................................................................................................................................

6.7 Teorema di Jacopini-Böhm


Lo studio degli algoritmi ebbe un notevole sviluppo negli anni ’60 del secolo scorso, quando i
programmi raggiunsero notevoli dimensioni in termini di righe di codice ed elevati gradi di com-
plessità.
In quegli anni, il linguaggio grafico dei diagrammi a blocchi era l’unico disponibile per descrivere
algoritmi.
Ma, inizialmente, lo studio degli algoritmi si sviluppò in modo disordinato, tanto che E.W. Dijrska
nel 1968 denunciò su una rivista del settore la necessità di razionalizzare tale studio, protestando per
l’eccessiva soggettività degli algoritmi che, quindi, risultavano non facilmente accessibili. Il processo
di razionalizzazione dello studio degli algoritmi trae fondamento dal teorema di Jacopini-Bö hm,
enunciato di seguito.

Ogni algoritmo può essere trasformato in un algoritmo equivalente che faccia uso
solo di alcune strutture di controllo.
Tali strutture di controllo sono:
: sequenza;
: alternativa;
: ripetizione.

Due algoritmi sono equivalenti quando, per gli stessi dati di ingresso, producono i medesimi risultati in
uscita.

Nei prossimi paragrafi daremo una definizione precisa di sequenza, alternativa e ripetizione. Gli
algoritmi descritti nel rispetto del teorema enunciato prendono il nome di algoritmi strutturati e
il loro studio si situa nel processo di razionalizzazione e standardizzazione logica iniziato negli an-
ni ’60.
Le strutture di controllo per creare algoritmi strutturati sono descritte in pseudocodifica mediante
espressioni linguistiche che esprimono in modo sintetico il significato di tali strutture.
Ogni struttura è considerata come un blocco di istruzioni avente una sola entrata e una sola uscita.

100
Unitˆ didattica 6 - Algoritmi e pseudocodifica

La presentazione di un algoritmo in pseudocodifica descrive la strategia risolutiva del proble-


ma, indipendentemente dalle caratteristiche del linguaggio di programmazione che si vuole uti-
lizzare.
Un programma per lÕelaborazione automatica • composto da diverse righe di codice che rispettano
le regole sintattiche del linguaggio utilizzato.
Lo schema logico che serve per strutturare il programma • la base razionale che sta al di sotto del co-
dice, • indipendente dal linguaggio di programmazione ed • quello che fa in modo che il program-
ma produca risultati utilizzabili e consistenti a partire dai dati iniziali.
Tale schema logico, ovvero lÕalgoritmo risolutivo, pu˜ essere descritto in pseudocodifica. LÕutilizzo
di questÕultima permette di ÒdimenticareÓ le specificitˆ del linguaggio di programmazione e con-
sente di concentrarsi sui passaggi razionali che costituiscono lÕelaborazione dei dati, cio• la trasfor-
mazione dei dati iniziali in risultati attesi affidabili.

La pseudocodifica, quindi, serve a descrivere la strategia per affrontare il problema e


a organizzare in modo razionale lÕalgoritmo risolutivo.

é ovvio che, per costruire un programma sicuro che non si blocchi al secondo tentativo di esecuzio-
ne, risulta fondamentale organizzare in modo razionale lÕalgoritmo risolutivo e, allo scopo, • fonda-
mentale il ricorso alla pseudocodifica e al rispetto del teorema di Jacopini-Bš hm.
Inoltre, da ogni algoritmo realizzato in pseudocodifica pu˜ essere derivato un programma in un
qualsiasi linguaggio, mentre un programma costruito senza rispettare il teorema di Jacopini-Bš hm
non pu˜ essere ÒtradottoÓ in pseudocodifica e rende difficoltoso il controllo della sua strategia riso-
lutiva (ammesso che ne abbia una).

Struttura di sequenza
Lo schema della struttura di sequenza di un algoritmo • indicato di seguito: ogni elemento Bi pu˜
rappresentare unÕistruzione semplice (come, per esempio, leggi, scrivi, assegna, ...) ed • detto blocco
semplice, oppure pu˜ essere costituito, a sua volta, da una delle strutture fondamentali (sequenza, al-
ternativa e ripetizione) e, in questo caso, • detto blocco composto.
La pseudocodifica della struttura di sequenza ha la seguente sintassi:

INIZIO
B1
B2
...
...
Bn
FINE

Tornando allÕesempio ÒRettangoloÓ, le istruzioni comprese tra INIZIO e FINE sono indicate in se-
quenza e lÕesecutore deve eseguirle nellÕordine in cui sono disposte.

Pseudocodifica
input : b, h
output : area
INIZIO
leggi (b, h)
area ← b * h
scrivi (ÒArea del rettangolo = Ó, area)
FINE

101
Sezione 3 - Organizzazione degli algoritmi

6.8 Struttura di alternativa


Lo schema di alternativa • costituito da un blocco di controllo che contiene una condizione il cui ve-
rif“carsi o meno determina lÕesecuzione delle istruzioni di un blocco B1 o di un blocco B2.
Lo schema sintattico della struttura di alternativa binaria •:
SE condizione
ALLORA
B1
ALTRIMENTI
B2
FINE SE
Come lo schema sintattico mette in evidenza, prima viene valutata la condizione; se questa • vera,
viene eseguito B1, se • falsa viene eseguito B2.

Esempio Verifica.................................................................................................................
Decidere quanto studiare nellÕeventualitˆ di una verifica.

Pseudocodifica
INIZIO
SE devi fare la verifica
ALLORA
ripassa gli argomenti vecchi
ALTRIMENTI
studia lezione del giorno
FINE SE
FINE

Le due parole INIZIO e FINE delimitano il blocco di istruzioni di cui • formato lÕintero programma.
Le righe dopo ALLORA e dopo ALTRIMENTI corrispondono, rispettivamente, ai blocchi B1 e B2 indicati nel-
lo schema sintattico precedente. In questo caso, entrambi i blocchi sono composti da una sola istruzione.

...........................................................................................................................................

Esempio Assoluto1 .............................................................................................................


Costruire lÕalgoritmo per calcolare il valore assoluto di un numero intero a.

Pseudocodifica
input : a
output : assoluto
INIZIO
leggi (a)
SE a >= 0
ALLORA
assoluto ← a
ALTRIMENTI
assoluto ← Ða
FINE SE
scrivi (Òvalore assoluto = Ó, assoluto)
FINE

102
Unitˆ didattica 6 - Algoritmi e pseudocodifica

Trattandosi di un esempio numerico, nelle prime due righe sono state definite le variabili su cui opera il
programma, distinguendo tra le variabili di input e quelle di output.
...........................................................................................................................................

Alternativa a una via


La struttura alternativa a una via presenta uno schema diverso dal precedente e non contiene il ÒramoÓ
ALTRIMENTI. Lo schema sintattico della struttura alternativa a una via •:

SE condizione
ALLORA
B1
FINE SE

Esempio Ombrello..............................................................................................................
Decidere se uscire con lÕombrello.

Pseudocodifica
INIZIO
SE piove
ALLORA
esci con lÕombrello
FINE SE
FINE
...........................................................................................................................................

Esempio Assoluto2 .............................................................................................................


LÕalgoritmo dellÕesempio ÒAssolutoÓ pu˜ essere cos“ modificato:

Pseudocodifica
input : a
output : assoluto
INIZIO
leggi (a)
assoluto ← a
SE a < 0
ALLORA
assoluto ← Ða
FINE SE
scrivi (Òvalore assoluto = Ó, assoluto)
FINE
...........................................................................................................................................

LÕesempio successivo mostra come • possibile combinare tra loro diverse strutture di selezione: si
parla in questo caso di strutture di selezione nidificate.

Esempio Targhe alterne .....................................................................................................


In una domenica ecologica vige il regime di traffico a targhe alterne: se il giorno corrente •
pari, circolano solo i veicoli con targa pari, altrimenti solo quelli con targa dispari.

103
Sezione 3 - Organizzazione degli algoritmi

Pseudocodifica
INIZIO
SE data odierna è pari
ALLORA
SE la targa finisce con una cifra pari
ALLORA
usa l’auto
ALTRIMENTI
esci a piedi
FINE SE
ALTRIMENTI
SE la targa finisce con una cifra dispari
ALLORA
usa l’auto
ALTRIMENTI
esci a piedi
FINE SE
FINE SE
FINE

Si noti che le istruzioni (nell’esempio “usa l’auto” e “esci a piedi”) sono scritte rientrate rispetto alle pa-
role ALLORA e ALTRIMENTI e che queste ultime occupano da sole un’intera riga. Inoltre le parole delle
coppie SE-FINE SE e ALLORA-ALTRIMENTI sono sempre incolonnate tra loro. Da ultimo, la parola SE deve
sempre avere il corrispondente FINE SE, mentre è possibile trovare delle strutture di alternativa che ri-
chiedono soltanto le parole SE-ALLORA-FINE SE, senza, dunque, l’alternativa ALTRIMENTI.

...........................................................................................................................................

6.9 Struttura di ripetizione


La struttura di ripetizione permette di eseguire una o più volte un blocco B di istruzioni.

La ripetizione presenta due schemi di composizione:

: ripetizione precondizionale (con controllo in testa);


: ripetizione postcondizionale (con controllo in coda).

Ripetizione precondizionale

La struttura di ripetizione precondizionale ha, in pseudocodifica, la seguente sin-


tassi:
MENTRE condizione
B
FINE MENTRE

Il blocco B viene eseguito una o più volte MENTRE la condizione è vera; quando questa diventa fal-
sa, la ripetizione si interrompe.
Il blocco B potrebbe anche non essere mai eseguito se la condizione non è mai vera. Chiariamo quan-
to detto con due esempi.

104
Unitˆ didattica 6 - Algoritmi e pseudocodifica

Esempio Semaforo .............................................................................................................


Descrivere il comportamento da seguire davanti a un semaforo in funzione.

Pseudocodifica
INIZIO
MENTRE semaforo rosso
Aspetta
FINE MENTRE
Attraversa
FINE

Cos“ come la struttura di alternativa • racchiusa tra le parole SE e FINE SE, anche lÕiterazione ha una pa-
rola iniziale, che • MENTRE, e parole di chiusura, ossia FINE MENTRE. Tali parole vanno incolonnate.
LÕiterazione continua se la condizione indicata dopo MENTRE risulta vera; quando la condizione di-
venta falsa lÕesecuzione del programma procede con lÕistruzione presente subito dopo le parole FINE
MENTRE.
...........................................................................................................................................

Esempio Somma primi 4 ....................................................................................................


Descrivere lÕalgoritmo per calcolare la somma dei primi quattro numeri naturali.

Analisi
I numeri da sommare sono: 1, 2, 3, 4.
Data la variabile somma, inizializzata con zero (somma ← 0) deve essere eseguita la seguente se-
quenza di istruzioni:
1 somma ← somma + 1
2 somma ← somma + 2
3 somma ← somma + 3
4 somma ← somma + 4
con lÕistruzione n. 4 la variabile somma contiene il risultato.
Notiamo che le quattro istruzioni sono tutte del tipo:
somma ← somma + k
dove k • una variabile a cui assegnare, di volta in volta, i valori 1, 2, 3, 4.

Pseudocodifica
output : somma
variabile di lavoro : k
INIZIO
somma ← 0
k ← 1;
MENTRE k <= 4
somma ← somma + k
k←k+1
FINE MENTRE
scrivi (Òrisultato = Ó, somma)
FINE

105
Sezione 3 - Organizzazione degli algoritmi

L’esecutore, per realizzare la ripetizione MENTRE, deve:


: controllare se la condizione di controllo k <= 4 è verificata;
: eseguire, se k <= 4, le istruzioni del ciclo:
somma ← somma + k; k ← k + 1;
tra queste istruzioni ve ne deve essere sempre una, detta istruzione di modifica, che modifica il va-
lore della variabile di controllo (in questo caso k); nell’esempio tale istruzione è:
k←k+1
che, a ogni ripetizione incrementa di una unità il valore di k;
: ripetere le azioni dei punti a) e b), uscendo dal ciclo di ripetizione quando la condizione k <= 4 è falsa.
Le istruzioni fuori ciclo:
somma ← 0 e k ← l
che inizializzano le due variabili somma e k, prendono il nome di istruzioni di inizializzazione del ciclo.

...........................................................................................................................................

Struttura di ripetizione postcondizionale

La struttura di ripetizione postcondizionale, ha in pseudocodifica la seguente sintassi:


RIPETI
B
FINCHÉ condizione

Il blocco B viene eseguito una o più volte FINCHÉ la condizione è falsa; quando la condizione di-
venta vera, la ripetizione si interrompe: il blocco B viene quindi ripetuto fino a quando la condizio-
ne indicata dopo FINCHÉ da falsa diventa vera. In questa ripetizione, il blocco B viene eseguito al-
meno una volta perché il controllo viene dopo il blocco B, a differenza della ripetizione MENTRE
nella quale il blocco di controllo viene prima del blocco di istruzioni da eseguire.

Esempio Asso di cuori ........................................................................................................


Determinare, all’interno di un mazzo di carte, in quale posizione si trova l’asso di cuori.

Analisi
Il problema si risolve concretamente scoprendo una carta alla volta e contando le carte che man ma-
no vengono scoperte. Se si vuole formalizzare l’algoritmo, una soluzione può essere la seguente.

Pseudocodifica
INIZIO
conta ← 0
RIPETI
scopri una carta
conta ← conta + 1
FINCHÉ è l’asso di cuori
Scrivi (“L’asso di cuori si trova alla posizione = ”, conta)
FINE

Anche in questo caso le istruzioni da ripetere sono racchiuse da una parola iniziale (RIPETI) e da una pa-
rola finale (FINCHÉ).
...........................................................................................................................................

106
Unità didattica 6 - Algoritmi e pseudocodifica

Esempio Somma primi 4bis................................................................................................


L’algoritmo per il calcolo della somma dei primi quattro numeri naturali, precedentemente
descritto, può essere modificato come segue.

Pseudocodifica
output : somma
variabile di lavoro : k
INIZIO
somma ← 0
k←4
RIPETI
somma ← somma + K
k←k–1
FINCHÉ k = 0
scrivi (“risultato = ”, somma)
FINE

Anche per l’istruzione RIPETI FINCHÉ si distinguono:


: le istruzioni di inizializzazione del ciclo: somma ← 0; K ← 4;
: la condizione di controllo: k = 0;
: le istruzioni del ciclo: somma ← somma + k e k ← k – 1;
: l’istruzione di modifica: k ← k – 1.

Negli esempi relativi alla somma dei primi 4 numeri, possiamo notare che nelle due strutture di ripe-
tizione MENTRE-FINE MENTRE e RIPETI-FINCHÉ sono sempre presenti:
: una (ma in molti casi anche più di una) variabile di controllo inizializzata prima del ciclo, che
è k, inizializzata con k ← 0 nel ciclo MENTRE e con k ← 4 nel ciclo RIPETI;
: una o più istruzioni che devono essere eseguite una o più volte, tra le quali c’è sempre
una istruzione di modifica della variabile di controllo (k ← k + 1, k ← k –1, rispettiva-
mente);
: la condizione di uscita dal ciclo, falsa per MENTRE e vera per FINCHÉ;

Le due iterazioni si differenziano perché in MENTRE la condizione viene controllata prima di ogni
ciclo e in RIPETI tale controllo viene eseguito dopo l’esecuzione di ogni ciclo. Inoltre, se in MEN-
TRE il blocco di istruzioni potrebbe non essere mai eseguito, in RIPETI il blocco è eseguito alme-
no una volta.
...........................................................................................................................................

6.10 Considerazioni sulla pseudocodifica


Gli esempi visti nei paragrafi precedenti mostrano algoritmi risolutivi, descritti dalla pseudocodifica
mediante costrutti che ben si adeguano alle strutture del linguaggio di programmazione e che per-
ciò permettono di costruire programmi affidabili.
La pseudocodifica non è altro che l’elencazione dei passaggi logici che conducono al risultato fi-
nale, espressi utilizzando termini di uso comune. Allo scopo di rendere comprensibile il linguag-
gio usato in questa prima fase di definizione dell’algoritmo, dato che ognuno di noi potrebbe uti-
lizzare termini diversi per dire la stessa cosa, sono state definite alcune regole di massima, che so-
no elencate di seguito.

107
Sezione 3 - Organizzazione degli algoritmi

: Ogni algoritmo deve iniziare con la parola INIZIO e finire con la parola FINE.
: La selezione è descritta dal costrutto SE ... ALLORA ... ALTRIMENTI ... FINE SE.
: La ripetizione precondizionale è descritta dal costrutto MENTRE ... FINE MENTRE.
: La ripetizione postcondizionale è descritta dal costrutto RIPETI ... FINCHÉ.
: È possibile assegnare a un blocco di istruzioni un nome che ne descrive le funzionalità,
che potrà essere costituito anche da più parole: l’iniziale di ciascuna parola dovrà
essere maiuscola.
: Per le strutture più complesse, che verranno descritte più avanti, si possono usare
anche i costrutti SCEGLI ... CASO ... FINE SCEGLI; PER ... DA ... A ... PASSO ... FINE PER.

Nelle strutture di iterazione, le parole MENTRE e FINCHÉ sono seguite da una condizione.
Anche se queste due parole hanno una funzione simile, il loro significato è ben diverso e va chiarito
con precisione.
MENTRE richiede che la ripetizione sia continuata quando la condizione che la segue è vera; la ri-
petizione si arresti quando la condizione è falsa.
FINCHÉ richiede che la ripetizione sia continuata quando la condizione che la segue è falsa; la ripe-
tizione si arresti quando la condizione è vera.
Nella ripetizione precondizionale, la condizione che indica se si devono eseguire le istruzioni da ite-
rare è posta all’inizio; ne consegue che l’iterazione può non essere eseguita mai (se la condizione in-
dicata dopo MENTRE non è verificata). Nella ripetizione postcondizionale la condizione che indi-
ca se si devono eseguire le istruzioni da ripetere è posta alla fine e, quindi, le istruzioni da iterare ven-
gono sempre eseguite almeno una volta. Le parole MENTRE e FINCHÉ possono essere scambiate,
se la condizione che le segue è sostituita con la sua negazione. L’esempio dell’algoritmo della ricer-
ca dell’asso di cuori in un mazzo di carte può avere anche la seguente versione.
INIZIO
conta ← 0
RIPETI
Scopri una carta
conta ← conta + 1
MENTRE non è l’asso di cuori
Comunica conta
FINE
In sintesi, valgono le seguenti uguaglianze MENTRE = FINCHÉ non e FINCHÉ = MENTRE non.
L’algoritmo in pseudocodifica si presenta come uno schema che può essere codificato senza sforzo
in un qualsiasi linguaggio di programmazione: per questo la sua costruzione riveste un’importanza
primaria. Concludendo, l’algoritmo descritto in modo strutturato rappresenta l’anello di congiun-
zione tra l’analisi del problema e la stesura del codice del programma risolutivo, come si può vedere
nella figura seguente.

PROGRAMMATORE ELABORATORE

Analisi Algoritmo Codice


Dati
del problema in pseudocodifica di programma

108
Unità didattica 6 - Algoritmi e pseudocodifica

Esercizi
Unità didattica 6

1 Si consideri l’algoritmo:
// input
a
// output
assoluto
INIZIO
leggi (a)
SE a >= 0
ALLORA
assoluto <-- a
ALTRIMENTI
assoluto <-- –a
FINE SE
scrivi (“valore assoluto = “, assoluto)
FINE
se a = –39, quale istruzione di assegnamento viene eseguita?

2 Descrivere l'algoritmo che, dati due interi a, b, scriva il valore assoluto della differenza tra il primo e il
secondo numero.

3 Che cosa viene scritto se si esegue il segmento di algoritmo indicato?


A <-- 5
SE A <= 2
ALLORA
scrivi(“A è piccolo”)
ALTRIMENTI
SE A <= 10
ALLORA
Scrivi(“A è medio”)
ALTRIMENTI
Scrivi(“A è grande”)
FINE SE
FINE SE

4 Che cosa viene scritto se si esegue il segmento di algoritmo indicato?


i <-- 0
RIPETI
i <-- i + 1
scrivi(i)
FINCHE’ i < 5

5 Che cosa viene scritto se si esegue il segmento di algoritmo indicato?


i <-- 0
RIPETI
i <-- i + 1

109
Sezione 3 - Organizzazione degli algoritmi

Esercizi
Unità didattica 6
scrivi(i)
FINCHE’ i >= 5
Per gli esercizi dal numero 6 al numero 10 indicare i dati di input, di output e le variabili di lavoro e strut-
turare l’algoritmo risolutivo.

6 In un piano cartesiano si conoscono le coordinate di un vertice e la lunghezza del lato di un quadrato. Si


consideri che i lati del quadrato sono paralleli agli assi e che il vertice di cui si conoscono le coordinate
è quello in basso a sinistra.

7 Dati i coefficienti di un'equazione di secondo grado, calcolare, se esistono, le due soluzioni reali.

8 Data in input una serie di numeri contare quelli pari e quelli dispari; comunicare anche la percentuale sul
totale dei pari e dei dispari. Suggerimento: si devono ripetere le seguenti istruzioni.
leggi numero
SE numero è pari
ALLORA conta pari
ALTRIMENTI conta dispari
FINE SE
Chiedi “serie finita ?”
leggi risposta

9 Scrivere un programma che legge una serie di numeri di tipo double e, alla fine, comunicare la media dei
numeri.

q0 Dati tre numeri indicare il minimo e il massimo.

qa Dato un elenco di persone di cui sono indicati il nome e l’anno di nascita, scrivere il nome del più vec-
chio e del più giovane.

qs Dati due numeri interi e positivi calcolare il loro quoziente intero utilizzando solo le operazioni di somma
e differenza.

qd Dato il segmento di pseudocodifica:


a = 15
SE a <= 2
ALLORA
x = “a è piccolo”
ALTRIMENTI
SE a <= 10
ALLORA
x = “a è medio”
ALTRIMENTI
x = “a è grande”
FINE SE
FINE SE
Scrivi (x)
che cosa compare a video?

110
Unità didattica 6 - Algoritmi e pseudocodifica

Esercizi
Unità didattica 6

qf Date le coordinate di due punti del piano cartesiano calcolare la loro distanza.

qg Date le coordinate di due punti del piano cartesiano determinare i coefficienti della retta passante per
essi (controllare che la retta non sia verticale).

qh Dati il nome e l’età di due persone indicare prima il nome del più giovane e dopo quello del più vecchio.

qj Dato in input un numero controllare se è compreso tra 0 e 10.

qk Dato in input un numero controllare se è esterno all’intervallo [5, 10].

ql Scrivere un programma che legge tre numeri in virgola mobile e che sceglie e scrive il maggiore dei tre.

111
Unitˆ didattica
7
Istruzioni di selezione

CHE COSA IMPARERAI A FARE

$ Usare le istruzioni di selezione


$ Utilizzare le diverse varianti dellÕistruzione if
$ Organizzare la scelta multipla con switch
$ Realizzare semplici applicazioni con algoritmi strutturati
$ Lavorare con i connettivi logici

CHE COSA DOVRAI STUDIARE

$ Sintassi dellÕistruzione if
$ Come organizzare if nidificati
$ Sintassi dellÕistruzione switch
Unitˆ didattica
Istruzioni di selezione
7
I programmi scritti finora per illustrare in pratica i concetti che sono stati sviluppati non erano dota-
ti di particolari strutture che consentissero un minimo controllo sul flusso del programma. Sono sta-
ti trattati anche tutti gli operatori relazionali e logici, ed • ora il momento di utilizzarli in contesti in
cui poter apprezzare le loro effettive qualitˆ nella gestione del flusso del codice.

7.1 Istruzione if
Gli operatori relazionali assumono la loro importanza dal fatto che consentono di prendere decisio-
ni in base al loro risultato.
ÒPrendere una decisioneÓ significa essere in grado di eseguire un blocco di codice al verificarsi di una
determinata condizione. C++, come tutti i linguaggi di alto livello, mette a disposizione lÕistruzione
if che consente al programma di eseguire un blocco di codice solo in base al verificarsi di una de-
terminata condizione.
La sua sintassi •:
if (condizione)
{
//blocco di codice
}
dove condizione • un test in cui vengono messi in relazione due operandi tramite un operatore
relazionale o logico. LÕistruzione if, con la sintassi vista sopra, realizza quanto visto in pseudocodi-
fica per la struttura di selezione a una via.
SE condizione
ALLORA
blocco di istruzioni
FINE SE
Quindi le istruzioni, allÕinterno del blocco, vengono eseguite solo se la condizione risulta verificata
(true).

Esempio If1 ........................................................................................................................


Verificare che un numero intero letto da tastiera sia maggiore di 50 con la scrittura di un
messaggio in caso affermativo.

Pseudocodifica
INIZIO
leggi (numero)
SE numero > 50
ALLORA
scrivi (Òil numero • maggiore di 50Ó)
FINE SE
FINE

113
Sezione 3 - Organizzazione degli algoritmi

Codice
If1.cpp
1 include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //dichiara la variabile intera
7 int numero;
8
9 //chiedi numero
10 cout << "\nInserisci un numero ";
11
12 //leggi un numero
13 cin >> numero;
14
15 //controlla se • > di 50
16 if (numero>50)
17 {
18 //scrivi se la condizione • verificata (true)
19 cout << "\nIl numero • maggiore di 50";
20 }
21
22 //salta due righe
23 cout << endl << endl;
24 //arresta l'esecuzione del programma
25 system ("pause");
26 //termina il programma
27 return 0;
28 }

Prova di esecuzione

Analisi del codice


Alla riga 16 lÕistruzione if controlla la condizione indicata tra parentesi tonde (numero>50): se il
test restituisce il valore true, cio• se la condizione • verificata, viene eseguito il blocco di codice de-
limitato dalle parentesi graffe.
In questo esempio il blocco di codice • composto solo da unÕistruzione di scrittura a video, che no-
tifica che il numero inserito risulta maggiore di 50. Se la condizione fosse risultata false il blocco
di codice non sarebbe stato eseguito, proseguendo con lÕelaborazione dellÕistruzione immediata-
mente successiva (riga 23).
...........................................................................................................................................

Il codice del programma precedente informa lÕutente solo al verificarsi della condizione; in caso con-
trario, non emette alcun messaggio.

114
Unitˆ didattica 7 - Istruzioni di selezione

7.2 Istruzione if..else


La soluzione adottata nel codice del programma precedente non risulta ottimale; per questo • possi-
bile utilizzare un altro costrutto, che estende lÕistruzione if rendendola pi• efficiente: il costrutto
else.
La sintassi di unÕistruzioni if completa • la seguente:
if (condizione)
{
//1¡ blocco di codice
}
else
{
//2¡ blocco di codice
}
Qui, il 2o blocco di codice viene eseguito quando la condizione non • verificata (false), con-
sentendo cos“ lÕesecuzione di codice in alternativa alla condizione imposta dallÕif. In questo caso
viene realizzata la struttura di selezione completa vista nellÕUnitˆ didattica 6, relativa alle struttu-
re di controllo.
SE condizione
ALLORA
B1
ALTRIMENTI
B2
FINE SE

Esempio If_Else ...................................................................................................................


Verificare che un numero intero letto da tastiera sia maggiore di 50. In caso affermativo,
scrivere un messaggio di conferma; nel caso contrario, segnalare che il numero non • mag-
giore di 50.

Pseudocodifica
INIZIO
leggi (numero)
SE numero > 50
ALLORA
scrivi (ÒIl numero • maggiore di 50Ó)
ALTRIMENTI
scrivi (ÒIl numero non • maggiore di 50Ó)
FINE SE
FINE

Codice
If_Else.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()

115
Sezione 3 - Organizzazione degli algoritmi

5 {
6 //dichiara una variabile intera
7 int numero;
8
9 //chiedi numero
10 cout << "\nInserisci un numero ";
11
12 //leggi un numero
13 cin >> numero;
14
15 //controlla se • > di 50
16 if (numero>50)
17 {
18 //scrivi che la condizione • verificata (true)
19 cout << "\nIl numero • maggiore di 50";
20 }
21 else
22 {
23 //scrivi che la condizione non • verificata (false)
24 cout << "\nIl numero non • maggiore di 50";
25 }
26
27 //salta due righe
28 cout << endl << endl;
29 //arresta l'esecuzione del programma
30 system ("pause");
31 //termina il programma
32 return 0;
33 }

Prova di esecuzione

Analisi del codice


LÕistruzione alla riga 16 riporta la versione estesa dellÕistruzione if dove, alla riga 21, compare ÒlÕal-
ternativaÓ else. Se il test risulta verificato viene eseguita lÕistruzione di riga 19, altrimenti il flusso
del programma ÒsaltaÓ alla riga 24, dove inizia il blocco di istruzioni relative allÕalternativa else.
...........................................................................................................................................

La condizione che segue la parola if pu˜ essere semplice, come negli esempi precedenti, oppure
pu˜ essere una condizione composta da pi• enunciati legati tra loro dai connettivi logici And, Or,
Xor e Not.

Esempio If_And...................................................................................................................
Verificare che un numero letto da tastiera sia compreso nellÕintervallo che va da 10 a 100.
Il numero • compreso tra 10 e 100 quando risulta maggiore o uguale a 10 e minore o ugua-
le a 100.

116
Unitˆ didattica 7 - Istruzioni di selezione

Pseudocodifica
INIZIO
leggi (numero)
SE numero >= 10 AND numero <= 100
ALLORA
scrivi (ÒIl numero • compreso nellÕintervalloÓ)
ALTRIMENTI
scrivi (ÒIl numero • esterno allÕintervalloÓ)
FINE SE
FINE

Codice
If_And.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //dichiara una variabile intera
7 int numero;
8
9 //chiedi numero
10 cout << "\nInserisci un numero ";
11
12 //leggi un numero
13 cin >> numero;
14
15 //controlla se • compreso nell'intervallo
16 if ((numero>=10)&&(numero<=100))
17 {
18 //scrivi che la condizione • verificata (true)
19 cout << "\nIl numero • compreso";
20 }
21 else
22 {
23 //scrivi che la condizione non • verificata (false)
24 cout << "\nIl numero non • compreso";
25 }
26
27 //salta due righe
28 cout << endl << endl;
29 //arresta l'esecuzione del programma
30 system ("pause");
31 //termina il programma
32 return 0;
33 }

Prova di esecuzione

117
Sezione 3 - Organizzazione degli algoritmi

Analisi del codice


LÕintroduzione dellÕistruzione if..else ha consentito un maggior controllo sul dato inserito da
tastiera, verificando tutte le situazioni possibili.
Infatti, alla riga 16 lÕistruzione if, tramite lÕoperatore logico &&, verifica se il numero inserito • com-
preso nellÕintervallo.
Se il test risulta verificato viene eseguita lÕistruzione di stampa alla riga 19, altrimenti il flusso del pro-
gramma ÒsaltaÓ alla riga 24, dove inizia il blocco di codice relativo allÕalternativa else.
...........................................................................................................................................

7.3 If nidificati
Riprendiamo lÕesempio ÒIf_ElseÓ relativo allÕutilizzo della struttura di alternativa.
Si vuole verificare che un numero intero inserito da tastiera sia uguale a 50. Una possibile soluzione
pu˜ essere quella di inserire unÕaltra istruzione if..else nel blocco di codice relativo allÕelse.
Infatti, una volta stabilito che il numero inserito risulta Ònon maggiore di 50Ó, si hanno due pos-
sibilitˆ:
1. il numero inserito • minore di 50;
2. il numero inserito • uguale a 50.

Esempio Else_If ...................................................................................................................


Controllare se un numero inserito da tastiera • minore, maggiore o uguale a 50.

Codice
Else_If.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //dichiara la variabile intera
7 int numero;
8
9 //chiedi numero
10 cout << "\nInserisci un numero ";
11
12 //leggi un numero
13 cin >> numero;
14
15 //controlla numero
16 if (numero>50)
17 {
18 //• verificata la prima possibilitˆ
19 cout << "\nIl numero inserito • pi• grande di 50";
20 }
21 else if (numero<50) //2a possibilitˆ
22 {
23 //• verificata la seconda possibilitˆ
24 cout << "\nIl numero inserito • pi• piccolo di 50";
25 }
26 else //3a possibilitˆ

118
Unitˆ didattica 7 - Istruzioni di selezione

27 {
28 //• verificata la terza possibilitˆ
29 cout << "\nIl numero inserito • uguale a 50";
30 }
31
32 //salta due righe
33 cout << endl << endl;
34 //arresta l'esecuzione del programma
35 system ("pause");
36 //termina il programma
37 return 0;
38 }

Prova di esecuzione

Analisi del codice


LÕistruzione if..else alla riga 16 • notevolmente cambiata e, tramite la nuova struttura, • sta-
to possibile effettuare tutti i controlli che il problema richiedeva.
Infatti, il primo controllo (riga 16) permette di informare lÕutente, qualora sia verificato, che il nu-
mero inserito • maggiore di 50.
Al contrario, il codice a partire dalla riga 21 consente di verificare, tramite lÕinserimento di una
nuova if..else , le altre due possibili situazioni, cio• se il numero inserito risulta inferiore a 50
o, tramite il costrutto else che inizia alla riga 26, se il numero inserito risulta sicuramente iden-
tico a 50.

Si noti lÕuso di parentesi graffe dopo il costrutto else di riga 21: queste servono a racchiudere un bloc-
co di codice composto da due o pi• istruzioni.
In questo esempio non • necessario inserirle (anche se nessuno lo vieta), in quanto il blocco di codice
relativo • composto da una sola istruzione. Le righe che vanno dalla 16 alla 31 si possono tranquilla-
mente riscrivere come segue.
...
//controlla numero
if (numero>50)
//• verificata la prima possibilitˆ
cout << "\nIl numero inserito • pi• grande di 50";
else if (numero<50) //2a possibilitˆ
//• verificata la seconda possibilitˆ
cout << "\nIl numero inserito • pi• piccolo di 50";
else //3a possibilitˆ
//• verificata la terza possibilitˆ
cout << "\nIl numero inserito • uguale a 50";
...

LÕimportante • che tutto il codice risulti chiaro e semplice da leggere.

...........................................................................................................................................

119
Sezione 3 - Organizzazione degli algoritmi

7.4 Istruzione switch (selezione multipla)


Lo schema organizzativo dell’alternativa a due vie non sempre risponde alle necessità che si possono
incontrare nella stesura degli algoritmi. Per risolvere situazioni complesse da esprimere con la strut-
tura di scelta è stato introdotto lo schema della scelta multipla.
In pseudocodifica tale struttura può essere rappresentata nel modo seguente.
SCEGLI variabile_di_controllo
CASO lista valori_1:
blocco istruzioni_1
ESCI;
CASO lista valori_2:
blocco istruzioni_2
ESCI;
...
CASO lista valori_n:
blocco istruzioni_n
ESCI;
ALTRIMENTI
blocco istruzioni
FINE SCEGLI
La presenza della struttura di selezione multipla all’interno di un algoritmo determina, al momento
dell’esecuzione del programma, le seguenti azioni:

: se il valore delle variabili è presente in una delle liste di valori viene eseguito il blocco di istru-
zioni corrispondente, fino alla prima istruzione ESCI; successivamente si procede con l’esecu-
zione della prima istruzione che segue la riga FINE SCEGLI;
: in caso contrario viene eseguito il blocco di istruzioni indicato dopo la riga ALTRIMENTI.

Se, per esempio, si deve associare a un numero compreso tra 1 e 7 il relativo giorno della settimana,
utilizzando l’istruzione if dobbiamo utilizzare sette costrutti if..else, controllando sempre che
il numero inserito non sia esterno ai limiti numerici imposti.
Nel linguaggio C++ la scelta multipla è realizzata dall’istruzione switch, che ha la sintassi riporta-
ta di seguito.
switch (variabile_di_controllo)
{
case valori1:
istruzioni1;
break;
case valori2:
istruzioni2;
break;

...
case valorin:
istruzionin;
break;
default:
istruzioni;
}

120
Unitˆ didattica 7 - Istruzioni di selezione

Dopo la parola chiave switch viene indicato il nome della variabile di controllo (detta anche va-
riabile selettore), di cui si deve controllare il valore per decidere quale strada seguire tra quelle pos-
sibili. Accanto ai valori previsti • consigliabile non dimenticare di scrivere le istruzioni da eseguire nel
caso in cui la variabile non assuma nemmeno uno dei valori indicati, inserendo la parola chiave de-
fault; se tale etichetta non • presente e non vi • stata alcuna corrispondenza tra la variabile di con-
trollo e le etichette case il controllo passa alla prima istruzione che segue il costrutto switch.
é bene inserire sempre la clausola default nel costrutto switch in quanto, grazie a essa, si posso-
no indicare istruzioni particolari nel caso in cui non esista alcuna corrispondenza tra la variabile di
controllo e i valori presenti nei case.
Viene generato un errore di compilazione se due o pi• etichette case fanno riferimento al medesi-
mo insieme di valori.
Per meglio chiarire la funzionalitˆ del costrutto switch codifichiamo lÕesempio della corrisponden-
za tra un numero inserito da tastiera e il relativo giorno della settimana.

Esempio Settimana ............................................................................................................


Visualizzare il giorno della settimana corrispondente a un numero inserito da tastiera, com-
preso tra 1 e 7.

Codice
Settimana.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //dichiara una variabile intera
7 int numero;
8
9 //chiedi un numero
10 cout << "\nInserisci un numero intero (da 1 a 7): ";
11
12 //leggi numero
13 cin >> numero;
14
15 switch (numero)
16 {
17 case 1: cout <<"\nE' Luned“ "; break;
18 case 2: cout <<"\nE' Marted“ "; break;
19 case 3: cout <<"\nE' Mercoled“ "; break;
20 case 4: cout <<"\nE' Gioved“ "; break;
21 case 5: cout <<"\nE' Venerd“ "; break;
22 case 6: cout <<"\nE' Sabato"; break;
23 case 7: cout <<"\nE' Domenica"; break;
24 default: cout <<"\nNumero non corrispondente!!!"; break;
25 }
26
27 //salta due righe
28 cout << endl << endl;
29 //arresta l'esecuzione del programma
30 system ("pause");
31 //termina il programma
32 return 0;
33 }

121
Sezione 3 - Organizzazione degli algoritmi

Prova di esecuzione

Analisi del codice


La variabile numero diventa il selettore del costrutto switch, che tenta di stabilire una corrispon-
denza con le costanti appartenenti ai case. In questo esempio le costanti indicate nei case con-
templano valori che vanno da 1 a 7 (dalla riga 17 alla riga 24); esse servono per verificare la corri-
spondenza con il numero inserito e, laddove tale corrispondenza compare, vengono eseguite tutte le
istruzioni relative. Se la corrispondenza non • verificata entra in gioco il costrutto default (riga 24)
che avverte, con un messaggio, che non esiste alcuna corrispondenza.
...........................................................................................................................................

7.5 Raggruppamento dei case


Come si • avuto modo di studiare nel paragrafo precedente, lÕutilizzo dellÕistruzione switch risul-
ta essere molto versatile quando si devono effettuare molte scelte in base ai valori che assume la va-
riabile di controllo. Pu˜ capitare, a volte, che a un valore della variabile di controllo vengano associati
pi• costrutti case. C++ prevede questa possibilitˆ, tramite la sintassi indicata di seguito.
switch (variabile_di_controllo)
{
case valori1:
case valori2:
istruzioni1;
break;

case valori4:
case valori5:
istruzioni5;
break;
...
...
default:
istruzioni;
break;
}
I valori dei due primi case, pur essendo differenti, sono stati raggruppati: ci˜ significa che, al verifi-
carsi della corrispondenza di uno dei due valori con quello della variabile variabile_di_con-
trollo, viene eseguito il medesimo codice.
Lo stesso concetto vale per i case 4 e 5.

Esempio PariDispari ...........................................................................................................


Determinare se un numero inserito da tastiera, compreso nellÕintervallo che va da 1 a 10,
• pari o dispari.

122
Unitˆ didattica 7 - Istruzioni di selezione

Codice
PariDispari.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6
7 //dichiara una variabile intera
8 int numero;
9
10 //chiedi un numero
11 cout << "\nInserisci un numero intero (da 1 a 10): ";
12
13 //leggi numero
14 cin >> numero;
15
16 switch (numero)
17 {
18 //case che individuano i numeri dispari
19 case 1:
20 case 3:
21 case 5:
22 case 7:
23 case 9: cout <<"\nNumero inserito dispari\n";
24 break;
25
26 //case che individuano i numeri pari
27 case 2:
28 case 4:
29 case 6:
30 case 8:
31 case 10: cout << "\nNumero inserito pari\n";
32 break;
33
34 default: cout << "\nNumero esterno ai limiti!!!\n";
35 break;
36 }
37
38 //salta due righe
39 cout << endl << endl;
40 //arresta l'esecuzione del programma
41 system ("pause");
42 //termina il programma
43 return 0;
44 }

Prova di esecuzione

123
Sezione 3 - Organizzazione degli algoritmi

Analisi del codice


In questo semplice programma sono stati raggruppati i case che riguardano i numeri dispari, dalla ri-
ga 19 alla riga 23, e i case che riguardano i numeri pari, dalla riga 27 alla riga 31.
In questo modo qualsiasi numero compreso nellÕintervallo trova corrispondenza attraverso il grup-
po di case di appartenenza; se il numero digitato non rientra nellÕintervallo richiesto il controllo
passa alla clausola default, alla riga 34, che avverte lÕutente dellÕerrore commesso.

La variabile di controllo dellÕistruzione switch non pu˜ essere di tipo qualsiasi: sono gestiti tutti i tipi in-
teri, il tipo carattere e gli enumeratori (che vedremo pi• avanti).
Si ricorda che i tipi interi sono int, long e char, mentre una stringa • una sequenza di caratteri rac-
chiusa tra virgolette.
Se il tipo della variabile di controllo • diverso dai valori dei case si rende necessario un suo casting espli-
cito, allo scopo di consentirne la corrispondenza.

...........................................................................................................................................

124
Unità didattica 7 - Istruzioni di selezione

Esercizi
Unità didattica 7

1 Scrivere la sintassi del costrutto if..else.

2 Nel seguente frammento di codice c’è un errore: individuare quale.


...
if (num>Max);
{
cout << “\nIl numero inserito è maggiore di” << Max;
}

3 Che cosa viene visualizzato utilizzando il seguente frammento di codice?


...
int num = 5;
if ((num>=10)&&(num<=20))
if (num % 2 == 0)
cout << “\nIl numero è pari”;
else
cout << “\nIl numero è dispari”;
else
cout << “\nNumero non compreso nell’intervallo”;
...

4 Si vuole calcolare l’imponibile IVA e il totale di un importo inserito da tastiera. Se l’importo risulta minore
o uguale a euro 150.00 l’IVA deve essere del 16%; se l’importo è superiore, l’IVA deve essere del 20%.

5 Scrivere il codice C++ per risolvere il problema seguente. In un piano cartesiano si conoscono le coor-
dinate di un vertice e la lunghezza del lato di un quadrato. Si consideri che i lati del quadrato sono pa-
ralleli agli assi e che il vertice di cui si conoscono le coordinate è quello in basso a sinistra.

6 Scrivere il codice C++ per risolvere il problema seguente: dati i coefficienti di un'equazione di secondo
grado calcolare, se esistono, le due soluzioni reali.

7 A che cosa serve l’istruzione switch?

8 A che cosa servono i costrutti case?

9 Perché bisogna inserire la parola chiave break alla fine dei case?

q0 A che cosa serve la clausola default?

qa Perché è consigliabile inserire la clausola default?

qs Scrivere un programma che associ a ogni mese dell’anno, inserito da tastiera e indicato da un numero
intero da 1 a 12, il nome corrispondente.

qd Scrivere un programma che notifichi a quale dei quattro trimestri appartiene un mese inserito da ta-
stiera.

125
Sezione 3 - Organizzazione degli algoritmi

Esercizi
Unità didattica 7

qf Date le coordinate di due punti sul piano cartesiano, determina il coefficiente angolare della retta pas-
sante per essi (controlla che la retta non sia verticale).

qg Dato in input un numero, controlla se è esterno all’intervallo [5;10].

qh Dato in input un numero intero compreso tra 0 e 15, scrivi la sua rappresentazione esadecimale.

qj Dato in input un numero intero compreso tra 0 e 255, scrivi la sua rappresentazione esadecimale (sug-
gerimento: dividere il numero dato per 16 due volte: i due resti, presi in ordine inverso, determinano la
sequenza delle cifre esadecimali).

qk Dati in input tre numeri, scrivi il minimo.

ql Dati in input tre numeri, indica il valore compreso tra il minimo e il massimo.

w0 Scrivere il codice C++ per risolvere il problema seguente: dati i coefficienti di un'equazione di secondo
grado calcolare, se esistono, le due soluzioni reali.

126
Unitˆ didattica
8
Istruzioni di ripetizione

CHE COSA IMPARERAI A FARE

$ Utilizzare le istruzioni di ripetizione nelle loro diverse forme


$ Sfruttare le istruzioni di controllo pi• complesse per la definizione
di algoritmi complessi
$ Nidificare le strutture

CHE COSA DOVRAI STUDIARE

$ Istruzione while
$ Istruzione do..while
$ Istruzione for
$ Modalitˆ di interruzione
Unità didattica
Istruzioni di ripetizione
8
Nell’Unità didattica precedente sono state studiate tutte quelle istruzioni che, in qualche modo, pos-
sono modificare il flusso di programma. Spesso, però, un programmatore ha la necessità di ripetere un
blocco di codice più volte.
All’interno di una applicazione le istruzioni che consentono di ripetere un blocco di codice vengono
chiamate istruzioni di ripetizione o iterative o, più grossolanamente, cicli.
Queste istruzioni, insieme alle strutture di controllo, rivestono un’enorme importanza nella pro-
grammazione, quale che sia il linguaggio di codifica.

Il C++ mette a disposizione tre tipi di istruzioni di ripetizione:

: while;
: do..while;
: for.

8.1 Istruzione while


L’istruzione while viene utilizzata per ripetere un blocco di codice fino a quando la
condizione di controllo presente risulta non verificata (false).
È possibile (nel caso in cui l’istruzione di controllo risulti non verificata fin dalla sua pri-
ma valutazione) che le istruzioni che costituiscono il corpo del ciclo non vengono
mai eseguite.

La sua sintassi è riportata di seguito.

while (condizione)
{
blocco_di_istruzioni;
}

L’istruzione while valuta la condizione tra parentesi, chiamata condizione di controllo e, se risulta
verificata (true), vengono eseguite le istruzioni all’interno delle parentesi graffe. Una volta esegui-
te tutte le istruzioni, la condizione di controllo viene nuovamente valutata e, solo se risulta non ve-
rificata, il flusso del programma prosegue con l’istruzione successiva al blocco while, altrimenti
vengono nuovamente eseguite le istruzioni al suo interno e così via.

L’istruzione while realizza il costrutto in pseudocofica visto per la struttura di ripetizione precon-
dizionale, che ha la forma seguente.

MENTRE condizione
blocco di istruzioni
FINE MENTRE

Facciamo un esempio.

128
Unitˆ didattica 8 - Istruzioni di ripetizione

Esempio Tabellina1 ............................................................................................................


Scrivere un programma che consenta di calcolare la ÒtabellinaÓ di un numero inserito da ta-
stiera compreso tra 1 e 10. Per ÒtabellinaÓ di un numero si intendono i suoi multipli da uno
a dieci.

Pseudocodifica
//input
num
//output
num
//variabile di lavoro
i
INIZIO
i ←1
leggi (num)
MENTRE i<= 10
scrivi (i, num, num * i)
i←i+1
FINE MENTRE
FINE

Codice
Tabellina1.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //definisci il contatore i
7 int i=1;
8
9 //dichiara una variabile intera. chiedi e leggi un numero
10 int num;
11 cout << "\nInserisci un numero intero (da 1 a 10): ";
12 cin >> num;
13
14 //ciclo while
15 while(i<=10)
16 {
17 cout <<"\n" << " " << i << " " << num << " " << num*i;
18 //incrementa il contatore
19 ++i;
20 }
21
22 //salta due righe
23 cout << endl << endl;
24 //arresta l'esecuzione del programma
25 system ("pause");
26 //termina il programma
27 return 0;
28 }

129
Sezione 3 - Organizzazione degli algoritmi

Prova di esecuzione

Analisi del codice


Alla riga 7 • stata dichiarata e inizializzata una variabile di tipo intero, che viene usata come contato-
re relativo ai 10 numeri che compongono la tabellina.
Dalla riga 10 alla riga 12 viene richiesto e acquisito un numero intero compreso da 1 a 10, di cui cal-
colare i valori della tabellina.
Alla riga 15 inizia il ciclo while, che ha come condizione unÕespressione che consente di controllare
che il contatore risulti inferiore o uguale a 10. Il corpo del ciclo • costituito da due semplici istruzioni.
La riga 17 serve per visualizzare il contatore, il numero inserito e il risultato del calcolo.
Alla riga 19 viene incrementata la variabile contatore i.
Il ciclo while si ripete fintanto che il contatore risulta inferiore o uguale a 10, come • stabilito dal-
la condizione.
NellÕultima iterazione, cio• quando il contatore ha assunto il valore 10, viene visualizzato lÕultimo ri-
sultato e viene incrementato il contatore stesso, che assume quindi il valore 11. La condizione, a que-
sto punto, non • pi• verificata e il ciclo while termina la sua esecuzione, passando il controllo alla
prima istruzione utile che lo segue.
...........................................................................................................................................

8.2 Tabulazione
Il programma visto nel paragrafo precedente produce risultati corretti, ma non li presenta in modo
leggibile e ordinato: i numeri disposti in colonna sono disallineati, mentre dovrebbero essere inco-
lonnati a destra, inoltre le colonne dei risultati sono prive di unÕintestazione che chiarisca il significa-
to dei numeri presentati in output.
Per una prima soluzione del problema indicato si deve ricorrere agli strumenti di formattazione visti
nel paragrafo 4.2, Ò Output formattatoÓ. NellÕesempio che segue useremo il metodo setw(n), che
imposta il numero di caratteri occupati dal risultato, anche detto ÒampiezzaÓ.

Esempio Tabellina2 ............................................................................................................


Scrivere un programma che consenta di calcolare e di scrivere la ÒtabellinaÓ di un numero
inserito da tastiera, compreso tra 1 e 10. Presentare i risultati incolonnati a destra.

Tabellina2.cpp
1 #include <iostream>
2 #include "iomanip"

130
Unitˆ didattica 8 - Istruzioni di ripetizione

3 using namespace std;


4 // INIZIO
5 int main ()
6 {
7 //definisci il contatore i
8 int i=1;
9
10 //chiedi e leggi il dato di input
11 int num;
12 cout << "\nInserisci un numero intero (da 1 a 10): ";
13 cin >> num;
14
15 //scrivi intestazione della tabellina
16 cout <<"\n"<<setw(5)<<"i"<<setw(8)<<"num"<<setw(8)<<"num*i"<<"\n";
17
18 //ciclo while
19 while(i<=10)
20 {
21 cout <<"\n" << setw(5)<< i << setw(8) << num << setw(8)<< num*i;
22 //incrementa il contatore
23 ++i;
24 }
25
26 //salta due righe
27 cout << endl << endl;
28 //arresta l'esecuzione del programma
29 system ("pause");
30 //termina il programma
31 return 0;
32 }

Prova di esecuzione

Analisi del codice


Rispetto allÕesempio precedente il codice presenta tre novitˆ.
1. Alla riga 2 viene richiamata la libreria iomanip, che contiene i metodi utili per la manipola-
zione dei dati di input/output.
2. Alla riga 21 lÕistruzione di scrittura dei dati contiene il metodo setw(...), che specifica il
numero di caratteri che devono essere destinati alla visualizzazione di un valore: per ogni da-

131
Sezione 3 - Organizzazione degli algoritmi

to deve essere sempre richiamato il metodo setw() e non • possibile distribuirne lÕeffetto su
pi• dati. Nel nostro esempio per la visualizzazione del contatore vengono riservati 6 caratteri,
per quella degli altri due dati 8 caratteri.
3. Alla riga 16 la medesima operazione di impostazione dellÕampiezza di scrittura viene eseguita
sulle stringhe di intestazione delle colonne, in modo tale che sia i dati numerici della tabella,
sia le intestazioni risultino tutti incolonnati sulla destra.

Si noti che la riga che contiene le intestazioni deve essere scritta una volta sola ed •, pertanto, posta al-
lÕesterno del ciclo while; al contrario, le istruzioni di scrittura a video dei risultati devono essere ripetu-
te pi• volte, quindi sono poste allÕinterno del blocco di istruzioni che • controllato dal ciclo while.
...........................................................................................................................................

8.3 Istruzione do..while


Con lÕuso dellÕistruzione while, se la condizione di controllo non risulta verificata, le istruzioni
contenute nel ciclo non vengono eseguite. A volte per˜ si ha la necessitˆ che il blocco di codice in-
terno al ciclo venga eseguito almeno una volta. C++ propone unÕistruzione che consente una tale
eventualitˆ: il ciclo do..while. Di seguito la sua sintassi.
do
{
blocco_di_istruzioni
}
while (condizione);
LÕistruzione do..while viene utilizzata per ripetere un blocco di codice fino a che la condizione di
controllo presente alla fine del blocco di istruzioni risulta non verificata (false).

Il ciclo do..while garantisce almeno una iterazione; ci˜ si evince dal fatto che la condizione di con-
trollo • posta alla fine di tutte le istruzioni, dopo la parola chiave do. Infatti, prima vengono esegui-
te le istruzioni contenute nel blocco di codice, poi viene eseguito il test di controllo e, se risulta ve-
rificato, il ciclo si ripete, altrimenti si esce dal ciclo proseguendo con lÕistruzione immediatamente
successiva. In realtˆ, tale caratteristica non sempre risulta soddisfacente perchŽ, di solito, le iterazio-
ni devono essere eseguite solo nel caso in cui risulti immediatamente verificata la condizione. Nel ci-
clo do..while, le istruzioni vengono eseguite sempre almeno una volta, a prescindere dal risulta-
to del test di controllo.

Il costrutto do..while realizza la struttura che in pseudocodifica viene chiamata ripetizione post-
condizionale, che • rappresentata dallo schema seguente.
RIPETI
blocco di istruzioni
MENTRE condizione

Esempio CalcolaMedia .....................................................................................................


Scrivere un programma che legga da tastiera la sequenza dei voti assegnati alle verifiche di
uno studente in una certa materia e alla fine ne calcoli la media. Dopo lÕacquisizione di ogni
voto, il programma deve chiedere allÕutente se lÕelenco dei voti • finito: quando la risposta
• positiva il ciclo termina e si procede al calcolo della media, mentre se la risposta • ne-
gativa vengono ripetute le operazioni di acquisizione di un voto.

132
Unitˆ didattica 8 - Istruzioni di ripetizione

Pseudocodifica
INIZIO
//input
voto;
risposta;
//output
media;
//variabili di lavoro
i=0 //contatore i
somma ← 0 //totalizzatore
RIPETI
chiedi e leggi un voto
somma = somma + voto //totalizza i voti
i=i+1 //incrementa contatore
chiedi se elenco terminato e leggi risposta
MENTRE risposta = ÔnÕ
media = somma / i; //calcola media
scrivi media
FINE

Codice
CalcolaMedia.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //INPUT
7 int voto;
8 char risposta;
9
10 //OUTPUT
11 double media;
12
13 //variabili di lavoro
14 int i=0; //contatore i
15 double somma=0; //totalizzatore
16
17 do
18 {
19 //chiedi e leggi un voto
20 cout << "\nInserisci un voto (da 1 a 10): ";
21 cin >> voto;
22
23 //totalizza i voti
24 somma += voto;
25
26 //incrementa contatore
27 i++;
28
29 //chiedi se elenco terminato e leggi risposta
30 cout << "\nl'elenco dei voti • terminato (s/n) ";

133
Sezione 3 - Organizzazione degli algoritmi

31 cin >> risposta;


32 }
33 //controlla se ripetere il blocco
34 while(risposta == 'n');
35
36 //calcola media
37 media = somma / i;
38 //scrivi media
39 cout << "\n\n media = " << media;
40
41 //salta due righe
42 cout << endl << endl;
43 //arresta l'esecuzione del programma
44 system ("pause");
45 //termina il programma
46 return 0;
47 }

Prova di esecuzione

Analisi del codice


Alle righe 7 e 8 vengono definite le variabili utili per contenere i dati di input, ovvero quelli inseriti
da tastiera. Alla riga 11 viene definita la variabile di output media. Le variabili di lavoro i e somma,
definite alle righe 14 e 15, servono rispettivamente per totalizzare e per contare i voti.
Il blocco di istruzioni compreso tra la riga 17 (do) e la riga 34 (while (..)) ha la funzione di ri-
chiedere un voto, sommare il voto nel totalizzatore somma, contare i voti e chiedere allÕutente se lÕe-
lenco dei voti • terminato; il blocco viene ripetuto ogni volta che lÕutente risponde ÒnÓ.
La decisione se ripetere le istruzioni del blocco viene presa in base allÕesito del test indicato alla riga
34: se risposta • ÒnÓ il test • verificato e lÕesecuzione del programma riprende dalla riga 17, altri-
menti il controllo dellÕesecuzione passa allÕistruzione successiva (dalla riga 35 in avanti).
...........................................................................................................................................

8.4 Istruzione for


Le strutture while e do..while risultano utili quando non si conosce a priori quante volte devo-
no essere eseguite le istruzioni da ripetere. Se, invece, si conosce il numero di volte per cui deve es-
sere ripetuto un gruppo di istruzioni, • meglio usare la struttura for. Le istruzioni da ripetere sono

134
Unitˆ didattica 8 - Istruzioni di ripetizione

indicate dopo la parola for e sulla riga di for viene indicato il criterio per contare il numero di ri-
petizioni. Diversamente dal costrutto while, nel ciclo di tipo for la variabile contatore viene in-
crementata o decrementata a ogni ripetizione delle istruzioni al suo interno.
In pseudocodifica la ripetizione enumerativa si indica con le parole indicate di seguito.
PER contatore DA inizio A fine [con PASSO incremento]
istruzioni
FINE PER
Tale struttura richiede che la variabile ÒcontatoreÓ venga impostata al valore di inizio e fa in modo
che, ogni volta che vengono eseguite le istruzioni comprese tra PER e FINE PER, la variabile Òcon-
tatoreÓ venga incrementata di un valore uguale a ÒincrementoÓ.
La parte dellÕistruzione compresa tra parentesi quadre pu˜ essere omessa: in tal caso lÕincremento •
da considerarsi uguale a 1.
Nella maggior parte dei casi, la struttura PER si presenta in una forma semplificata rispetto a quella
vista: per esempio, se si vuole ripetere un gruppo di istruzioni per 10 volte, basta scrivere:
PER contatore DA 1 A 10
istruzioni
RIPETI
Esaminando il ciclo while del programma di esempio tabellina.cpp (e in particolare il codice
relativo al ciclo while), si possono riscontrare alcuni elementi di base che ci portano alla definizio-
ne del ciclo for.

//definisci il contatore i
int i=1;
....
//ciclo while
while(i<=10)
{
cout <<"\n" << " " << i << " " << num << " " << num*i;
//incrementa il contatore
++i;
}

Gli elementi di base sono i seguenti:

1. dichiarazione e inizializzazione di un contatore (int i=1;);


2. verifica condizionale del contatore (i<=num);
3. istruzioni di manipolazione del dato (cout << ...;);
4. incremento del contatore (i++;).

Il C++, come tutti i linguaggi di alto livello, mette a disposizione unÕistruzione che riunisce tre dei
quattro punti precedenti in un unico costrutto: il ciclo for.
La sua sintassi • riportata di seguito.
for (inizializzazione; controllo; incremento)
{
blocco di istruzioni
}

In cui ogni espressione che fa parte della condizione, che viene indicata tra parentesi tonde, deve es-
sere separata obbligatoriamente da un punto e virgola.

135
Sezione 3 - Organizzazione degli algoritmi

Si ha che:

: inizializzazione rappresenta l’espressione in cui viene dichiarato e inizializzato un con-


tatore, per esempio int i=1;;
: condizione rappresenta l’espressione in cui compare la condizione di controllo, per esem-
pio i<=num;;
: incremento, infine, rappresenta l’espressione che effettua l’incremento del contatore, per
esempio i++;.

Volendo quindi riscrivere il ciclo utilizzando l’istruzione for, il ciclo while precedente assume la
forma seguente:

for (int i=1; i<=num; i++)


{
fatt*=i;
}

L’istruzione for viene utilizzata per ripetere un blocco di codice per un numero stabi-
lito di volte.

Ecco spiegato nel dettaglio come funziona il ciclo for.

In primo luogo esegue l’espressione di inizializzazione del contatore e lo fa, ovviamente, solo la
prima volta; poi controlla la condizione e, se questa risulta verificata, vengono elaborate tutte le
istruzioni del blocco di codice interno, altrimenti vengono eseguite le istruzioni successive al ci-
clo. Se la condizione di controllo risulta verificata, dopo che le istruzioni interne al ciclo sono sta-
te eseguite il flusso ritorna all’istruzione for, che incrementa il contatore ed esegue nuovamente
il test di controllo. Tutto il processo si ripete fino a che la condizione di controllo non risulta più
verificata.

Esempio Fattoriale .............................................................................................................


Scrivere un programma che consenta di calcolare il fattoriale di un numero non maggiore di
10 inserito da tastiera.

Si tratta di moltiplicare la sequenza degli interi che vanno da 1 a un numero intero inserito da tastie-
ra. Un simile calcolo viene effettuato mediante un ciclo che ripete la stessa operazione, che consiste
nell’esecuzione di una moltiplicazione tante volte fino ad arrivare al numero inserito. In matematica,
questo genere di calcolo prende il nome di fattoriale. Per esempio, il fattoriale del numero 5 è 120
e si indica con 5!; il calcolo da eseguire è 1 * 2 * 3 * 4 * 5 = 120.
Il calcolo del fattoriale viene effettuato moltiplicando tra loro il numero dato e tutti i numeri prece-
dentemente forniti. Per esempio: il fattoriale di 6 (si scrive 6!) è 720 cioè, 1 * 2 * 3 * 4 * 5 * 6.

Pseudocodifica
//input
num
//output
fatt
//variabili di lavoro
i
INIZIO

136
Unitˆ didattica 8 - Istruzioni di ripetizione

fatt ← 1
scrivi (Ò\nInserisci un numero intero (Max 10) = Ó)
leggi (num)
PER i DA 1 A num
fatt ← fatt * i
FINE PER
scrivi (fatt)
FINE

Codice
Fattoriale.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //input
7 int num;
8 //output
9 int fatt;
10
11 //chiedi e leggi il numero su cui calcolare il fattoriale
12 cout << "\nInserisci un numero intero (Max 10) = ";
13 cin >> num;
14
15 //inizializzazione
16 fatt = 1;
17 //esegue il ciclo for
18 for (int i=1; i<=num; i++)
19 {
20 fatt *= i;
21 }
22
23 //scrivi il risultato
24 cout << "\nFattoriale = " << fatt;
25
26 //salta due righe
27 cout << endl << endl;
28 //arresta l'esecuzione del programma
29 system ("pause");
30 //termina il programma
31 return 0;
32 }

Prova di esecuzione

137
Sezione 3 - Organizzazione degli algoritmi

Analisi del codice


Dopo aver dichiarato le variabili e dopo aver letto il numero da tastiera, alle righe 12 e 13, viene ese-
guito il ciclo for dove compaiono le espressioni che consentono lÕiterazione (righe dalla 18 alla 21):
infatti, subito dopo la parola for compare la dichiarazione e lÕinizializzazione del contatore int
i=1;, il controllo i<=num; e lÕincremento del contatore i++.
Il blocco di codice del ciclo contiene solo lÕoperazione di calcolo del fattoriale fatt *= i;, alla ri-
ga 20.

A questo punto ci si potrebbe chiedere: ÒPerchŽ usare un ciclo for piuttosto che un while?Ó. Le istru-
zioni iterative while, do e for consentono di creare cicli ciascuno con caratteristiche strutturali diffe-
renti, che si adeguano a situazioni specifiche.
Quando in un programma si deve utilizzare unÕistruzione di ripetizione e si • a conoscenza dellÕesatto nu-
mero di cicli da effettuare, come nellÕesempio appena visto, sarebbe meglio utilizzare un ciclo for; se,
invece, il numero di iterazioni dipende da un valore che il programmatore non pu˜ stabilire a priori, come
nel caso del programma CalcolaMedia.cpp (in cui la condizione di terminazione del ciclo poteva es-
sere verificata anche dopo migliaia di inserimenti di voti), si dovrebbe usare un ciclo while; infine, se il
blocco di codice da iterare deve essere eseguito almeno una volta, • conveniente utilizzare il ciclo
do..while.
Per concludere, possiamo dire che la scelta del tipo di ciclo da eseguire • strettamente correlata alla na-
tura del problema e allÕalgoritmo utilizzato per risolverlo.

...........................................................................................................................................

138
Unità didattica 8 - Istruzioni di ripetizione

Esercizi
Unità didattica 8

1 Dato il codice:
int i = 0;
do
i++;
while (i >= 5)
cout << i;

quale valore viene scritto?

2 Dato il codice:
int i = 0;
while (i >= 5)
{
i++;
}
cout << i;

quale valore viene scritto?

3 Dato il codice:
int i = 0;
do
i++;
while (i < 5)
cout << i;

quale valore viene scritto?

4 Dato il codice:
int i = 0;
while (i < 5)
{
i++;
}
cout << i;

quale valore viene scritto?

5 Dato il codice:
int i = 0;
do
i++;
cout << i;
while (i >= 5)

quali valori vengono scritti?

139
Sezione 3 - Organizzazione degli algoritmi

Esercizi
Unità didattica 8

6 Dato il codice:
int i = 0;
while (i >= 5)
{
i++;
cout << i;
}

quali valori vengono scritti?

7 Dato il codice:
int i = 0;
do
i++;
cout << i;
while (i < 5)

quali valori vengono scritti?

8 Dato il codice:
int i = 0;
while (i < 5)
{
i++;
cout << i;
}

quali valori vengono scritti?

9 Dato un numero intero N, calcolare la somma dei quadrati degli interi minori o uguali a N.

q0 Dato un numero intero N, calcolare il fattoriale di N (fattoriale di N : N! = 1 * 2 * 3* . . . N).

qa Contare i divisori di un numero intero (N è divisore di M, se M % N = 0).

qs Determinare se un numero intero è primo (un numero è primo se nessun numero minore o uguale alla
sua metà è divisore del numero dato (N è divisore di M, se M % N = 0).

qd Dato l'elenco dei voti di uno studente, calcolare la media dei voti.

qf Dati in input il valore di un deposito bancario e il tasso di interesse annuo, calcolare gli interessi matu-
rati dopo 5 anni.

qg Qual è la sintassi dell’istruzione while?

qh Qual è la sintassi del ciclo do?

140
Unità didattica 8 - Istruzioni di ripetizione

Esercizi
Unità didattica 8

qj Dato l'elenco dei voti di uno studente, calcolare la media dei voti. Si devono ripetere le seguenti istru-
zioni:
leggi voto
somma ← somma + numero
Chiedi “elenco finito ?”
leggi risposta

qk Data in input una serie di numeri, contare quelli pari e quelli dispari; comunicare anche la percentuale
sul totale dei pari e dei dispari. Si devono ripetere le seguenti istruzioni:
leggi numero
SE numero è pari
ALLORA conta pari
ALTRIMENTI conta dispari
FINE SE
Chiedi “serie finita ?”
leggi risposta

ql Indicare la sintassi del ciclo for.

w0 È dato in input un elenco di N studenti (con N dato in input): per ogni studente si conoscono il nome, il
sesso e il voto in informatica. Calcolare la media dei voti di tutti gli studenti e la media dei voti dei ma-
schi e delle femmine.

wa Con i dati di input dell’esercizio precedente scrivere il nome dello studente che ha il voto più alto tra gli
studenti maschi, e il nome della studentessa che ha il voto più alto tra le studentesse.

141
Unitˆ didattica
9
Le funzioni

CHE COSA IMPARERAI A FARE

$ Organizzare in modo logico e razionale un programma


suddiviso in funzioni
$ Utilizzare la sintassi per la dichiarazione di una funzione
$ Distinguere le variabili locali da quelle globali
$ Stabilire lÕesatta collocazione delle variabili in ambiti funzionali
$ Utilizzare i valori di ritorno di una funzione
$ Impiegare funzioni che usufruiscono di parametri
$ Gestire i parametri per valore e per riferimento

CHE COSA DOVRAI STUDIARE

$ Necessitˆ delle funzioni


$ Definizione e uso di funzioni semplici
$ Ambito delle variabili: variabili locali e globali
$ Funzioni: valori di ritorno
$ Sintassi per il passaggio di parametri
$ Passaggio di parametri per riferimento
$ Funzioni matematiche
Unità didattica 9 Le funzioni
9.1 Le funzioni
Questa Unità didattica esamina le caratteristiche delle funzioni C++, incluso il passaggio di parame-
tri, la restituzione di un valore e la ricorsione.
La forma generale di una funzione in C++ è la seguente:
tipo_restituito nome_funzione (elenco_parametri)
{
corpo_della_funzione
}
dove:

: tipo_restituito: specifica il tipo di dato restituito dalla funzione. Una funzione può re-
stituire un qualsiasi tipo di dato, tranne un array.
: elenco_parametri: è un elenco di nomi di variabili separati da virgole e preceduti dai ri-
spettivi tipi; tali variabili sono destinate a ricevere i valori degli argomenti nel momento in cui
la funzione viene richiamata. Una funzione può anche non avere parametri, nel qual caso l’e-
lenco dei parametri sarà vuoto; tuttavia, anche in mancanza di parametri, è richiesta la presen-
za delle parentesi.
Nelle istruzioni di dichiarazione delle variabili è possibile dichiarare più variabili facenti capo a uno
stesso tipo, inserendo l’elenco dei nomi di variabili separati da virgole; al contrario, tutti i parametri
di una funzione devono essere dichiarati singolarmente e di ognuno si deve specificare sia il tipo sia
il nome: questo significa che la dichiarazione dei parametri di una funzione ha la seguente forma ge-
nerale:

nome-funzione(tipo nomevarl, tipo nomevar2, ... , tipo nomevarN)


Per esempio, ecco un modo corretto e uno errato di dichiarare i parametri di una funzione.

f(int i, int k, int j) //corretta


f(int i, k, float j) //errata

Nelle Unità didattiche precedenti è stata più volte incontrata la parola funzione, che è stato detto esse-
re una porzione di codice che viene richiamata tramite il suo nome.

Nonostante il C++ metta a disposizione tutte le funzionalità ad hoc per i nostri programmi, spesso si
ha la necessità, data la natura specifica di un’applicazione, di dover scrivere nuove funzioni che più si
adattano alle nostre esigenze.
Il linguaggio non mette a disposizione certo, per esempio, una funzione che calcola il perimetro di
un trapezio, ma, tramite l’uso delle istruzioni di calcolo, si può scrivere una funzione che effettui tut-
te le operazioni.
Dopo aver visto i concetti principali della programmazione, si può passare a esaminare meglio che
cos’è una funzione.

143
Sezione 3 - Organizzazione degli algoritmi

Una funzione • un blocco di codice autonomo che esegue unÕelaborazione.

Le funzioni sono fondamentali per due motivi: innanzitutto, suddividono il programma in parti pi•
ridotte e lo rendono pi• comprensibile; in secondo luogo, permettono il riutilizzo di segmenti di co-
dice in diversi programmi. Come sapete, quando si scrive il codice, si parte da un algoritmo di alto li-
vello e si continua a rifinirlo nel dettaglio finchŽ non si ottiene il codice di un programma che espri-
ma tutti gli algoritmi fino a comprendere quello di alto livello. Una funzione descrive una ÒrigaÓ in
uno di questi algoritmi, per esempio Òapri un fileÓ, Òvisualizza il testo sullo schermoÓ, Òstampa un
documentoÓ e cos“ via.
Inoltre, una funzione pu˜ essere richiamata, allÕinterno di un programma, tutte le volte che • neces-
sario senza ripetere ogni volta le istruzioni in essa contenute. Quindi, una volta scritta e definita una
funzione, il codice al suo interno viene eseguito ogni volta che il flusso del programma incontra una
sua invocazione.
Definire una funzione significa creare e dotare di una nuova funzionalitˆ il programma che si sta scri-
vendo. La sua definizione assume la seguente forma generale.
intestazione funzione
{
blocco_di_codice_della_funzione
}

9.2 La funzione pi• importante


Nei programmi eseguiti finora, il codice • stato scritto seguendo la struttura generale di un pro-
gramma C++.
Prendiamo in considerazione il codice del primo programma di questo volume, Ciao.cpp.

Ciao.cpp
1 #include "iostream"
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //scrivi il messaggio di saluto
7 cout << "Ciao, mondo!" << endl;
8 //arresta l'esecuzione del programma
9 system ("pause");
10 //termina il programma
11 return 0;
12 }

Alla riga 4 cÕ• la dichiarazione del ÒcontenitoreÓ del nostro codice. Tale contenitore • una funzione
e si chiama main. Il codice del programma • delimitato dalle parentesi graffe di riga 5 e riga 12.

Alla riga 4 viene definita la funzione main.

int main()
{
//scrivi il messaggio di saluto
cout << "Ciao, mondo!" << endl;
...

144
Unità didattica 9 - Le funzioni

La funzione main è la funzione principale ed è obbligatorio che sia definita in ogni


applicazione; essa costituisce il cosiddetto “entry point” del programma, cioè il pun-
to in cui inizia l’esecuzione del programma una volta compilato il codice.

La sua intestazione è quella di una funzione standard:

: restituisce il valore 0, quindi int;


: ha un nome univoco: main;
: non ha parametri di ingresso: ().

la funzione main() è una funzione speciale che ogni applicazione deve incorporare, ma se si vo-
gliono suddividere logicamente le funzionalità del programma occorre scrivere nuove funzioni, da
richiamarsi per eseguire i vari compiti.

Si può ora modificare il programma Ciao.cpp, inserendo una nuova funzione che effettua la scrit-
tura del messaggio di saluto.

void saluto()
{
//scrivi il messaggio di saluto
cout << "Ciao, mondo!" << endl;
}

La funzione si chiama saluto, non restituisce alcun valore e non ha parametri. Poiché, in questo
esempio, la funzione esegue una elaborazione, ma non restituisce alcun valore, il tipo di dato asso-
ciato alla funzione è void. Il corpo è composto da una sola semplice istruzione di stampa.

Quando una funzione non restituisce alcun valore viene anche chiamata procedura
e il valore restituito mancante è specificato mediante la parola chiave vod.

A questo punto, una domanda è d’obbligo: dove va inserita la funzione saluto?


La funzione va inserita prima della funzione main.

Esempio Ciao1 ...................................................................................................................


Ecco il codice della nuova versione del programma Ciao.cpp, che chiameremo Ciao1.cpp.

Codice
Ciao1.cpp
1 #include "iostream"
2 using namespace std;
3
4 void saluto()
5 {
6 //scrivi il messaggio di saluto
7 cout << "Ciao, mondo!" << endl;
8 }
9
10 //INIZIO
11 int main ()
12 {

145
Sezione 3 - Organizzazione degli algoritmi

13 saluto();
14
15 //arresta l'esecuzione del programma
16 system ("pause");
17 //termina il programma
18 return 0;
19 }

Analisi del codice


Il programma, ora, • composto da due metodi:
1. la funzione principale main();
2. la funzione saluto().
La funzione main() inizia alla riga 11 e termina alla riga 19, mentre alla riga 4 • stata dichiarata la
nuova funzione saluto(), che termina alla riga 8.
Alla riga 13 la funzione main() ospita una sola istruzione, che consente di richiamare (invocare) la
funzione saluto(), che provvede alla stampa del messaggio sul monitor.
é evidente che il normale flusso di esecuzione del codice subisce unÕalterazione, dovuta allÕinvoca-
zione della funzione saluto(), che trasferisce il flusso alla riga 4, dove inizia il codice della funzio-
ne. Una volta terminata lÕesecuzione del blocco di codice della funzione il flusso di esecuzione torna
allÕistruzione successiva allÕinvocazione della funzione, che in questo caso coincide con le istruzionei
che terminano il programma.

Nella figura • schematizzato il flusso di elaborazione del programma Ciao1.cpp:

main
//INIZIO
int main()
{
Invocazione
saluto();
funzione
//arresta lÕesecuzione del programma
system(ÓpauseÓ);
//termina il programma
return 0;
}

funzione saluto()

void saluto()
{
//scrivi il messaggio di saluto
cout << ÒCiao, mondo!Ó << endl;
}

Ritorna
al main
...........................................................................................................................................

146
Unitˆ didattica 9 - Le funzioni

9.3 Definizione delle funzioni


Il programma precedente • un banalissimo esempio sullÕuso delle funzioni ma in realtˆ, in program-
mi pi• complessi, le funzioni consentono di implementare una logica suddivisa nei vari compiti che
il programma stesso deve svolgere. Ogni compito viene implementato tramite una funzione che vie-
ne invocata ogni volta che se ne ha bisogno e tutte le volte che lo si desidera. Un codice ben struttu-
rato rappresenta il frutto di unÕattenta analisi del problema da risolvere in fase di progettazione e, co-
sa pi• importante, rende il codice stesso pi• facile da leggere e agevolmente predisposto a eventuali
modifiche e/o correzioni.
é il momento, dunque, di scrivere un programma un poÕ pi• complesso contenente nuove funzioni.

Esempio Quadrato1 ...........................................................................................................


Calcolare lÕarea del quadrato, dato il lato.

Il problema pu˜ essere suddiviso nei passi seguenti:

1. inserimento da tastiera della misura del lato;


2. calcolo del quadrato;
3. visualizzazione del risultato.

Si pu˜ capire, dalla breve analisi effettuata, che il programma deve assolvere a tre ÒcompitiÓ: lettura
dei dati, calcolo del risultato e stampa del risultato. Questi lavori si prestano benissimo a essere rea-
lizzati con altrettante funzioni cui daremo, rispettivamente, i seguenti nomi:

1. leggiLato();
2. calcolaQuadr();
3. scriviQuadr().

Codice
Quadrato1.cpp
1 #include <iostream>
2 using namespace std;
3
4 void leggiLato()
5 {
6 //chiedi e leggi lato
7 cout << "\nInserisci la misura del lato ";
8 cin >>lato;
9 }
10
11 void calcolaQuadr()
12 {
13 //calcola area quadrato
14 area = lato * lato;
15 }
16
17 void scriviQuadr()
18 {
19 //scrivi area
20 cout << "\nArea quadrato = " << area;
21 }

147
Sezione 3 - Organizzazione degli algoritmi

22
23 //INIZIO
24 int main ()
25 {
26 //dichiara variabili lato e area
27 double lato;
28 double area;
29
30 //invoca le funzioni
31 leggiLato();
32 calcolaQuadr();
33 scriviQuadr();
34
35 //fine programma
36 cout << "\n\nFine ";
37 system ("pause");
38 return 0;
39 }
...........................................................................................................................................

La compilazione del codice indicato sopra, per˜, dˆ luogo a una discreta lista di errori; di seguito ri-
portiamo a titolo di esempio che cosa appare nella finestra che elenca gli errori nel programma di edi-
ting Microsoft Visual C++ 2005 Express Edition.

Il programma, come cÕ• da aspettarsi, non viene compilato.


Il codice scritto • sintatticamente corretto, eppure il compilatore genera degli errori dovuti, come
si legge nella figura, al contesto operativo di due delle variabili utilizzate dal programma: infatti,
lÕerrore segnalato • Òidentificatore non dichiaratoÓ. Questo errore deriva dal fatto che la defini-
zione delle variabili lato e area si trova dopo la definizione delle funzioni che utilizzano tali va-
riabili.
Si tratta di un errore connesso a un problema che tecnicamente viene definito Òambito delle variabi-
liÓ, di cui parliamo nel prossimo paragrafo.

9.4 Ambito delle variabili


Tutti i programmi scritti nelle Unitˆ didattiche precedenti comprendevano solo una funzione, la fun-
zione main(), nella quale erano state dichiarate tutte le variabili di cui il programma aveva bisogno
e, insieme a esse, tutto il codice dellÕalgoritmo che le utilizzava. Una volta eseguita lÕultima istruzio-
ne, il programma aveva termine. Tutte le variabili in esso dichiarate vengono, per cos“ dire, distrutte

148
Unitˆ didattica 9 - Le funzioni

automaticamente allÕuscita dalla funzione, cio• viene perso ogni loro riferimento, quindi le variabili
appartenenti alla funzione main() vengono definite variabili locali in quanto sono state dichiarate
e utilizzate nel corpo della funzione stessa. Con lÕinserimento di nuove funzioni nel codice • neces-
sario definire il campo dÕazione (ambito) di ciascuna variabile, per controllare in quali funzioni deve
essere disponibile.
Gli errori elencati nel tentativo di compilazione del programma Quadrato.cpp spiegano che il
contesto in cui operano le variabili dichiarate • errato: infatti, nel codice tutte le variabili utilizzate
nel programma sono state dichiarate allÕinterno della funzione main(), quindi sono locali a essa e
in quanto tali possono essere utilizzate solo ed esclusivamente in tale ambito.
Le altre funzioni presenti nel codice non possono essere a conoscenza della loro presenza, quindi
richiamandone i nomi fanno riferimento a entitˆ che si trovano al di fuori del loro ambito operati-
vo: ecco il motivo degli errori.
A questo punto si rende necessario cambiare lÕambito delle variabili utilizzate dalle nuove funzio-
ni, dotandole di un livello di visibilitˆ pi• ampio: si rende necessario dar loro visibilitˆ globale, af-
finchŽ anche le altre funzioni oltre a main possano utilizzarle. Per questo vengono dette variabi-
li globali.

Le variabili globali vengono definite allÕinterno del contenitore del codice, non pi• al-
lÕinterno della funzione main() (o altre funzioni).

La modifica necessaria per evitare gli errori di compilazione visti prima • illustrata nellÕesempio che
segue.

Esempio Quadrato_corretto ..............................................................................................


Modificare il programma Quadrato.cpp in modo che, una volta compilato, non segnali al-
cun errore, come si vede nella figura della prova di esecuzione.

Codice
Quadrato_corretto.cpp
1 #include <iostream>
2 using namespace std;
3
4 //dichiara variabili lato e area
5 double lato;
6 double area;
7
8 void leggiLato()
9 {
10 //chiedi e leggi lato
11 cout << "\nInserisci la misura del lato ";
12 cin >>lato;
13 }
14
15 void calcolaQuadr()
16 {
17 //calcola area quadrato
18 area = lato * lato;
19 }
20

149
Sezione 3 - Organizzazione degli algoritmi

21 void scriviQuadr()
22 {
23 //scrivi area
24 cout << "\nArea quadrato = " << area;
25 }
26
27 //INIZIO
28 int main ()
29 {
30 //invoca le funzioni
31 leggiLato();
32 calcolaQuadr();
33 scriviQuadr();
34
35 //fine programma
36 cout << "\n\nFine ";
37 system ("pause");
38 return 0;
39 }

Prova di esecuzione

Analisi del codice


Alle righe 5 e 6 sono state dichiarate tutte le variabili necessarie al programma. Si tratta delle mede-
sime variabili usate nel programma precedente ma, per fare in modo che le funzioni possano utiliz-
zarle, sono state dichiarate al di fuori della funzione main(), quindi rese ÒvisibiliÓ (globali) a tutte
le altre funzioni.
Alla riga 8 compare la dichiarazione della prima funzione che, non restituendo alcun valore, • di ti-
po void; si chiama leggiLato() e non ha alcun parametro. Il corpo di questa funzione contiene
due istruzioni molto banali: la prima, alla riga 11, visualizza un messaggio di invito a inserire la mi-
sura del lato del quadrato; la seconda, alla riga 12, legge il numero inserito alla tastiera e lo assegna
alla variabile globale lato. Le altre due funzioni sono definite alle righe 15 e 21.
Infine, alla riga 28, cÕ• la giˆ nota funzione main() (entry point), alla quale • demandata la funzio-
ne di richiamare (invocare) le funzioni precedenti in sequenza, secondo la struttura che si • voluto
dare al programma.
...........................................................................................................................................

LÕinserimento delle funzioni allÕinterno del programma non segue alcun ordine particolare: possono es-
sere collocate, per esempio, in ordine inverso rispetto a quello precedentemente presentato, ma le loro
intestazioni devono sempre essere collocate prima della funzione main(). LÕimportante • che vengano
invocate secondo la struttura logica stabilita dal programmatore. Nel blocco di codice di una funzione
possono essere invocate tutte le funzioni esterne che si desidera, ma non possono essere dichiarate al-
tre funzioni, ovvero una funzione non pu˜ contenere unÕaltra funzione.

150
Unitˆ didattica 9 - Le funzioni

9.5 Variabili locali e globali


é stato pi• volte accennato al concetto di variabile locale e globale: ora • il caso di approfondire e
chiarire un problema di carattere formale che pu˜ essere riscontrato quando viene utilizzata una stes-
sa variabile in ambito sia locale sia globale.

Esempio Segno...................................................................................................................
Creare una funzione che cambi il segno a una variabile intera.

Codice
Segno.cpp
1 #include <iostream>
2 using namespace std;
3
4 void cambiaSegno ()
5 {
6 int num; //variabile locale
7 num = -num; //cambia segno alla variabile locale
8 }
9
10 //INIZIO
11 int main ()
12 {
13 int num = 125; //variabile globale
14
15 //invoca la funzione cambia segno
16 cambiaSegno();
17
18 //scrivi num
19 cout << "\nLa variabile num contiene " << num;
20
21 //fine programma
22 cout << "\n\nFine ";
23 system ("pause");
24 return 0;
25 }

Analisi del codice


Il programma non ha alcuna utilitˆ pratica, se non quella di fare capire in che modo le due variabili
dichiarate alla riga 6 e alla riga 13 vengono gestite.

Prova di esecuzione

151
Sezione 3 - Organizzazione degli algoritmi

Il C++, pur accertando la presenza di due variabili con lo stesso nome e tipo, ne ammette chiara-
mente l’uso, in quanto fanno parte di due ambiti completamente diversi: la variabile num definita al-
la riga 6 è stata dichiarata all’interno della funzione cambiaSegno(), quindi locale, mentre la va-
riabile num definita alla riga 13 è stata dichiarata come appartenente alla funzione main, quindi glo-
bale rispetto a tale funzione.
Il risultato dell’elaborazione è frutto dell’utilizzo della variabile locale che, in questo esempio, “vie-
ne nascosta” dal campo d’azione della variabile globale.
...........................................................................................................................................

9.6 Valori di ritorno


Le funzioni consentono, come già è stato detto, di strutturare in maniera logica il programma, im-
plementando singolarmente i vari algoritmi che insieme rappresentano la soluzione del problema. Le
funzioni viste finora sono state utilizzate nella loro forma base, che non prevede la restituzione di va-
lori al codice chiamante: infatti, nella loro intestazione sono state dichiarate void.
In realtà, la possibilità di gestire un valore frutto dell’elaborazione di una funzione è molto più con-
creta di quanto si possa credere. Una funzione può restituire solo un unico valore. L’intestazione di
una funzione che restituisce un valore al codice chiamante assume la seguente sintassi:
tipo nome-funzione()
{
//corpo della funzione
return variabile;
}
dove:

: tipo rappresenta uno dei tipi di dato C++ validi e indica il tipo della variabile che la funzione
restituisce al codice chiamante;
: return è la parola chiave dell’istruzione che restituisce un valore di quel tipo.

Di seguito è riportato un esempio.

Esempio Quadrato2 ...........................................................................................................


Quadrato2.cpp
1 #include <iostream>
2 using namespace std;
3
4 //dichiara variabili lato e area
5 double lato;
6 double area;
7
8 void leggiLato()
9 {
10 //chiedi e leggi lato
11 cout << "\nInserisci la misura del lato ";
12 cin >>lato;
13 }
14
15 double calcolaQuadr()
16 {

152
Unitˆ didattica 9 - Le funzioni

17 //calcola quadrato
18 return lato*lato;
19 }
20
21 void scriviQuadr()
22 {
23 //scrivi area
24 cout << "\nArea quadrato = " << area;
25 }
26
27 //INIZIO
28 int main ()
29 {
30 //invoca le funzioni
31 leggiLato();
32 area = calcolaQuadr();
33 scriviQuadr();
34
35 //fine programma
36 cout << "\n\nFine ";
37 system ("pause");
38 return 0;
39 }

Analisi del codice


Le modifiche significative sono due.
La prima • alla riga 18, dove il calcolo del quadrato non viene assegnato alla variabile area ma vie-
ne indicato come valore da ÒconsegnareÓ al programma principale: questo valore • associato alla fun-
zione calcolaQuadr.
La seconda • alla riga 32, in cui il risultato della funzione calcolaQuadr() viene assegnato alla va-
riabile area.
Si noti la differenza della sintassi tra le funzioni che non contengo lÕistruzione return e quelle che
la contengono: le prime, menzionate alle righe 31 e 33, da sole costituiscono unÕistruzione, mentre
le seconde sono utilizzate alla stregua di una variabile, in quanto restituiscono un valore, pertanto so-
no poste a destra dellÕoperatore di assegnazione.
...........................................................................................................................................

Per alcune funzioni lÕinserimento dellÕistruzione return non • indispensabile: se la funzione deve ese-
guire un compito che non richiede la ÒconsegnaÓ di un risultato al main, per essa non • richiesta lÕistru-
zione return. é il caso della funzione scriviQuadr(), che deve eseguire unÕoperazione di output. In-
vece, le funzioni che alla fine della loro esecuzione producono un risultato devono contenere lÕistruzione
return. Rientra in questa categoria anche la funzione leggiLato.

9.7 Passaggio dei parametri per valore


LÕinserimento delle funzioni, come si • visto, estende notevolmente la potenzialitˆ di unÕapplicazio-
ne, strutturandone la logica in piccole porzioni di codice richiamabili dalla struttura principale. Ma il
solo utilizzo delle funzioni che restituiscono un valore limita molto la progettazione di un program-
ma robusto e di facile manutenzione. Il discorso si amplia qualora si abbia la necessitˆ che una fun-
zione possa elaborare valori che il codice chiamante fornisce alla funzione stessa. Ci˜ implica che al-
la funzione vengano comunicati i valori necessari al suo corretto funzionamento.

153
Sezione 3 - Organizzazione degli algoritmi

Le funzioni fin qui utilizzate non prevedevano parametri ma, a volte, si ha la necessitˆ di dover co-
municare loro dei valori (argomenti o parametri) che vengono utilizzati dal codice della funzione
stessa. Come si ricorderˆ, lÕintestazione tipo di una funzione • la seguente:
tipo-di-ritorno nome-funzione([lista parametri])
{
blocco di codice della funzione
}
dove, oltre allÕeventuale tipo di ritorno e al nome della funzione, tra parentesi tonde compare una li-
sta (opzionale) di parametri; tale lista comprende una serie di coppie definite dal tipo e dal nome del-
la variabile, definite localmente alla funzione in questo modo:
tipo-di-ritorno nome-funzione([tipo1 var1, tipo2 var2,...])
{
blocco di codice
}
Ogni coppia • separata da una virgola. LÕinvocazione della funzione, dal canto suo, deve contenere
lÕesatta lista degli argomenti, dello stesso tipo, passati come parametri alla funzione, in questo modo:
nome-funzione (variabile1, variabile2, ...);

Esempio AreaRettangolo ...................................................................................................


Costruire una funzione che riceva come parametri la misura della base e dellÕaltezza di un
rettangolo e ne restituisca lÕarea.

Codice
AreaRettangolo.cpp
1 #include <iostream>
2 using namespace std;
3
4 double calcolaArea(double b, double h)
5 {
6 //calcola area rettangolo
7 return b*h;
8 }
9
10 //INIZIO
11 int main ()
12 {
13 double base;
14 double altezza;
15 double area;
16
17 //chiedi e leggi base
18 cout << "\nInserisci misura base ";
19 cin >>base;
20 //chiedi e leggi altezza
21 cout << "\nInserisci misura altezza ";
22 cin >>altezza;
23
24 //invoca le funzioni

154
Unitˆ didattica 9 - Le funzioni

25 area = calcolaArea(base, altezza);


26
27 //scrivi area
28 cout << "\nArea rettangolo = " << area;
29
30 //fine programma
31 cout << "\n\nFine ";
32 system ("pause");
33 return 0;
34 }

Prova di esecuzione

Analisi del codice


Alla riga 4 lÕintestazione della funzione specifica due parametri: b e h. Questi parametri vengono
considerati variabili locali alla funzione e ricevono dal programma principale i valori corrispondenti
alle variabili globali base e altezza, indicati al momento dellÕinvocazione della funzione.
Alla riga 25 viene invocata la funzione calcolaArea, e come argomenti vengono inserite le varia-
bili globali base e altezza: i valori di tali argomenti vengono memorizzati nei parametri b e h lo-
cali alla funzione.
...........................................................................................................................................

Lo schema che riassume graficamente il passaggio dei parametri per valore a una
funzione • riportato di seguito.
area = calcolaArea(base, altezza); base,altezza: argomenti della funzione

double calcolaArea(double b, double h); b,h: parametri della funzione

Gli argomenti passati come parametri alla funzione devono rispettare fedelmente sia il tipo di dato sia
lÕordine di dichiarazione dellÕintestazione della funzione, altrimenti il compilatore genera un errore
perchŽ non riesce a trovare la funzione da eseguire, non essendoci corrispondenza di tipo e di nu-
mero degli argomenti. Inoltre, nellÕintestazione della funzione non • consentito assegnare valori ai pa-
rametri.
Si ricordi che tutti i parametri passati per valore sono considerati locali alla funzione: al termine dellÕela-
borazione, dopo lÕesecuzione dellÕistruzione return, i loro valori non esisteranno pi•.

155
Sezione 3 - Organizzazione degli algoritmi

LÕinvocazione di una funzione determina lÕassegnazione degli argomenti ai relativi


parametri.
LÕassegnazione in questione • definita passaggio degli argomenti per valore,
in quanto a ogni parametro viene assegnato il corrispondente valore dellÕargo-
mento.

In altre parole, per ogni argomento viene generata un copia che viene assegnata al corrisponden-
te parametro; qualsiasi eventuale modifica di tale copia determinata dallÕesecuzione del codice del
corpo della funzione non si rifletterˆ in alcun modo sugli argomenti originariamente passati alla
funzione, come risulta chiaro dallÕesempio che segue.
Questo tipo di passaggio dei parametri consente dunque di rendere disponibile alla funzione un
valore indispensabile per le elaborazioni che avvengono al suo interno, ma le cui ÒvicissitudiniÓ
non sono di nessun interesse per il programma chiamante.

Esempio PerValore .............................................................................................................


Creare una funzione che incrementi il valore di due variabili intere passate alla funzione co-
me parametri per valore.

Codice
PerValore.cpp
1 #include <iostream>
2 using namespace std;
3
4 void incrementa(int num1, int num2)
5 {
6 cout<<"\n\n\t--- Ingresso nella funzione ---\n";
7 //incrementa i valori
8 ++num1;
9 ++num2;
10 cout<<"I parametri sono stati incrementati valgono "<<num1<<" "<<num2;
11 cout << "\n\t--- Uscita dalla funzione ---";
12 }
13 //INIZIO
14 int main ()
15 {
16 int numero1 = 0;
17 int numero2 = 0;
18
19 cout<<"\n\nArgomenti prima dell'invocazione = "<<numero1<<" "<<numero2;
20
21 incrementa(numero1,numero2); //invoca la funzione
22
23 cout<<"\n\nArgomenti dopo l'invocazione = "<<numero1<<" "<<numero2;
24
25 //fine programma
26 cout << "\n\nFine ";
27 system ("pause");
28 return 0;
29 }

156
Unitˆ didattica 9 - Le funzioni

Prova di esecuzione

Analisi del codice


Il codice del programma non esegue unÕelaborazione specifica, ma conferma ci˜ che • stato affer-
mato in precedenza. Alle righe 16 e 17 sono state dichiarate due variabili, rispettivamente nume-
ro1 e numero2 di tipo int. Tali variabili sono state, inoltre, inizializzate a 0 e alla riga 21 sono di-
ventate gli argomenti dellÕinvocazione della funzione incrementa la quale, nella sua intestazio-
ne, ha come parametri due variabili di tipo int. Come si vede nella prova di esecuzione, il pro-
gramma visualizza alcuni messaggi, che descrivono il flusso di elaborazione: si comincia con la vi-
sualizzazione dei valori degli argomenti prima dellÕinvocazione della funzione incrementa alla ri-
ga 19 della funzione main, cui fa seguito lÕinvocazione della funzione alla riga 21, che ÒdirottaÓ il
flusso di elaborazione alla riga 4, dove inizia la definizione della funzione incrementa; in que-
stÕultima viene richiesto per prima cosa (riga 6) di scrivere a video un messaggio che avvisa lÕutente
che si • entrati nella funzione. Alle righe 8 e 9 avviene lÕincremento dei parametri di ingresso; alla
riga 10 viene visualizzato un messaggio di conferma dellÕavvenuto incremento e infine, alla riga 11,
viene visualizzato un messaggio che segnala lÕuscita dalla funzione. Il flusso viene cos“ restituito al-
lÕistruzione successiva a quella dellÕinvocazione della funzione (cio• alla riga 23), dove viene visua-
lizzato il valore corrente delle variabili passate come argomenti dellÕinvocazione. Entrambi i valori
sono rimasti inalterati, a conferma che la funzione incrementa non ha alterato i valori delle va-
riabili numero1 e numero2.
...........................................................................................................................................

Il passaggio dei parametri per valore, in generale, va bene per moltissime applicazioni ma, spesso, si
ha la necessitˆ di dover utilizzare le funzioni per manipolare, cio• modificare, proprio i valori dei pa-
rametri passati a esse, affinchŽ il codice chiamante possa utilizzarli per successive elaborazioni. La
tecnica utilizzata per ottenere questo scopo viene detta passaggio dei parametri per riferimento, ed •
lÕargomento del prossimo paragrafo.

9.8 Passaggio dei parametri per riferimento


Con il passaggio dei parametri per valore, come si • visto, gli argomenti dellÕinvocazione della fun-
zione non vengono modificati. Si studia ora una tecnica che prevede la modifica dei parametri in in-
gresso alla funzione. LÕintestazione della funzione tradizionale cambia e viene cos“ modificata:
tipo-di-ritorno nome-funzione ([tipo1 &var1, tipo2 &var2,...])
{
blocco di codice
}

157
Sezione 3 - Organizzazione degli algoritmi

LÕintestazione generale della funzione • rimasta quasi inalterata, tranne che per la modalitˆ con cui
viene passato il parametro.

Il carattere Ò&Ó che precede il parametro indica che ogni eventuale modifica effet-
tuata su quel parametro si riflette sullÕargomento dellÕinvocazione.

Nel passaggio per riferimento la funzione riceve, infatti, lÕindirizzo della locazione di memoria che
contiene il valore da elaborare, sicchŽ ogni ÒvicissitudineÓ del parametro provoca la modifica del va-
lore della variabile originariamente passata alla funzione.
LÕinvocazione della funzione non subisce alcun cambiamento, come si pu˜ vedere nella sintassi che
segue.

nome-funzione (var1, var2, ...);

Esempio PerRiferimento .....................................................................................................


Per una prova concreta, viene modificato il codice del programma perValore.cpp in mo-
do che nella funzione incrementa i parametri vengano passati per riferimento e non pi•
per valore. Il programma viene rinominato in PerRiferimento.cpp.

Codice
PerRiferimento.cpp
1 #include <iostream>
2 using namespace std;
3
4 void incrementa(int &num1, int &num2)
5 {
6 cout<<"\n\n\t--- Ingresso nella funzione ---\n";
7 //incrementa i valori
8 ++num1;
9 ++num2;
10 cout<<"I parametri sono stati incrementati valgono "<<num1<<" "<<num2;
11 cout << "\n\t--- Uscita dalla funzione ---";
12 }
13 //INIZIO
14 int main ()
15 {
16 int numero1 = 0;
17 int numero2 = 0;
18
19 cout<<"\n\nArgomenti prima dell'invocazione = "<<numero1<<" "<<numero2;
20
21 incrementa(numero1,numero2); //invoca la funzione
22
23 cout<<"\n\nArgomenti dopo l'invocazione = "<<numero1<<" "<<numero2;
24
25 //fine programma
26 cout << "\n\nFine ";
27 system ("pause");
28 return 0;
29 }

158
Unitˆ didattica 9 - Le funzioni

Prova di esecuzione

Analisi del codice


La struttura del programma • pressochŽ identica a quella del precedente: • stata cambiata solo lÕin-
testazione della funzione incrementa.
Alla riga 4, nellÕintestazione della funzione, compaiono ora i due parametri passati per riferimento,
tramite lÕoperatore di indirizzamento &.
Al momento dellÕinvocazione, alla riga 21, vengono passati alla funzione non pi• le copie dei valori
degli argomenti ma un loro riferimento alla zona di memoria in cui sono stati definiti. La funzione
riceve tali riferimenti e, dopo lÕelaborazione, ne modifica il contenuto che, ovviamente, si riflette an-
che sugli argomenti dellÕinvocazione.
...........................................................................................................................................

Di seguito un altro semplice esempio.

Esempio Scambia ..............................................................................................................


Scrivere un programma che effettui lo scambio del contenuto di due variabili. Il programma
prevede lÕacquisizione dei due valori tramite la tastiera.

Codice
Scambia.cpp
1 #include <iostream>
2 using namespace std;
3
4 //variabili globali
5 int num1=0;
6 int num2=0;
7
8 void leggiNum()
9 {
10 cout << "\nInserire il primo valore ";
11 cin >> num1;
12
13 cout << "\nInserire il secondo valore ";
14 cin >> num2;
15 }
16
17 void scambia(int &n1, int &n2)
18 {

159
Sezione 3 - Organizzazione degli algoritmi

19 int temp;
20 //effettua lo scambio
21 temp=num1;
22 num1=num2;
23 num2=temp;
24 }
25
26 //INIZIO
27 int main ()
28 {
29 leggiNum(); //acquisisci i valori
30 cout << "\n\nArgomenti prima dell'invocazione = " << num1 << " " << num2;
31 scambia(num1, num2); //invoca la funzione per lo scambio
32 cout << "\nArgomenti dopo l'invocazione = " << num1 << " " << num2;
33
34 //fine programma
35 cout << "\n\nFine ";
36 system ("pause");
37 return 0;
38 }

Prova di esecuzione

Analisi del codice


Il codice prevede due funzioni oltre a quella principale.
La prima è LeggiNum(), alla riga 8, che viene invocata dalla funzione main alla riga 29 e consente
la lettura da tastiera dei due valori che devono essere scambiati.
La seconda è scambia, alla riga 17, che viene invocata dalla funzione main alla riga 31 ed effettua
lo scambio dei valori con l’ausilio della variabile locale temp.
Tale funzione riceve i paramentri per riferimento quindi, una
volta usciti dalla funzione, i valori delle due variabili risultano argomenti num1 num2
effettivamente scambiati.
Si può notare che gli argomenti dell’invocazione e i relativi pa-
rametri hanno nomi differenti, ma ciò non ha alcuna importan-
za perché, essendo gli argomenti passati per riferimento, en- memoria valore1 valore2
trambi “puntano” alle locazioni di memoria delle rispettive va-
riabili, come si può evincere dallo schema a destra. E
Quindi, ogni modifica dei parametri della funzione si riflette au- parametri n1 n2
tomaticamente sugli argomenti dell’invocazione.
...........................................................................................................................................

160
Unitˆ didattica 9 - Le funzioni

9.9 Ricorsione
In C++, cos“ come in diversi altri linguaggi, una funzione pu˜ richiamare se stessa.

Quando, all'interno del corpo di una funzione, un'istruzione richiama la funzione stes-
sa, tale funzione si dice ricorsiva.

La ricorsione • un processo che definisce qualcosa in termini di se stesso e in alcuni casi viene chia-
mata definizione circolare. In matematica • facile incontrare formule ricorsive.
Prendiamo come esempo il giˆ noto fattoriale di un numero intero: il fattoriale di un numero n • il
prodotto di tutti i numeri interi da 1 a n. Il fattoriale di 3 • l × 2 × 3, ovvero 6. Il fattoriale di n si in-
dica con n!.

Preso un numero n possiamo scrivere:


fattoriale(n) = n! = 1 × 2 × 3 × ... n ;
oppure possiamo esprimere lo stesso concetto con una formula ricorsiva:

1 se n = 0 o n = 1
fattoriale(n) = n! =
n × fattoriale(n Ð 1) per ogni altro valore di n

Osservando la formula si vede che la condizione di arresto della ricorsione • n = 1 e che, nella parte
a destra del segno di uguale, ÒricorreÓ o ÒricompareÓ lÕinvocazione della funzione che deve essere
calcolata.

Esempio Fattoriale .............................................................................................................


Prendiamo lÕesempio del calcolo del fattoriale mostrando due funzioni diverse, ma equiva-
lenti: la prima presenta lÕutilizzo di una struttura iterativa, la seconda sfrutta il concetto di
ricorsione.

Codice
Fattoriale.cpp
1 #include "iostream"
2 using namespace std;
3
4 int fatt_iteraz(int n)
5 {
6 //variabile locale e inizializzazione
7 int fatt = 1;
8 //esegui il ciclo for
9 for (int i=1; i<=n; i++)
10 {
11 fatt = fatt * i;
12 }
13 return fatt;
14 }
15
16 int fatt_ricors (int n)
17 {
18 //variabile locale

161
Sezione 3 - Organizzazione degli algoritmi

19 int fatt;
20 if (n == 1)
21 return 1;
22 //la funzione richiama se stessa
23 fatt = n*fatt_ricors(n-1);
24 return fatt;
25 }
26
27 //INIZIO
28 int main ()
29 {
30 int num;
31 //chiedi e leggi il numero su cui calcolare il fattoriale
32 cout << "\nInserisci un numero intero (Max 10) = ";
33 cin >> num;
34
35 //scrivi il risultato della funzione con iterazione
36 cout << "\n\nFattoriale per iterazione = " << fatt_iteraz (num);
37
38 //scrivi il risultato della funzione con ricorsione
39 cout << "\nFattoriale per ricorsione = " << fatt_ricors (num);
40
41 //fine programma
42 cout << "\n\nFine ";
43 system ("pause");
44 return 0;
45 }

Prova di esecuzione

Analisi del codice


Il codice contiene due funzioni: la prima • fatt_iteraz(int n), che calcola il fattoriale di n con
un algoritmo iterativo ed • definita a partire dalla riga 4; la seconda • fatt_ricors(int n), che
• di tipo ricorsivo ed • definita a partire dalla riga 16.
I valori restituiti dalle due funzioni sono scritti dalle istruzioni delle righe 36 e 39 (rispettiva-
mente).
La versione non ricorsiva della funzione dovrebbe risultare chiara: utilizza un ciclo for che va
da 1 a n e moltiplica progressivamente ogni numero per il prodotto accumulato nella variabi-
le fatt.

162
Unitˆ didattica 9 - Le funzioni

LÕoperazione svolta dalla versione ricorsiva fatt_ricors() • un poÕ pi• complessa.


Quando fatt_ricors() viene richiamata con argomento uguale a 1, la funzione restituisce 1; in
ogni altro caso la funzione restituisce il prodotto n * fatt_ricors(n-1), per valutare il quale
viene nuovamente richiamata la funzione fatt_ricors(). Questo avviene finchŽ n non diventa
uguale a 1 e la funzione inizia a restituire un valore.
A partire dal primo valore restituito dalla funzione, finalmente potranno essere calcolati tutti i valori
Òrimasti in sospesoÓ dalla chiamata ricorsiva, fino ad arrivare al risultato richiesto. é una specie di
Ògioco delle scatole cinesiÓ.
Supponiamo di voler calcolare il fattoriale di 2. La prima chiamata a fatt_ricors() provoca una
seconda chiamata ricorsiva con argomento uguale a 1, che restituisce il valore 1 che viene poi molti-
plicato per 2 (il valore originale di n). Il risultato sarˆ quindi 2.
Si pu˜ provare a seguire il procedere del calcolo del fattoriale per esempio del numero 3 inserendo
istruzioni cout nelcodice della funzione fatt_ricors(), per verificare il livello raggiunto da
ogni chiamata ed i vari risultati intermedi.
Quando una funzione richiama se stessa sullo stack viene allocata una nuova serie di variabili loca-
li e parametri, e il codice della funzione viene eseguito nuovamente dallÕinizio utilizzando tali va-
riabili. Una chiamata ricorsiva non crea una nuova copia della funzione: solo i valori su cui opera
sono nuovi. Quando ogni istanza della funzione esegue il return le sue variabili locali e i suoi pa-
rametri vengono rimossi dallo stack e lÕesecuzione riprende nel punto in cui la funzione aveva ri-
chiamato se stessa.
La progettazione di una funzione ricorsiva deve essere condotta con molta attenzione. Infatti, se da
una parte la ricorsione rende pi• efficiente e veloce il programma, dallÕaltra pu˜ rivelarsi una fonte di
errori di esecuzione. Il pi• comune errore in cui si pu˜ incorrere utilizzando la ricorsione • ÒlÕentra-
ta in loopÓ del programma: pu˜ verificarsi, cio•, che lÕesecuzione del programma Òrimanga confina-
taÓ allÕinterno delle istruzioni della funzione ricorsiva, richiamandola Òin eternoÓ. Le osservazioni ri-
portate di seguito saranno sicuramente utili per evitare di progettare funzioni ricorsive ÒdifettoseÓ,
oltre che per imparare a utilizzare al meglio questo utile strumento.

1. Quando si scrivono funzioni ricorsive, da qualche parte si deve prevedere unÕistruzione condizionale
(per esempio un if) che faccia in modo che la funzione termini senza eseguire alcuna ricorsione. Se
si omette tale istruzione condizionale, una volta entrati nel corpo della funzione non sarˆ pi• possibi-
le uscirne. Durante lo sviluppo del programma si consiglia di utilizzare abbondantemente le istruzio-
ni cout, in modo da sapere sempre cosa sta avvenendo e annullare lÕesecuzione nel momento in cui
si rileva un errore.
2. Spesso le routine ricorsive non riducono in modo significativo le dimensioni del codice nŽ migliorano
lÕutilizzo della memoria, rispetto alla versione iterativa. Inoltre, le versioni ricorsive della maggior par-
te delle routine vengono eseguite in modo leggermente pi• lento rispetto alle loro equivalenti iterati-
ve, a causa del sovraccarico dovuto alla continua istanziazione delle funzioni.
3. Il vantaggio principale delle funzioni ricorsive consiste nella possibilitˆ di creare versioni pi• chiare e
semplici degli algoritmi. Per esempio, lÕalgoritmo QuickSort • piuttosto difficile da implementare in
modo iterativo; inoltre, alcuni problemi, specialmente quelli di intelligenza artificiale, sono pi• adatti
a soluzioni ricorsive; infine, spesso lÕesecuzione ricorsiva risulta essere pi• lineare dellÕanalogo ite-
rativo.

...........................................................................................................................................

9.10 Funzioni matematiche


Oltre alle funzioni definite dal programmatore, per quanto riguarda le funzioni matematiche, C++
mette a disposizione la libreria <cmath> che contiene unÕampia gamma di funzioni per le applica-
zioni di tipo scientifico.
Ecco una breve descrizione delle funzioni pi• usate di <cmath>.

163
Sezione 3 - Organizzazione degli algoritmi

Fanno parte di questa libreria tutte le funzioni trigonometriche e le funzioni di matematica analitica,
che sono così immediatamente disponibili al programmatore.

FUNZIONE DESCRIZIONE
double sqrt(double x) EFx Radice quadrata
double pow(double x,double y) y Elevamento a potenza
Se x > 0, y può essere un valore qualsiasi
Se x è 0, y deve essere > 0
Se x < 0, y deve essere un intero
double sin(double x) senx Seno di x (x in radianti)
double cos(double x) cosx Coseno di x (x in radianti)
double tan(double x) tanx Tangente di x (x in radianti)
double asin(double x) sen-1 x Arco seno di x (x∈[-1,1])
double acos (double x) cos-1 x Arco coseno di x (x∈[-1,1])
double atan(double x) tan-1 x Arco tangente di x
double exp(double x) ex Esponenziale di x
double log(double x) log(x) Logaritmo naturale di x (x > 0)
double log10(double x) log10(x) Logaritmo decimaledi x (x>0)
double ceil(double x) Intero più piccolo > x
double floor(double x) Intero più grande < x
double fabs(double x) |x| Valore assoluto di x

164
Unità didattica 9 - Le funzioni

Esercizi
Unità didattica 9

1 Descrivere la struttura dell’intestazione di una funzione.

2 Scrivere una funzione che calcoli e scriva l’area di un trapezio. All’interno della funzione sono presenti
l’acquisizione dei dati (base e altezza) e la scrittura dell’output (area).

3 Scrivere una funzione che calcoli e scriva l’area di un trapezio e che abbia come parametri le basi e l’al-
tezza.

4 Scrivere una funzione che acquisisca da tastiera i dati dell’esercizio precedente e stabilire l’esatta mo-
dalità di passaggio dei parametri.

5 Nella seguente porzione di codice compare un errore: individuare quale.


void Areaquadrato()
{
int area=0;
area=lato*lato;
return area;
}

6 Nella seguente funzione, che calcola il quadrato di un numero intero, compare un errore: individuarlo e
correggerlo.
int Quad(int numero)
{
int quad = 0;
quad=numero*numero;
return numero;
}

7 Scrivere una funzione che stabilisca l’aliquota IVA in relazione ai seguenti importi:
: importo fino a € 100.000 → IVA 20%;
: importo compreso tra € 100.000 fino a € 200.000 → IVA 18%;
: importo superiore a € 200.000 → IVA 16%.

8 Confermare la seguente affermazione: “I parametri passati per riferimento forniscono alla funzione una
copia esatta degli argomenti”.

9 Definire una funzione che, dato il prezzo di vendita di un prodotto e l’aliquota IVA, presenti il prezzo com-
prensivo di IVA.

q0 Definire una funzione che, dato il prezzo di vendita di un prodotto comprensivo di IVA, restituisca il prez-
zo al netto dell’IVA e l’ammontare dell’IVA (Aliquota Iva costante = 20%).

qa Dato in input un numero N, definire una funzione che restituisca la somma dei numeri dispari minori o
uguali a N.

qs Scrivere una funzione che abbia come parametri le coordinate di un punto sul piano cartesiano e che co-
munichi in output il valore della distanza del punto dall’origine degli assi.

165
Sezione 3 - Organizzazione degli algoritmi

Esercizi
Unità didattica 9

qd Data l’altezza in cm e il peso in kg di una persona, definire una funzione che scriva il messaggio:
“sottopeso” se altezza – 100 > peso;
“normale” se altezza – 100 = peso;
“soprappeso” se altezza – 100 < peso.

qf Scrivere una funzione che abbia come parametri le coordinate di un punto sul piano cartesiano e che re-
stituisca il valore della distanza del punto dall’origine degli assi.

qg Scrivere una funzione che, acquisito in input un numero, calcoli e scriva in output il quadrato e la radice
quadrata del numero (utilizzare le funzioni matematiche della libreria <cmath>).

qh Scrivere una funzione che, acquisito in input un numero, calcoli e restituisca al main come parametri il
quadrato, il cubo, la radice quadrata e la radice cubica del numero (utilizzare le funzioni matematiche del-
la libreria <cmath>).

qj Scrivere una funzione che abbia come parametri le coordinate cartesiane di due punti e che restituisca
la distanza dei due punti.

qk Scrivere una funzione che abbia come parametri le coordinate cartesiane di due punti e che calcoli le
coordinate del punto medio del segmento che unisce i due punti (scegliere il tipo di passaggio di para-
metri opportuno).

ql Dato un elenco di persone con l’indicazione del nome e dell’altezza, calcolare l’altezza media. Utilizzare
una funzione per l’acquisizione dei dati di input.

w0 Dato un elenco di persone con l’indicazione del nome e dell’altezza, indicare il nome del più alto e del
più basso. Utilizzare una funzione per l’acquisizione dei dati di input.

166
Sezione 4
Strutture dei dati


Obiettivi
generali
◊ Definire insiemi di dati numerabili
◊ Saper utilizzare schemi logici per organizzare insiemi
complessi di dati
◊ Costruire tabelle di dati omogenei
◊ Manipolare stringhe di caratteri

&
◊ Raggruppare dati di tipo diverso

Questa sezione contiene


U.D.10 Enumerazioni e array
U.D.11 Stringhe e strutture
10
Unitˆ didattica
Enumerazioni e array

CHE COSA IMPARERAI A FARE

$ Definire dati di tipo enumerativo


$ Definire un array a una dimensione in C++
$ Caricare un vettore in memoria
$ Definire un vettore a dimensione variabile
$ Definire una matrice
$ Popolare una matrice
$ Visualizzare gli elementi di una matrice

CHE COSA DOVRAI STUDIARE

$ Definizione di tipo enumerativo


$ Concetto di vettore
$ Definizione di vettore
$ Sintassi per la gestione di un vettore
$ Sintassi per la gestione delle matrici
Unitˆ didattica
Enumerazioni e array
10
10.1 Introduzione
Finora abbiamo visto i tipi di dato pi• comuni e i pi• facili da utilizzare in C++, cio• i tipi predefini-
ti: int, double, bool ecc.
In C++, per˜, si possono anche utilizzare tipi definiti dal programmatore. Tali nuovi tipi, per poter
essere utilizzati, vanno inseriti nella dichiarazione delle variabili secondo la sintassi consueta:
tipo variabile;
Tutti i dati che compaiono in un programma possiedono uno e un solo tipo, e possono essere di ti-
po semplice oppure aggregati in modo complesso.
I tipi di dato semplici sono classificati secondo lo schema gerarchico riportato di seguito.

semplici

reali ordinali

float double enumerativi predefiniti

int char bool

I tipi semplici possono essere float o double, oppure di tipo ordinale; i tipi ordinali possono es-
sere definiti dal programmatore attraverso i tipi enumerativi, oppure possono appartenere ai tipi or-
dinali predefiniti int, bool, char.

Il tipo ordinale si chiama cos“ perchŽ descrive un insieme finito e ordinato di valori,
che possono essere associati a numeri interi positivi.

I tipi ordinali predefiniti e i tipi float e double sono giˆ stati presentati e utilizzati in prece-
denza.

10.2 Tipi enumerativi (enumerazioni)


A volte una variabile pu˜ assumere solo una serie di valori definiti allÕinterno di un insieme discreto
di possibilitˆ. Le enumerazioni sono molto comuni nella vita di tutti i giorni: per esempio, pu˜ esse-
re unÕenumerazione la lista dei controlli da eseguire sullÕauto prima di affrontare un viaggio: freni,
fari, gomme, olio, tergicristalli, carburante.

169
Sezione 4 - Strutture dei dati

Per definire in C++ unÕenumerazione si usa la seguente sintassi:


enum nome_enumerazione {elenco_enumerazioni};
dove enum • la parola chiave che introduce lÕenumerazione.
Riprendendo la lista di esempio proposta in precedenza, si pu˜ scrivere:

enum controlli {freni, fari, gomme, olio, tergicristalli, carburante};

Il nome dellÕenumerazione pu˜ essere utilizzato per dichiarare variabili di tale tipo, in maniera ana-
loga alle dichiarazioni di tipo viste in precedenza. Per esempio, potremo scrivere:

controlli check;

La variabile check pu˜ assumere uno qualsiasi dei valori della lista dei controlli definita in prece-
denza. Per esempio, si pu˜ scrivere:

check = gomme;

oppure

if (check == fari) cout << Ócontrolla fariÓ;

Si deve ricordare che a ognuno dei valori di una variabile enumerativa corrisponde il numero dÕordi-
ne che esso occupa allÕinterno della definizione dellÕenumerazione.

Esempio Enumera...............................................................................................................
Scrivere un programma che utilizza una variabile di tipo controlli.

Codice
Enumera.cpp
1 #include <iostream>
2 using namespace std;
3
4 //INIZIO
5 int main ()
6 {
7 //definisci l'enumerazione
8 enum controlli {freni, fari, gomme, olio, tergicristalli, carburante};
9
10 //definisci la variabile di tipo enumerativo
11 controlli check;
12
13 //assegna un valore alla variabile
14 check = gomme;
15
16 //esegui un confronto
17 if (check == gomme) cout << "controlla fari";
18
19 //scrivi il numero d'ordine del valore della variabile enumerativa
20 cout << "\nNumero d'ordine di gomme = " << check;
21

170
Unitˆ didattica 10 - Enumerazioni e array

22 //fine programma
23 cout << "\n\nFine ";
24 system ("pause");
25 return 0;
26 }

Prova di esecuzione

Analisi del codice


Alla riga 8 viene definito il tipo enumerativo controlli.
Alla riga 11 viene introdotta la variabile check di tipo controlli e alla riga 14 le viene assegnato
un valore (scelto tra quelli definiti nellÕenumerazione controlli).
Alla riga 17 viene eseguito un confronto. Anche se la variabile check contiene ÒgommeÓ, lÕistruzio-
ne alla riga 20 non fa comparire in output la parola ÒgommeÓ, ma il suo numero di posizione (par-
tendo da zero) allÕinterno della definizione dellÕenumerazione.
...........................................................................................................................................

Fino a questo momento abbiamo visto come sia possibile definire e utilizzare in C++ tipi di dati che
abbiamo definito ÒsempliciÓ.
Se ci soffermiamo, per˜, a considerare qualche esempio un poÕ pi• complesso di quelli presentati fi-
no a ora ci rendiamo rapidamente conto che non • affatto raro incontrare la necessitˆ di gestire elen-
chi di dati.

Pensiamo per esempio a un elenco di invitati a una festa, alla lista degli studenti di una classe o agli
iscritti a una gara. In questi tre esempi siamo di fronte a un dato (cognome e nome) sempre dello stes-
so tipo che si ripete pi• volte.

Nei prossimi paragrafi presenteremo le strutture messe a disposizione da C++ per gestire non pi• da-
ti singoli, bens“ strutture di dati che raggruppano in unÕunica variabile dati diversi.

10.3 Tipo vettore


Cominciamo a esaminare le strutture che contengono dati tutti dello stesso tipo: in generale, tali
strutture prendono il nome di array e si distinguono in vettori come array a una dimensione e ma-
trici come array a due dimensioni.
La prima struttura che esaminiamo • la struttura di vettore che permette di raggruppare diversi dati
dello stesso tipo e di associare un indice a ogni dato.

Un vettore • un insieme ordinato di oggetti omogenei, ovvero appartenenti a un


unico tipo.
é possibile accedere ai vari oggetti del vettore utilizzando il loro indice numerico.
Il numero di elementi contenuto nel vettore • indicato come dimensione.

171
Sezione 4 - Strutture dei dati

Prendiamo, per esempio, l’elenco degli studenti di una classe, così come appare sul registro.

1 Abbiati Mario
2 Bonaldi Piera
3 Casati Luigi
4 Esposito Salvatore
.. ...
.. ...
24 Vivaldi Giuseppe

A ogni studente è associato un numero di posizione e ogni numero individua uno studente.
In questo esempio, ogni elemento del vettore è di tipo stringa (per contenere il cognome e nome del-
lo studente) e ogni elemento è individuato da un numero, detto indice. La dimensione del vettore è 24.
Oppure consideriamo un quadrilatero irregolare in cui ogni lato ha una misura diversa.

4
3

1 2

Possiamo scrivere: E LATO MISURA


1 15
2 8
3 7
4 16

Ogni elemento dell’esempio precedente è un dato di tipo numerico (la misura del lato) e per ogni
misura è presente un indice che è il numero del lato; la dimensione è 4.

10.4 Vettori in C++


L’istruzione per definire un vettore ha il seguente formato:
tipo nomeVettore [dimensione];
dove:
: tipo specifica il tipo di dato comune a tutte le componenti;
: nomeVettore è il nome collettivo delle componenti del vettore;
: dimensione è il numero degli elementi contenuti nel vettore.

Riprendendo gli esempi del paragrafo precedente, per definire il vettore che contiene i nomi dei 24
studenti di una classe possiamo scrivere:
int dim = 24;
string Studenti [dim];

172
Unitˆ didattica 10 - Enumerazioni e array

Per definire le misure dei lati di un quadrilatero:


int dim=4;
int misure [dim];

é buona norma definire la dimensione del vettore come variabile (nel nostro caso: dim): in questo
modo eventuali variazioni della dimensione richiedono un solo intervento nel codice.

Si ricordi che lÕintervallo dei valori possibili per lÕindice di un vettore parte da 0 e arri-
va fino a dimensione Ð1.

In base a questa regola, nellÕesempio dellÕelenco degli studenti il primo studente (Abbiati Mario) •
individuato dallÕindice 0 e lÕultimo (Vivaldi Giuseppe) dallÕindice 23.
Quando si lavora con una variabile di tipo vettore, occorre sempre, allÕinterno del programma, indi-
care sia il nome della variabile sia lÕindice che individua la componente del vettore che vogliamo trat-
tare; per esempio, per assegnare al primo elemento del vettore la misura del primo lato, si scrive:
misure[0] = 15

Si noti che lÕindice va indicato racchiuso tra parentesi quadre dopo il nome della variabile.
Per assegnare a ciascun lato la misura corrispondente, si scrive:
misure[1] = 8
misure[2] = 7
misure[3] = 16

Dal punto di vista concettuale, possiamo pensare che in memoria • presente una struttura di questo
tipo:
VETTORE
misure → 15 8 7 16
indice → 0 1 2 3

Alcuni esempi di istruzioni sugli elementi di un vettore


Sempre riferendosi alle misure dei lati di un quadrilatero, per scrivere la misura del terzo lato si uti-
lizza la seguente istruzione:
cout << Òmisura terzo lato = Ó << misure[2];

Per sommare le misure del primo e del quarto lato, si utilizza la seguente istruzione:

somma = misure[0]+ misure[3]

Spesso nasce lÕesigenza di accedere a tutti gli elementi di un vettore o, come si usa dire, di ÒvisitareÓ
tutti gli elementi del vettore per poter eseguire una elaborazione su ciascuno di essi. In questo caso,
torna molto utile ricorrere alla struttura della ripetizione con contatore: attraverso di essa si utilizza
un indice che assume tutti i valori che vanno da 0 a dimensione Ð1 del vettore e che serve per riferir-
si a ciascun elemento.

Esempio Perimetro .............................................................................................................


Dopo avere definito e inizializzato un vettore che contenga le misure dei lati di un quadrila-
tero, visualizzare la misura del perimetro.

173
Sezione 4 - Strutture dei dati

Descrizione della soluzione


Le istruzioni per definire lÕarray e per assegnargli i valori iniziali sono giˆ state viste in questo para-
grafo. Per sommare tutti i lati del quadrato si definisca un ciclo for con lÕindice che va da 0 a 3 e che
permette di accedere a tutti e quattro i lati del quadrilatero, vale a dire a tutti e quattro gli elementi
del vettore delle misure.

Pseudocodifica
//struttura dati
vettore delle misure dei lati
//input
//tutti i dati sono definiti allÕinterno del programma
//output
perimetro
//variabili di lavoro
i //indice per ciclo for

INIZIO
//inizializza il vettore delle misure
misure[0] ← 15
misure[1] ← 8
misure[2] ← 7
misure[3] ← 16

//inizializza perimetro
perimetro ← 0

//visita il vettore
PER i DA 0 A dim Ð 1
perimetro = perimetro + misure[i] //somma le misure ad 1 ad 1
FINE PER
scrivi (Perimetro)
FINE

Codice
Perimetro.cpp
1 #include <iostream>
2 using namespace std;
3
4 //INIZIO
5 int main ()
6 {
7 //struttura del vettore
8 const int dim=4;
9 int misure[dim];
10
11 int i; //contatore per ciclo for
12 int perim; //dato di output
13
14 //inizializza il vettore misure

174
Unitˆ didattica 10 - Enumerazioni e array

15 misure[0] = 15;
16 misure[1] = 8;
17 misure[2] = 7;
18 misure[3] = 16;
19
20 //inizializza perimetro
21 perim = 0;
22
23 //visita il vettore
24 for(i=0; i<dim; i++)
25 {
26 perim = perim + misure[i]; //somma le misure ad 1 ad 1
27 }
28
29 //scrivi perimetro
30 cout<<"Perimetro = "<<perim;
31
32 //fine programma
33 cout << "\n\nFine ";
34 system ("pause");
35 return 0;
36 }

Prova di esecuzione

Analisi del codice


Alla riga 8 viene indicata la dimensione del vettore, che in questo caso • costante, e alla riga 9 viene
definita la struttura del vettore di nome misure e di dimensione dim.
Alle righe da 15 a 18 viene inizializzato il vettore misure assegnando un valore a ogni componente.
Alla riga 24 comincia la ÒvisitaÓ del vettore grazie al ciclo for (con lÕindice che, a partire da 0, assu-
me tutti i valori minori di dim).
Alla riga 26 la visita consiste nel sommare in perim ogni elemento del vettore misure.

Le istruzioni di inizializzazione usate nellÕesempio precedente possono essere sostituite da una scrittu-
ra pi• compatta. Nel codice Perimetro.cpp le righe da 15 a 18 servono per assegnare i valori iniziali
alle componenti del vettore misure. Le riportiamo di seguito.
misure[0] = 15;
misure[1] = 8;
misure[2] = 7;
misure[3] = 16;
LÕoperazione precedente pu˜ essere realizzata assegnando direttamente i valori delle componenti in fa-
se di definizione del vettore. Quindi le sette righe di codice
7 const int dim=4;
8 int misure[dim];

175
Sezione 4 - Strutture dei dati

. . .
14 //inizializza il vettore misure
15 misure[0] = 15;
15 misure[1] = 8;
16 misure[2] = 7;
17 misure[3] = 16;
18 . . .

possono essere sostituite dalla seguente scrittura pi• ÒveloceÓ:

int [] misure = new int [] {15, 8, 7, 16};

Si noti che non • neppure necessario specificare la dimensione del vettore.

...........................................................................................................................................

10.5 Caricamento di un vettore in memoria


Nel paragrafo precedente • stato illustrato un esempio di trattamento di un vettore, ipotizzando che
i suoi valori fossero caricati allÕinterno del codice del programma. NellÕesempio che segue, viene il-
lustrato come si pu˜ fare in modo che sia lÕutente a caricare direttamente in un vettore i valori che
desidera. Inizialmente, consideriamo il caso in cui la dimensione del vettore • nota a priori.

Esempio arrayCarica .........................................................................................................


Consentire lÕinserimento da tastiera dei valori di un vettore di interi con dimensione fissa = 5.

Descrizione della soluzione


Dopo avere definito un vettore di 5 interi, si eseguono due ÒvisiteÓ sugli elementi del vettore: la pri-
ma contiene il blocco di istruzioni che permette di caricare i dati inseriti da tastiera in ciascun ele-
mento del vettore; la seconda viene utilizzata per visualizzare i dati caricati.

Pseudocodifica
//costanti
dimensione = 5
//struttura dati
vettore di 5 interi
//input
serie di 5 numeri
//output
vettore di 5 interi

INIZIO
PER ogni elemento del vettore
richiedi dato
carica dato
FINE PER
PER ogni elemento del vettore
scrivi (elemento)
FINE PER
FINE

176
Unitˆ didattica 10 - Enumerazioni e array

Codice
arrayCarica.cpp
1 #include <iostream>
2 using namespace std;
3
4 //INIZIO
5 int main ()
6 {
7 //definisci il vettore di 5 elementi
8 const int dim = 5;
9
10 //definisci la struttura dati
11 int vett [dim];
12
13 //ciclo for x caricare vettore
14 for (int i=0; i<dim; i++)
15 {
16 //richiedi un dato
17 cout<<"Inserisci un dato ";
18 cin>>vett[i];
19 }
20
21 cout<<"\nContenuto del vettore: ";
22
23 //visita il vettore
24 for (int i=0; i<dim; i++)
25 {
26 cout<<" "<<vett[i]; //scrivi elemento di posto i
27 }
28 cout<<"\nVettore visualizzato";
29
30 //fine programma
31 cout << "\n\nFine ";
32 system ("pause");
33 return 0;
34 }

Prova di esecuzione

Analisi del codice


Alla riga 14 viene introdotto il ciclo for per il caricamento dei dati nel vettore. LÕindice i • definito
in modo ÒlocaleÓ rispetto allÕistruzione for.

177
Sezione 4 - Strutture dei dati

Alla riga 18 il dato inserito da tastiera viene assegnato allÕelemento vett con indice i, dove i, gra-
zie al ciclo for, assume, di volta in volta, i valori che vanno da 0 a 4.
Alla riga 24 la scrittura degli elementi del vettore viene realizzata con un secondo ciclo for.
Alla riga 26 ogni elemento del vettore viene scritto sulla stessa riga dei precedenti. Solo alla fine del
ciclo for, alla riga 28, si va a capo per scrivere una nuova riga di messaggio.
...........................................................................................................................................

10.6 Array di dimensione variabile


Allo scopo di fornire allÕutente strumenti flessibili, vediamo di perfezionare ulteriormente gli esem-
pi fin qui visti, in modo da realizzare un programma che permetta allÕutente di caricare un vettore la
cui dimensione • definita dallÕutente stesso. NellÕesempio del paragrafo precedente • stata data la
possibilitˆ di caricare i dati di un array da tastiera, ma la dimensione del vettore era definita come co-
stante uguale a 5. Nulla vieta, per˜, di acquisire da tastiera la dimensione del vettore e di definire il
vettore stesso solo dopo avere avuto a disposizione la dimensione scelta dallÕutente.

Esempio arrayVaria............................................................................................................
Permettere lÕinserimento da tastiera dei valori di un vettore di interi. Anche la dimensione
del vettore • acquisita da tastiera e deve essere inferiore a 20.

Descrizione della soluzione


LÕalgoritmo e il programma che viene presentato si discostano dal precedente soltanto perchŽ la di-
mensione non • definita come costante, ma • letta da tastiera.

Pseudocodifica
//costanti
dimensione massima = 20
//struttura dati
vettore di interi
//input
dimensione
serie di n interi (n = dimensione)
//output
vettore di interi
INIZIO
leggi (dimensione)
PER ogni elemento del vettore
chiedi dato
carica dato
FINE PER
PER ogni elemento del vettore
scrivi (elemento)
FINE PER
FINE

Codice
arrayVaria.cpp
1 #include <iostream>
2 using namespace std;

178
Unitˆ didattica 10 - Enumerazioni e array

3
4 //INIZIO
5 int main ()
6 {
7 const int MAX = 20; //dimensione massima per il vettore
8 int dim;
9 int i;
10
11 //definisci il vettore
12 int vett[MAX];
13
14 //acquisisci e controlla la dimensione
15 do
16 {
17 cout<<"\nInserisci la dimensione (max = 20) "
18 cin>>dim;
19 }
20 //controlla il rispetto dei limiti
21 while (dim < 1 | dim > MAX);
22
23 //salta una riga
24 cout<<"\n";
25
26 for (i=0; i<dim; i++)
27 {
28 //richiedi un dato e caricalo nel vettore
29 cout<<"Inserisci un dato ";
30 cin>>vett[i];
31 }
32
33 cout<<"\nContenuto del vettore: ";
34 //visita il vettore
35 for (i=0; i<dim; i++)
36 {
37 cout<<" "<<vett[i];
38 }
39 cout<<"\nVettore visualizzato";
40
41 //fine programma
42 cout << "\n\nFine ";
43 system ("pause");
44 return 0;
45 }

Prova di esecuzione

179
Sezione 4 - Strutture dei dati

Analisi del codice


Alla riga 7 viene definita la costante intera MAX, che rappresenta la dimensione massima per il vet-
tore. Tale dimensione • posta pari a 20, cos“ come richiesto dal testo del problema. Alla riga 8 •
definita la variabile dim, in cui viene memorizzata la dimensione effettiva del vettore inserita da ta-
stiera.
Alla riga 12 viene definito il vettore di dimensione MAX, di cui verranno utilizzate soltanto le prime
dim posizioni.
Alle righe 17 e 18 viene rispettivamente richiesto e acquisito il valore di dim; le elaborazioni succes-
sive del vettore fanno riferimento solo a questo valore, come si pu˜ vedere alle righe 26 e 35.
Alla riga 21 viene eseguito un controllo sul valore dim inserito da tastiera: soltanto se rientra tra i li-
miti imposti dal problema (• cio• compreso tra 1 e MAX) lÕelaborazione pu˜ procedere.
Alla riga 24 viene immessa a video una riga vuota, per separare le istruzioni di acquisizione della di-
mensione dalla fase di caricamento e stampa dei dati del vettore. Tale fase • identica a quella giˆ vista
nellÕesempio ArrayCarica.
...........................................................................................................................................

10.7 Matrici
Nei paragrafi precedenti abbiamo visto come lavorare con array a una dimensione, vale a dire con ar-
ray che hanno un unico indice di individuazione degli elementi che li compongono.
é possibile, tuttavia, definire anche array a due o pi• dimensioni.

Solitamente, gli array a una dimensione prendono il nome di vettori, mentre gli array
a due dimensioni sono detti matrici.

Come giˆ visto, un array a una dimensione • definito da un nome e da una dimensione, con una sin-
tassi del tutto simile a quella riportata di seguito.

const int dim = 4;


int vettore[dim];

Se, per esempio, • dato il vettore:

VETTORE
Dati → 37 53 11 28
Indice → 0 1 2 3

avremo che lÕelemento Vettore[2] contiene il valore 11.

Per definire una matrice, invece, occorre specificare due o pi• dimensioni.

Una matrice • un insieme di dati dello stesso tipo organizzati in una griglia: ogni ele-
mento che compone la matrice • individuato dallÕindice di riga e dallÕindice di co-
lonna in cui lÕelemento • posizionato.

In C++ la definizione della struttura di una matrice • analoga alla definizione di un array a una di-
mensione. Per esempio, se si scrive:

int matrice[3][4]

180
Unitˆ didattica 10 - Enumerazioni e array

sÕintende che la struttura di nome matrice • composta da 3 righe e 4 colonne, come mostrato nel-
lÕesempio che segue.

MATRICE

0 7 37 24 3
Righe 1 45 12 18 81
2 11 53 37 28

0 1 2 3
Colonne

Per elaborare un singolo elemento della matrice occorre specificare il numero di riga e il numero di
colonna. Con riferimento alla matrice precedente, si pu˜ dire che 18 • contenuto nella cella indivi-
duata da matrice[1][2].

Esempio MatriceCarica .....................................................................................................


Caricare i dati di una matrice di interi con numero di righe e numero di colonne non supe-
riore a 20. Scrivere il contenuto della matrice.

Descrizione della soluzione


Il limite di 20 • posto per non definire in memoria una struttura troppo ingombrante.
Il programma • organizzato in tre parti. La prima parte serve per acquisire il numero di righe e il nu-
mero di colonne e per preparare la matrice.
Quando viene acquisito il numero di righe, viene controllato che questo non ecceda il limite di 20:
se ci˜ accade, il programma richiede di nuovo lÕinserimento del dato e cos“ succede per il numero del-
le colonne.
La matrice • inizialmente definita come un array con dimensioni massime: solo dopo avere acquisito
il numero di righe e di colonne vengono definite le sue dimensioni effettive.
La seconda parte consiste nel caricamento dei dati nella matrice. Vengono utilizzati due indici: i per
le righe e j per le colonne. Per il caricamento si consideri ogni riga della matrice come un vettore e
si proceda come negli esempi del paragrafo precedente al caricamento del vettore attraverso una ri-
petizione enumerativa con lÕindice j che va da 0 al numero di colonne Ð 1. Tale iterazione viene ni-
dificata in una ripetizione pi• esterna che ripete lÕoperazione di acquisizione per tutte le righe della
matrice, con lÕindice i che varia da 0 al numero di righe Ð 1.
Per la terza e ultima parte, la stampa della matrice, di nuovo si devono organizzare due cicli for uno
interno allÕaltro: il pi• interno serve per ÒvisitareÓ gli elementi di una riga mentre lÕesterno serve per
ripetere tale operazione su tutte le righe. Occorre avere lÕaccortezza di scrivere gli elementi di una
stessa riga senza andare a capo e di andare a capo solo alla fine della riga della matrice, cio• alla fine
del ciclo for pi• interno.

Pseudocodifica
Costante MAX = 20 //limite massimo per le dimensioni della matrice
//struttura dati
Matrice di interi
//input
Righe
Colonne

181
Sezione 4 - Strutture dei dati

Serie di numeri per caricare la matrice


//output
Matrice
//variabili di lavoro
i indice di riga
j indice di colonna

INIZIO
//acquisisci numero delle righe
RIPETI
leggi (righe)
//controlla rispetto dei limiti
MENTRE righe < 1 OR righe > 20

//acquisisci numero delle colonne


RIPETI
leggi (colonne)
//controlla rispetto dei limiti
MENTRE colonne < 1 OR colonne > 20
//carica dati nella matrice
PER i DA 0 A righe Ð 1
PER j DA 0 A colonne Ð 1
chiedi dato
carica dato
FINE PER
FINE PER

//visita la matrice
PER i DA 0 A righe Ð 1
PER j DA 0 A colonne Ð 1
scrivi (matrice [i, j])
FINE PER
vai a capo
FINE PER
FINE

Codice
MatriceCarica.cpp
1 #include <iostream>
2 #include <iomanip>
3 using namespace std;
4
5 //INIZIO
6 int main ()
7 {
8 const int MAX = 20; //dimensione massima per la matrice
9
10 //definisci la matrice
11 int mat[MAX][MAX];
12 int righe;
13 int colonne;

182
Unitˆ didattica 10 - Enumerazioni e array

14 int i;
15 int j;
16
17 //acquisisci numero delle righe
18 do
19 {
20 cout<<"\nInserisci il numero righe (max="<<MAX<<") ";
21 cin>>righe;
22 }
23 //controlla il rispetto dei limiti
24 while (righe < 1 | righe > MAX);
25
26 //acquisisci numero delle colonne
27 do
28 {
29 cout<<"\nInserisci il numero colonne (max="<<MAX<<") ";
30 cin>>colonne;
31 }
32 //controlla il rispetto dei limiti
33 while (colonne < 1 | colonne > 20);
34
35 cout<<"\n";
36
37 //carica dati nella matrice
38 for (i=0; i<righe; i++) //per ogni riga della matrice
39 {
40 for (j=0; j<colonne; j++) //per ogni elemento della riga
41 {
42 //richiedi un dato e caricalo nella matrice
43 cout<<"Inserisci un dato ";
44 cin>>mat [i][j];
45 }
46 }
47
48 cout<<"\nContenuto della matrice: \n";
49 //visita la matrice
50 for (i=0; i<righe; i++) //per ogni riga della matrice
51 {
52 for (j=0; j<colonne; j++) //per ogni elemento della riga
53 {
54 //scrivi gli elementi di una riga
55 //uno di seguito all'altro, con ingombro pari a 6 caratteri
56 cout<<setw(6)<<mat[i][j];
57 }
58 //alla fine della riga va a capo
59 cout<<"\n";
60 }
61
62 cout<<"\nmatrice visualizzata";
63
64 //fine programma
65 cout << "\n\nFine ";
66 system ("pause");
67 return 0;
68 }

183
Sezione 4 - Strutture dei dati

Prova di esecuzione

Analisi del codice


Alla riga 11 viene definita la struttura della matrice, che viene identificata con il nome mat e per la
quale • specificato il numero delle righe e delle colonne.
Alle righe 20 e 21 viene acquisito il numero di righe che compongono la matrice; le istruzioni di ac-
quisizione sono inserite allÕinterno di una ripetizione che serve per controllare se il numero digitato
rientra nei limiti imposti (da 1 a 20).
Alle righe 29 e 30 viene definito un processo analogo per lÕacquisizione del numero di colonne.
Alla riga 38 inizia il caricamento dei dati nella matrice: il primo ciclo for serve per ripetere le opera-
zioni sottostanti per tutte le righe.
Alla riga 40 viene definito il ciclo for pi• interno, che permette di ÒmuoversiÓ allÕinterno di una ri-
ga, e per ogni elemento della riga viene acquisito il dato da inserire come indicato alle righe 43 e 44.
In particolare, alla riga 44, il dato letto da tastiera viene assegnato a una ÒcasellaÓ della matrice di no-
me mat individuata dai due indici i e j.
Alla riga 50 inizia la scrittura dei dati della matrice: lÕimpostazione generale del codice successivo •
analoga a quella precedente relativa al caricamento dei dati: la differenza principale • che, nel ciclo
for pi• interno, lÕistruzione di scrittura indicata alla riga 56 permette di scrivere i dati uno di segui-
to allÕaltro, mentre alla riga 59 • indicata lÕistruzione per andare a capo. Tale istruzione non rientra
nel ciclo for interno, ma fa parte del ciclo for esterno, cos“ che essa venga eseguita solo alla fine di
ogni riga e venga ripetuta per ogni riga.
...........................................................................................................................................

Allo scopo di acquisire dimestichezza con i problemi relativi al trattamento delle matrici, viene pre-
sentato un secondo esempio.

Esempio TotaleRighe..........................................................................................................
Caricare i dati di una matrice di interi con numero di righe e numero di colonne non supe-
riore a 20. Scrivere il contenuto della matrice e indicare per ogni riga il totale dei valori in
essa contenuti.

Descrizione della soluzione


La prima parte della richiesta del problema • identica a quella dellÕesempio precedente; si tratta solo
di intervenire nella parte relativa alla visualizzazione del contenuto della matrice e inserire in questa
parte le istruzioni per comunicare i totali di riga.

184
Unitˆ didattica 10 - Enumerazioni e array

Si definisca una variabile totRiga in cui accumulare i valori di ciascuna riga e, con riferimento al
segmento di codice relativo alla scrittura della matrice, si inseriscano le istruzioni che servono per:
1. inizializzare totRiga;
2. sommare in totRiga i valori di una riga della matrice;
3. scrivere totRiga.

Pseudocodifica
Costante Max = 20 //limite massimo per le dimensioni della matrice
//struttura dati
matrice di interi
//input
Righe
Colonne
Serie di numeri per caricare la matrice
//output
matrice
totaleRiga
//variabili di lavoro
i indice di riga
j indice di colonna
INIZIO
....
//parte uguale allÕesempio precedente
....
//visita la matrice
PER i DA 0 A righe Ð 1
totRiga ← 0
PER j DA 0 A colonne Ð 1
scrivi (matrice [i, j])
totRiga ← totRiga + mat [i, j]
FINE PER
scrivi (totRiga) e vai a capo
FINE PER
FINE

Codice
TotaleRighe.cpp
1 #include <iostream>
2 #include <iomanip>
3 using namespace std;
4
5 //INIZIO
6 int main ()
7 {
8 const int MAX = 20; //dimensione massima per la matrice
9
10 //definisci la matrice
11 int mat[MAX][MAX];
12 int righe;

185
Sezione 4 - Strutture dei dati

13 int colonne;
14 int i;
15 int j;
16 int totRiga;
17
18 //acquisisci numero delle righe
19 do
20 {
21 cout<<"\nInserisci il numero righe (max="<<MAX<<") ";
22 cin>>righe;
23 }
24 //controlla il rispetto dei limiti
25 while (righe < 1 | righe > MAX);
26
27 //acquisisci il numero delle colonne
28 do
29 {
30 cout<<"\nInserisci il numero colonne (max="<<MAX<<") ";
31 cin>>colonne;
32 }
33 //controlla il rispetto dei limiti
34 while (colonne < 1 | colonne > MAX);
35
36 //salta una riga
37 cout<<"\n";
38
39 //carica la matrice
40 for (i=0; i<righe; i++) //per ogni riga della matrice
41 {
42 for (j=0; j<colonne; j++) //per ogni elemento della riga
43 {
44 //richiedi un dato e caricalo nella matrice
45 cout<<"Inserisci un dato ";
46 cin>>mat [i][j];
47 }
48 }
49
50 cout<<"\nContenuto della matrice: \n";
51 cout<<setw(14)<<"Totale"<<endl;
52
53 //visita la matrice
54 for (i=0; i<righe; i++) //per ogni riga della matrice
55 {
56 totRiga = 0;
57 for (j=0; j<colonne; j++) //per ogni elemento della riga
58 {
59 //scrivi gli elementi di una riga
60 //uno di seguito all'altro, con ingombro pari a 4 caratteri
61 cout<<setw(4)<<mat[i][j];
62
63 //accumula i valori in totRiga
64 totRiga = totRiga + mat[i][j];
65 }
66 //alla fine della riga scrivi il totale
67 //e vai a capo

186
Unitˆ didattica 10 - Enumerazioni e array

68 cout<<setw(6)<<totRiga<<endl;
69 }
70
71 cout<<"\nMatrice visualizzata";
72
73 //fine programma
74 cout << "\n\nFine ";
75 system ("pause");
76 return 0;
77 }

Prova di esecuzione

Analisi del codice


Le righe 56 e 68 realizzano rispettivamente lÕinizializzazione e la scrittura a video di totRiga; poi-
chŽ queste due operazioni devono essere fatte una volta sola per ogni riga, esse sono poste allÕinter-
no del ciclo for pi• grande, ma allÕesterno del ciclo for pi• interno.
Alla riga 64 • indicata lÕistruzione per totalizzare i singoli valori di ogni elemento di ogni riga, per-
tanto essa • posta allÕinterno del ciclo for pi• interno.
...........................................................................................................................................

10.8 Passaggio di un vettore come parametro a una funzione


NellÕUnitˆ didattica 9 • stato affrontato lÕargomento del passaggio dei parametri dal main alla fun-
zione; in particolare, • stata spiegata la differenza tra passaggio per valore e per riferimento. I para-
metri presi in considerazione erano, tuttavia, dati di tipo semplice, non dati strutturati come un vet-
tore o una matrice. Nel caso in cui i parametri da passare siano array occorre rispettare alcune parti-
colaritˆ sintattiche, che sono oggeto di questo paragrafo.
Vediamo da subito un esempio.

Esempio funzioniArray........................................................................................................
Acquisire da tastiera un vettore di dimensione non superiore a 20 righe e stamparlo. Per la
realizzazione del programma utilizzare tre funzioni, i cui nomi sono chiediDim, leggi-
Vettore, scriviVettore.

187
Sezione 4 - Strutture dei dati

Lo sviluppo top-down del programma


è illustrato nella figura a destra. E main

Chiedi Leggi Scrivi


dimensione vettore vettore

Codice
funzioniArray.cpp
1 #include <iostream>
2 using namespace std;
3
4 const int MAX = 20; //dimensione massima per il vettore
5 int dim; //dimensione acquisita da tastiera
6 int i;
7
8 //funzione per leggere la dimensione
9 int chiediDim (int &d)
10 {
11 //acquisisci e controlla la dimensione
12 do
13 {
14 //chiedi e leggi la dimensione
15 cout<<"\nInserisci la dimensione ";
16 cin>>d;
17 }
18 //controlla il rispetto dei limiti
19 while (d < 1 | d > MAX);
20 return d;
21 }
22
23 //funzione per leggere il vettore
24 void leggiVettore (int v[], int d)
25 {
26 for (i=0; i<d; i++)
27 {
28 //richiedi un dato e caricalo nel vettore
29 cout<<"Inserisci un dato ";
30 cin>>v[i];
31 }
32 }
33
34 //funzione per scrivere il vettore
35 void scriviVettore (int v[], int d)
36 {
37 cout<<"\nContenuto del vettore: ";
38 //visita il vettore
39 for (i=0; i<d; i++)
40 {
41 cout<<" "<<v[i];
42 }
43 cout<<"\nVettore visualizzato";
44 }
45

188
Unitˆ didattica 10 - Enumerazioni e array

46 //INIZIO
47 int main ()
48 {
49 //definisci il vettore con dimensione MAX
50 int vett[MAX];
51
52 //richiama la funzione per acquisire la dimensione del vettore
53 dim = chiediDim (dim);
54
55 //salta una riga
56 cout<<"\n";
57
58 //richiama la funzione leggiVettore
59 leggiVettore(vett,dim);
60
61 //richiama la funzione scriviVettore
62 scriviVettore(vett,dim);
63
64 //fine programma
65 cout << "\n\nFine ";
66 system ("pause");
67 return 0;
68 }

Prova di esecuzione
Risulta del tutto uguale a quella dellÕesempio ArrayVaria.

Analisi del codice


Dalla riga 9 alla riga 21 • sviluppata la funzione chiediDim. Si noti che nellÕintestazione della funzio-
ne (riga 9) il parametro d richiede il passaggio per riferimento (&d), quindi le variazioni del contenuto
della variabile d hanno effetto sulla variabile globale dim, quando questa verrˆ passata alla funzione al-
la riga 53. Alla riga 59 viene richiamata la funzione leggiVettore, che • sviluppata dalla riga 24 alla
riga 32 del codice. Si noti che alla riga 59 lÕargomento vett non riporta nŽ la dimensione nŽ lÕindica-
zione che si tratta di unÕarray, e che il parametro v[] presente nellÕintestazione della funzione alla riga
24 si limita a ÒricordareÓ, con la presenza delle parentesi quadre, che si tratta di un vettore e non di un
dato elementare; inoltre v[] non • preceduto dal carattere & per richiederne il passaggio per riferi-
mento, in quanto tale modalitˆ di passaggio di parametri • in questo caso automatica.
Analoghe considerazioni valgono per la funzione scriviVettore: in particolare, • da notare che
il passaggio dei parametri per riferimento • indispensabile per la funzione leggiVettore, ma non
per scriviVettore.

Nel caso in cui si voglia lavorare con array a pi• di una dimensione i richiami alla funzione leggiVetto-
re (che in questo caso si pu˜ chiamare leggiMatrice) mantengono la stessa sintassi indicata nellÕe-
sempio, con lÕargomento privo di dimensioni, mentre nellÕintestazione della funzione il parametro che si ri-
ferisce alla struttura multidimensionale DEVE contenere la specifica di tutte le dimensioni tranne la prima.
Per esempio, se si vuole lavorare sulla matrice quadrata matr di ordine MAX, per richiamare la funzione
leggiMatrice si deve scrivere lÕistruzione che segue.
leggiMatrice(matr, . . . );
mentre lÕintestazione della funzione assume la forma indicata di seguito.
void scriviMatrice (int m[][MAX], . . . )

...........................................................................................................................................

189
Sezione 4 - Strutture dei dati

Esercizi
Unità didattica 10

1 Dopo aver caricato in memoria un vettore di interi con dimensione d (con d inserito da tastiera), calco-
lare la somma dei valori contenuti nel vettore.

2 Dopo aver caricato in memoria un vettore di interi con dimensione d (con d inserito da tastiera), azzera-
re il primo elemento del vettore.

3 Dopo aver caricato in memoria un vettore di interi con dimensione d (con d inserito da tastiera), azzera-
re l’ultimo elemento del vettore.

4 Dopo aver caricato in memoria un vettore di interi con dimensione d (con d inserito da tastiera), azzera-
re l’elemento di posto n, con n dato in input.

5 Dopo aver caricato in memoria un vettore di interi con dimensione d (con d inserito da tastiera), calco-
lare la media dei valori contenuti nel vettore.

6 Dopo aver caricato in memoria un vettore di interi con dimensione d (con d inserito da tastiera), calco-
lare la media dei valori contenuti nel vettore. Successivamente scrivere gli elementi del vettore che han-
no valore superiore alla media.

7 Dopo aver caricato in memoria un vettore di interi con dimensione d (con d inserito da tastiera), calco-
lare la media dei valori contenuti nel vettore. Successivamente contare gli elementi del vettore che han-
no valore superiore alla media.

8 Dopo aver caricato in memoria un vettore di interi con dimensione d (con d inserito da tastiera), calco-
lare la media dei valori contenuti nel vettore. Successivamente creare un nuovo vettore che contenga gli
elementi del vettore iniziale che hanno valore superiore alla media.

9 Dopo aver caricato in memoria un vettore di interi con dimensione d (con d inserito da tastiera), scrive-
re gli elementi pari contenuti nel vettore.

q0 Dopo aver caricato in memoria un vettore di interi con dimensione d (con d inserito da tastiera), scrive-
re gli elementi di posto pari contenuti nel vettore.

qa Dopo aver caricato in memoria un vettore di interi con dimensione d (con d inserito da tastiera), creare
un nuovo vettore che contenga gli elementi pari del vettore iniziale.

qs Dopo aver caricato in memoria un vettore di interi con dimensione d (con d inserito da tastiera), inserire
“in testa” al vettore un nuovo elemento. Scrivere il vettore iniziale e il vettore modificato. (Suggerimen-
ti: poiché deve essere inserito un nuovo elemento il vettore deve essere definito con dimensione pari a
d + 1; “in testa” al vettore vuol dire al posto 0 del vettore; per fare spazio al nuovo elemento, i dati pree-
sistenti devono essere spostati di un posto a destra).

qd Dopo aver caricato in memoria un vettore di interi con dimensione d (con d inserito da tastiera), inserire
“in coda” al vettore un nuovo elemento. Scrivere il vettore iniziale e il vettore modificato. (Suggerimenti:
poiché deve essere inserito un nuovo elemento il vettore deve essere definito con dimensione pari a d
+ 1; “In coda” al vettore vuol dire dopo l’ultimo elemento del vettore; per fare spazio al nuovo elemen-
to, basta aumentare la dimensione iniziale).

190
Unità didattica 10 - Enumerazioni e array

Esercizi
Unità didattica 10

qf Dopo aver caricato in memoria un vettore di interi con dimensione d (con d inserito da tastiera), elimi-
nare l’ultimo elemento del vettore. Scrivere il vettore iniziale e il vettore modificato. (Suggerimenti: ba-
sta decrementare la dimensione del vettore).

qg Dopo aver caricato in memoria un vettore di interi con dimensione d (con d inserito da tastiera), elimi-
nare il primo elemento del vettore. Scrivere il vettore iniziale e il vettore modificato. (Suggerimenti: si trat-
ta di spostare tutti gli elementi del vettore di un posto a sinistra e, successivamente, diminuire la di-
mensione del vettore).

qh Dopo aver caricato in memoria una matrice con dimensioni date in input non superiori a 20, scrivere l’e-
lemento di posto [r,c] (con r e c inserite da tastiera).

qj Dopo aver caricato in memoria una matrice con dimensioni date in input non superiori a 20, scrivere gli
elementi della riga k (con k dato in input).

qk Dopo aver caricato in memoria una matrice con dimensioni date in input non superiori a 20, scrivere gli
elementi della colonna k (con k dato in input).

ql Dopo aver caricato in memoria una matrice con dimensioni date in input non superiori a 10, calcolare i
totali di colonna.

w0 Dopo aver caricato in memoria una matrice quadrata di ordine n (con n dato in input non superiore a 10),
scrivere gli elementi che stanno sulla diagonale principale (per matrice quadrata di ordine n si intende
una matrice con numero-righe = numero-colonne = n; gli elementi che stanno sulla diagonale principale
hanno indice di riga e indice di colonna uguali).

191
Unitˆ didattica
11
Stringhe e strutture

CHE COSA IMPARERAI A FARE

$ Definire una stringa


$ Concatenare pi• stringhe
$ Estrarre sottostringhe
$ Confronto lessicografico tra stringhe
$ Trattare le stringhe come array di caratteri
$ Dichiarare una struttura
$ Usare le strutture come un nuovo tipo di dato

CHE COSA DOVRAI STUDIARE

$ Sintassi del metodo length


$ Operazione di somma tra stringhe
$ Sintassi per estrarre una sottostringa
$ Operazioni di confronto tra stringhe
$ Concetto di dato aggregato
$ Concetto di struttura e la sua sintassi
Unitˆ didattica 11
Stringhe e strutture

1 1.1 D e fi n i z i o n e d i s t r i n g a
Insieme ai dati di tipo numerico, le stringhe rappresentano il tipo di dati pi• utilizzato nei programmi.

Una stringa • una sequenza di caratteri, come ÒHelloÓ. In C++ le stringhe sono rac-
chiuse tra virgolette doppie, che non sono considerate parte della stringa.

é possibile dichiarare variabili destinate a contenere stringhe, utilizzando istruzioni come quella che
segue.

string nome = ÒGiovanniÓ;

Il tipo string • un tipo standard di C++; per utilizzarlo • sufficiente includere il riferimento al-
lÕheader file <string>, utilizzando la direttiva riportata di seguito.

#include <string>

LÕoperazione di assegnazione permette di definire la stringa che deve essere contenuta da una varia-
bile di tipo string, come illustra lÕistruzione di esempio che segue.

nome = ÒCarloÓ ;

é anche possibile leggere una stringa da tastiera: il frammento di codice seguente mostra come.

cout Ç ÒInserisci il tuo nome => Ò;


cin È nome;

In questo caso nella variabile di tipo stringa viene memorizzata una sola parola: infatti, tenendo pre-
sente che le parole sono considerate dal C++ divise dai separatori (caratteri di spaziatura, di tabula-
zione, di a capo...), se lÕutente digita ÒMario BianchiÓ in risposta al prompt, nella variabile nome vie-
ne memorizzata la sola parola ÒMarioÓ. Per leggere la stringa successiva si deve utilizzare una se-
conda istruzione di input.
Questo vincolo complica la scrittura di unÕistruzione di input che possa trattare correttamente le ri-
sposte di tipo stringa fornite dallÕutente in risposta a una domanda: in alcuni casi • possibile che lÕu-
tente digiti solo il nome, in altri il nome seguito dal cognome, in altri ancora possono essere presen-
ti le iniziali di un secondo nome.
Questa situazione pu˜ essere risolta mediante il comando getline.

LÕistruzione
getline(cin, nome);
legge tutti i caratteri digitati fino a che si preme INVIO e genera una stringa che con-
tiene tutti i caratteri e che viene memorizzata nella variabile nome.

193
Sezione 4 - Strutture dei dati

Esempio LeggiNome ..........................................................................................................


Chiedere il nome allÕutente e memorizzarlo in una stringa.

Codice
LeggiNome.cpp
1 #include <iostream>
2 #include <string>
3 using namespace std;
4
5 //INIZIO
6 int main ()
7 {
8 //definisci una variabile stringa
9 string nome;
10
11 //chiedi e leggi il nome
12 cout << Ò\nInserisci il tuo nome => Ò;
13 cin >> nome;
14
15 //scrivi il contenuto della variabile nome
16 cout << Ò\nla variabile nome contiene Ò << nome;
17
18 //fine programma
19 cout << Ò\n\nFine Ò;
20 system (ÒpauseÓ);
21 return 0;
22 }

Prova di esecuzione

Analisi del codice


Alla riga 2 viene incluso lÕheader file <string>, che permette di richiamare i metodi in grado di
operare sulle stringhe.
Alla riga 13 viene letto da tastiera quanto ha digitato lÕutente.
Come si vede dalla prova di esecuzione, lÕutente scrive sia il nome sia il cognome ma, poichŽ i due
dati sono separati da uno spazio, il contenuto della variabile nome • soltanto ÒMarioÓ: infatti, quan-
do alla riga 16 del programma viene scritto a video il contenuto della variabile nome, compare sol-
tanto la stringa ÒMarioÓ.
...........................................................................................................................................

Esempio NomeCompleto ...................................................................................................


Chiedere il nome allÕutente e memorizzare in una stringa tutti i caratteri digitati.

194
Unitˆ didattica 11 - Stringhe e strutture

Codice
NomeCompleto.cpp
1 #include <iostream>
2 #include <string>
3 using namespace std;
4
5 //INIZIO
6 int main ()
7 {
8 //definisci una variabile stringa
9 string nome;
10
11 //chiedi e leggi tutta la risposta dellÕutente
12 cout << Ò\nInserisci il tuo nome => Ò;
13 getline(cin,nome);
14
15 //scrivi il contenuto della variabile nome
16 cout << Ò\nla variabile nome contiene Ò << nome;
17
18 //fine programma
19 cout << Ò\n\nFine Ò;
20 system (ÒpauseÓ);
21 return 0;
22 }

Prova di esecuzione

Analisi del codice


Come si vede dalla prova di esecuzione, il programma questa volta visualizza tutto ci˜ che • stato
digitato dallÕutente. Questo risultato dipende dalla riga 13, dove lÕistruzione di lettura permette
la memorizzazione nella stringa di tutto il contenuto della riga inserita dopo il prompt ÒInserisci
il tuo nome =>Ó.
...........................................................................................................................................

1 1.2 L u n g h e z z a d i u n a s t ri n g a
NellÕesempio appena visto la variabile nome contiene la stringa ÒMario BianchiÓ, che • composta da
12 caratteri compreso lo spazio.

Il numero di caratteri di una stringa definisce la lunghezza della stringa.

Per esempio, la lunghezza della stringa ÒMario BianchiÓ • 13, mentre la lunghezza di ÒHello,
World!\nÓ • 14, dato che il carattere di escape Ò\nÓ • considerato unico.

195
Sezione 4 - Strutture dei dati

Si pu˜ calcolare la lunghezza di una stringa mediante la funzione length.

A differenza della funzione getline, la funzione length • richiamata con la cosiddetta Ònotazio-
ne puntoÓ: si deve per prima cosa scrivere il nome della stringa di cui si vuole calcolare la lunghezza,
poi si inserisce un punto e infine il nome della funzione, seguiti da una coppia di parentesi. Un esem-
pio • riportato di seguito.

int n = nome.length();

Anticipando la terminologia che sarˆ trattata pi• avanti, nellÕUnitˆ didattica 12, relativa alla pro-
grammazione a oggetti, si precisa che length() • un metodo della classe string che si applica a
un oggetto come nome.

Esempio Lunghezza ............................................................................................................


Scrivere la lunghezza della risposta dellÕutente a un prompt a video.

Codice
Lunghezza.cpp
1 #include <iostream>
2 #include <string>
3 using namespace std;
4
5 //INIZIO
6 int main ()
7 {
8 //definisci una variabile stringa
9 string nome;
10
11 //chiedi e leggi tutta la risposta dellÕutente
12 cout << Ò\nInserisci il tuo nome => Ò;
13 getline(cin,nome);
14
15 //scrivi il contenuto della variabile nome
16 cout << Ò\nLunghezza della risposta = Ò << nome.length();
17
18 //fine programma
19 cout << Ò\n\nFine Ò;
20 system (ÒpauseÓ);
21 return 0;
22 }

Prova di esecuzione

196
Unitˆ didattica 11 - Stringhe e strutture

Analisi del codice


LÕunica differenza con lÕesempio precedente • data dalla riga 16 dove, al posto della visualizzazione
di quanto inserito dallÕutente, viene scritta la lunghezza della stringa nome.
...........................................................................................................................................

1 1.3 C o n c a t e n a zi o n e e d e s t r a zi o n e
é possibile unire due stringhe utilizzando lÕoperatore Ò+Ó che, applicato alle stringhe, si limita ad ac-
codare una stringa a unÕaltra stringa.

LÕoperazione che permette di unire pi• stringhe per formarne una unica prende il no-
me di concatenazione.

Esempio Concatena ..........................................................................................................


Concatenare la stringa Òin laboratorioÓ alla stringa ÒBenvenutiÓ, in modo da ottenere la fra-
se ÒBenvenuti in laboratorioÓ.

Codice
Concatena.cpp
1 #include <iostream>
2 #include <string>
3 using namespace std;
4
5 //INIZIO
6 int main ()
7 {
8 //dichiara due stringhe
9 string stringa1 = ÒBenvenutiÓ;
10 string stringa2 = Òin laboratorioÓ;
11
12 //concatena le stringhe
13 stringa1 = stringa1 + stringa2;
14
15 //scrivi il risultato
16 cout << Ò\nStringa risultante = Ò << stringa1;
17
18 //fine programma
19 cout << Ò\n\nFine Ò;
20 system (ÒpauseÓ);
21 return 0;
22 }

Prova di esecuzione

197
Sezione 4 - Strutture dei dati

Analisi del codice


Il codice mostra come concatenare due stringhe: infatti alla riga 13 , grazie all’operatore “+”, la strin-
ga “in laboratorio” viene accodata alla stringa “Benvenuti”.
Nonostante la compilazione del programma Concatena.cpp non rilevi errori, il risultato non è
quello atteso: infatti, manca un carattere di separazione tra la prima stringa e la seconda.
Per correggere questa “imperfezione”, la riga 13 deve essere modificata come segue:

13 stringa1 = stringa1 + “ “ + stringa2;

...........................................................................................................................................

Così come è possibile unire stringhe corte per formarne una lunga, si possono estrarre stringhe se-
condarie da una stringa iniziale più lunga.

Per estrarre una stringa secondaria (detta anche sottostringa) da una stringa princi-
pale si utilizza la funzione substr.

La sintassi della funzione substr è


s.substr(inizio,lung)
dove:

: s è la stringa a cui viene applicata la funzione substr;


: inizio è il numero del carattere della stringa da cui inizia l’estrazione della sottostringa (si
tenga conto che la posizione del primo carattere ha indice 0);
: lung è la lunghezza della sottostringa da estrarre.

Esempio Estrai ....................................................................................................................


Estrarre la prima parola del messaggio di saluto “Hello, world!”.

Codice
Estrai.cpp
1 #include <iostream>
2 #include <string>
3 using namespace std;
4
5 //INIZIO
6 int main ()
7 {
8 //stringa di saluto
9 string stringa = “Hello, world!”;
10
11 //estrai la prima parola
12 string sottoStringa = stringa.substr(0,5);
13
14 //scrivi il risultato dell’estrazione
15 cout << “\nStringa estratta = “ << sottoStringa;
16
17 //fine programma
18 cout << “\n\nFine “;

198
Unitˆ didattica 11 - Stringhe e strutture

19 system (ÒpauseÓ);
20 return 0;
21 }

Prova di esecuzione

Analisi del codice


La parola da estrarre • ÒHelloÓ, che inizia alla posizione 0 della stringa ed • lunga 5 caratteri, quindi
lÕistruzionee alla riga 12 • substr(0,5).
...........................................................................................................................................

Esempio Iniziali ..................................................................................................................


Dopo aver letto da console il nome e il cognome specificato dallÕutente scriverne a video le
iniziali, separate da un punto.

Codice
Iniziali.cpp
1 #include <iostream>
2 #include <string>
3 using namespace std;
4
5 //INIZIO
6 int main ()
7 {
8 //dichiara le due stringhe nome e cognome
9 string nome;
10 string cognome;
11
12 //chiedi e leggi il nome e il cognome
13 cout << ÒInserisci nome e cognome => Ò;
14 cin >> nome >> cognome;
15
16 //estrai le lettere iniziali e concatenale
17 string iniziali = nome.substr(0, 1) + Ò.Ó + cognome.substr(0, 1) + Ò.Ó;
18
19 //scrivi il risultato
20 cout << Ò\nLe iniziali sono Ò << iniziali;
21
22 //fine programma
23 cout << Ò\n\nFine Ò;
24 system (ÒpauseÓ);
25 return 0;
26 }

199
Sezione 4 - Strutture dei dati

Prova di esecuzione

Analisi del codice


L’istruzione da esaminare è alla riga 17: nella variabile di nome iniziali vengono concatenate le
seguenti stringhe:

: il primo carattere della variabile nome, ottenuto come nome.substr(0, 1);


: il carattere “.”;
: il primo carattere della variabile cognome, ottenuto come cognome.substr(0, 1);
: un altro carattere “.”.
...........................................................................................................................................

1 1.4 C o n f r o n t i t r a s t ri n g h e
Ogni istruzione if verifica una condizione, che in molti casi consiste nel confronto tra due valo-
ri: per esempio, si può verificare la condizione che “area < 10 e area >= 0”. I simboli “<” e “>=”
sono definiti operatori relazionali. Il C++ dispone di sei operatori relazionali, riepilogati nella ta-
bella che segue.

O P E R A T O R E D E S C RIZIO N E
== Uguale a
< Minore di
> Maggiore di
<= Minore o uguale a
>= Maggiore o uguale a
!= Diverso

Come si può osservare, solo due degli operatori relazionali (> e <) sono simili alle notazioni
matematiche.
L’operatore == può inizialmente confondere la maggior parte dei neofiti di C++, tuttavia in C++
l’operatore = ha già un significato specifico, dato che è l’operatore di assegnazione. L’operatore
== indica invece la relazione di uguaglianza, come risulta evidente nelle due righe di codice che
seguono.

a = 5; //operazione di assegnazione
if (a== 5) //verifica se a è uguale a 5

È necessario ricordarsi di utilizzare l’operatore == all’interno delle operazioni di confronto.

È anche possibile confrontare le stringhe. Per esempio, si può scrivere:

if (name == “Harry”)

200
Unitˆ didattica 11 - Stringhe e strutture

In C++ la differenza tra lettere maiuscole e minuscole • importante: per esempio, ÒHarryÓ e
ÒHARRYÓ non sono la stessa stringa.
Nel confronto tra stringhe, gli operatori <, <=, > e >= seguono le regole dellÕordinamento alfabeti-
co. Se si scrive

string name = ÒTomÓ;


if (name < ÒDickÓ)

la condizione indicata dopo if risulta falsa perchŽ, nellÕordinamento alfabetico, ÒDickÓ viene prima
(cio• • ÒminoreÓ) di Tom. In realtˆ, lÕordine alfabetico utilizzato dal C++ • diverso da quello di un
normale dizionario: il C++, infatti, • sensibile alle maiuscole e ordina i caratteri iniziando dai nume-
ri, seguiti dai caratteri maiuscoli e da quelli minuscoli: per esempio, la cifra Ò1Ó viene prima del ca-
rattere ÒBÓ, che viene prima di ÒaÓ. Il carattere spazio viene prima di tutti gli altri caratteri.
Volendo essere rigorosi si dovrebbe precisare che lÕordinamento dei caratteri dipende dal sistema
operativo utilizzato: la maggioranza dei sistemi operativi per PC utilizza il cosiddetto codice ASCII
(American Standard Code for Information Interchange) oppure una delle sue estensioni, i cui carat-
teri sono ordinati come • stato descritto.
Quando si confrontano due stringhe, le lettere corrispondenti vengono confrontate finchŽ termina
una delle stringhe o si riscontra la prima differenza: se termina una delle stringhe, quella pi• lunga
viene disposta dopo lÕaltra (cio•, risulta essere maggiore dellÕaltra), mentre se viene rilevata unÕerra-
ta corrispondenza tra i caratteri si rende necessario confrontarli per determinare quale stringa • la
successiva nella sequenza lessicale. QuestÕultimo processo viene definito confronto lessicografico.
Se, per esempio, si confrontano le stringhe ÒautoÓ e ÒautomaÓ, le prime quattro lettere corrispon-
dono e si raggiunge il termine della prima stringa. Pertanto la stringa ÒautoÓ precede la stringa Òau-
tomaÓ, secondo la convenzione di ordinamento spiegata sopra.
Confrontiamo ora le stringhe Ò autoritˆÓ e ÒautomaÓ. Anche in questo caso le prime quattro lettere
corrispondono ma, dal momento che la ÒrÓ viene dopo la ÒmÓ, la stringa ÒautoritˆÓ segue la parola
ÒautomaÓ.

é possibile confrontare solo i numeri con i numeri e le stringhe con le stringhe. Il test indicato di se-
guito, per esempio,

string name = ÒHarryÓ;


if (name > 5) //Errore//

non • valido.

1 1.5 C a r a t t e ri e s t ri n g h e C
Il C++ prevede che i singoli caratteri siano di tipo char. Nel linguaggio C, il precursore di C++, lÕu-
nico modo per implementare le stringhe consiste nel definire array di caratteri.
Nel codice C++ • possibile distinguere le stringhe di tipo C, poichŽ sono definite come di tipo char*
oppure come array (char[]).
Si ricordi, inoltre, che in C i singoli caratteri sono racchiusi tra virgolette semplici: per esempio, la strin-
gaÔaÕ identifica il carattere a mentre ÒaÓ corrisponde a una stringa che contiene il singolo carattere ÔaÕ.
LÕutilizzo di array di caratteri per definire le stringhe implica una complicazione significativa per i
programmatori, che devono implementare manualmente lo spazio di memoria da riservare per
queste sequenze: in C • comune lÕerrore di memorizzare una stringa in una variabile troppo pic-
cola per contenerne tutti i caratteri. Non essendo possibile verificare questa eventualitˆ, • preve-
dibile che il programmatore poco esperto possa sovrascrivere le aree di memoria destinate ad altre
variabili.

201
Sezione 4 - Strutture dei dati

Le stringhe C++ standard gestiscono invece questa complicazione in modo completamente automa-
tico. Nella maggior parte delle attivitˆ di programmazione, infatti, non • necessario ricorrere al tipo
di dati char e si possono utilizzare stringhe di lunghezza unitaria per definire singoli caratteri.

1 1.6 D i c h i a r a zi o n e d i u n a s t r u t t u r a
Le strutture sono particolari tipi di dati che vengono definiti aggregati, cio• capaci di contenere tipi
di dati diversi.
Dichiarare un oggetto di tipo struttura significa avere a che fare con un nuovo tipo di dato.

La sintassi di una struttura fa uso della parola chiave struct:


struct Nome_struttura
{
//membri della struttura
}

I membri della struttura altro non sono che variabili. Di solito le strutture vengono utilizzate per ge-
stire quantitˆ di dati non molto grandi e che occupano poca memoria.
Per dichiarare una struttura si usa la stessa sintassi della dichiarazione di una variabile, dove al posto
del tipo si inserisce il nome della struttura.
Nome_struttura MiaStruttura;
Viene definita, ora, una semplice struttura e un breve programma di esempio.

Esempio Struttura ...............................................................................................................


Creare una struttura che consenta di memorizzare il nome e il cognome di uno studente.

Questa potrebbe essere la soluzione:


struct Studente
{
string nome;
string cognome;
}

La struttura si chiama Studente e ha due membri di tipo string, che sono rispettivamente il no-
me e il cognome dello studente.
La variabile Alunno di tipo Studente si dichiara come segue.

Studente alunno;

Una volta dichiarata una variabile di tipo struct, si pu˜ accedere ai suoi membri
utilizzando la cosiddetta notazione puntata, che prevede che il nome della variabile,
per esempio alunno, sia seguito dal punto e dal nome della variabile membro a cui
si deve fare riferimento.

Nel caso dellÕassegnazione di un valore al membro nome della variabile alunno, lÕistruzione da uti-
lizzare • la seguente.
alunno.nome=ÓPippoÓ;

202
Unitˆ didattica 11 - Stringhe e strutture

Per assegnare, invece, il valore di una variabile membro a una variabile, si procede come di seguito.
string nomealunno = alunno.nome;

Una struttura deve essere dichiarata prima del main e dopo le direttive using del programma.
...........................................................................................................................................

Esempio Struttura1 ........................................................................................................


Definire la struttura Studente che contiene un nome e un cognome; assegnare alle va-
riabili membro i valori ÒPaoloÓe ÒRossiÓ e visualizzare il contenuto della struttura.

Codice
Struttura1.cpp
1 #include <iostream>
2 #include <string>
3 using namespace std;
4
5 struct Studente
6 {
7 //membri della struttura
8 string nome;
9 string cognome;
10 };
11
12 //INIZIO
13 int main ()
14 {
15 //dichiara la variabile di tipo struct
16 Studente alunno;
17
18 //assegna i valori ai membri
19 //della struttura
20 alunno.nome="Paolo";
21 alunno.cognome="Rossi";
22
23 cout<<"\nNome dello studente: ";
24
25 //visualizza i membri della struttura
26 cout<<" "<<alunno.nome<<" "<<alunno.cognome;
27
28 //fine programma
29 cout << "\n\nFine ";
30 system ("pause");
31 return 0;
32 }

Prova di esecuzione

203
Sezione 4 - Strutture dei dati

Analisi del codice


Dalla riga 5 alla riga 10 cÕ• lÕimplementazione della struttura Studente. é stata dichiarata prima
del main.
La struttura contiene, rispettivamente alle righe 8 e 9, le dichiarazioni di due membri di tipo
string, atti a contenere il nome e il cognome dello studente.
Alla riga 16 viene dichiarata la variabile alunno di tipo Studente che viene utilizzata alle righe 20
e 21 per fare riferimento, con la notazione puntata, ai suoi membri assegnando loro valori dello stes-
so tipo, ossia string.
Infine, alla riga 26, vengono visualizzati i valori cos“ assegnati ai membri della struttura alunno, tra-
mite la ormai consueta notazione.
...........................................................................................................................................

1 1.7 M e t o d i c o s t r u t t o ri
Una struttura pu˜ contenere tutti i tipi di dati e una particolare funzione.

Esiste una funzione che pu˜ essere definita allÕinterno di una struttura e che ha la
particolare caratteristica di impostare lo stato iniziale della struttura. Tale tipo di
funzione viene definita costruttore, e il suo compito • inizializzare i membri della
struttura.

é da premettere che i membri di una struttura non possono essere inizializzati al momento della defi-
nizione. Per esempio, la seguente struttura:

public struct Studente


{
//membri della struttura
public string nome=ÓPippoÓ;
public string cognome=ÓInzaghiÓ;
}
....

genera un errore in fase di compilazione quindi, per avere la sicurezza che i membri siano ini-
zializzati, bisogna richiamare il costruttore al momento della creazione di una variabile di tipo
struttura.
Il costruttore deve avere obbligatoriamente lo stesso nome della struttura e avere tanti parametri (pas-
sati per valore) quanti sono i membri della struttura.

Esempio di costruttore .......................................................................................................


....
struct Studente
{
//membri della struttura
string nome;
string cognome;

//costruttore
public Studente(string nome, string cognome)
{
//assegnazione dei parametri

204
Unitˆ didattica 11 - Stringhe e strutture

//ai membri della struttura


nome = nome;
cognome = cognome;
}
}

...........................................................................................................................................

Questo per quanto riguarda la definizione del costruttore allÕinterno della struttura; ora lo si deve
utilizzare al momento della creazione della struttura allÕinterno del codice.
Per la creazione dellÕoggetto alunno si deve utilizzare lÕistruzione riportata di seguito.

...
Studente Alunno(ÒPippoÓ,ÓInzaghiÓ);
...

In questo modo si crea un nuovo oggetto di nome alunno e si richiama la sua funzione costrutto-
re, che ne inizializza i membri.

Esempio Struttura2 .............................................................................................................


Modificare il codice di Struttura1.cpp per inserire il costruttore del tipo Studente.

Codice
Struttura2.cpp
1 #include <iostream>
2 #include <string>
3 using namespace std;
4
5 struct Studente
6 {
7 //membri della struttura
8 string nome;
9 string cognome;
10
11 //costruttore
12 Studente(string n, string c)
13 {
14 nome=n;
15 cognome=c;
16 }
17 };
18
19 //INIZIO
20 int main ()
21 {
22 //creazione della variabile struct alunno
23 Studente alunno("Paolo","Rossi");
24
25 cout<<"\nNome dello studente: "<<alunno.nome<<" "<<alunno.cognome;
26
27 //fine programma
28 cout << "\n\nFine ";

205
Sezione 4 - Strutture dei dati

29 system ("pause");
30 return 0;
31 }

Il risultato • uguale a quello dellÕesempio Struttura1.

Analisi del codice


Alla riga 12 viene dichiarato il costruttore della struttura Studente; la sua intestazione possiede due
parametri passati per valore, dello stesso tipo dei membri, cio• string. Il codice al suo interno as-
segna i parametri ai rispettivi membri.
Nel main, alla riga 23, cÕ• lÕistruzione per la creazione dellÕoggetto alunno di tipo Studente, che
richiama automaticamente il costruttore della struttura con i relativi parametri.
...........................................................................................................................................

Lo studio delle strutture introduce alcuni concetti importantissimi che sono alla base delle classi. In-
fatti, le strutture sono molto simili alle classi tranne per il fatto che non possono ereditare membri da
altre strutture. LÕereditarietˆ • un sistema che consente di espandere notevolmente le Òfunzionalitˆ
di una classeÓ ed • uno dei principi su cui si basa lÕintero linguaggio C++.
I concetti e gli argomenti che riguardano le classi verranno affrontati in maniera dettagliata nella
prossima Sezione.

206
Unità didattica 11 - Stringhe e strutture

Esercizi
Unità didattica 11

1 Scrivere un programma che acquisisca il nome dell’utente da tastiera e ne visualizzi ogni lettera su una
riga differente.

2 Scrivere un programma che acquisisca il nome dell’utente da tastiera e visualizzi le lettere in ordine in-
verso.

3 Aggiungere un metodo al programma precedente che visualizzi il nome dell’utente al contrario.

4 Scrivere un programma che contenga una funzione che abbia come parametro una stringa inserita dal-
l’utente e ne sostituisca tutte le occorrenze della lettera “i” con un “°”.

5 Come si può ricavare il primo carattere di una stringa? E l’ultimo? Come si elimina il primo carattere?
E l’ultimo?

6 Considerare un programma C++ che contiene le due istruzioni di richiesta di input:


string nome;
string cognome;
int age;
cout << “Inserisci nome e cognome: “;
cin >> nome >> cognome;
cout << “ età : “;
cin >> age;

Qual è il contenuto delle variabili nome, cognome ed età se l’utente digita gli input indicati di seguito?
James Carter
56
Lyndon Johnson
49
Hodding Carter 3rd
44
Richard M. Nixon
62

7 Quali sono i valori delle espressioni seguenti? In ciascuna riga si consideri che:
string s = “Hello” ;
string t = “World” ;

A s.substr(l, 2)
B s.length() + t.length()

8 Disegnare lettere sullo schermo.


Si può riprodurre una grande lettera, per esempio “H”, nel modo indicato di seguito.

* *
* *
*****
* *
* *

207
Sezione 4 - Strutture dei dati

Esercizi
Unità didattica 11
Questa lettera può essere definita come una costante stringa impostata come segue:
const string LETTER_H =
“* *\n* *\n*****\n* *\n* *\n”;

Si può effettuare la stessa operazione con le lettere E, L e O. Scrivere poi il messaggio disegnando le
lettere appena definite come stringhe.

H
E
L
L
O

9 Scrivere un programma che trasformi i nomi dei mesi nei numeri corrispondenti.

q0 Trasformare la stringa “Rota Cesare” in “Rota dr. Cesare”.

qa Controllare che la terza cifra di un CAP sia soltanto o “0” o “1”.

qs Tra le coppie di stringhe seguenti, quale viene prima secondo l’ordine lessicografico?
A “Tom”, “Dick”
B “Tom”, “Tornato”
C “church”, “Churchill”
D “operaio”, “operatore”
E “capo”, “capello”
F “C++”, “Car”
G “Tom”, “Tom”
H “cantuccini”, “canto”
I “tarallo”, “taralluccio”

qd Scrivere un programma che stampa la domanda “Vuoi continuare?” e legge un input dall’utente. Se l’in-
put dell’utente è “s”, “S”, “Sì”, “sì”, “Si”, “si”, “OK”, “Ok”, “ok”, “Sicuramente” o “Perché no? “, vi-
sualizzare “OK.” Se l’input dell’utente è “N” o “No”, visualizzare “Fine lavoro”. Altrimenti visualizzare
“Errore”.

qf Descrivere una struttura che possa contenere i dati relativi a una partita di calcio.

qg Descrivere una struttura che possa contenere i dati relativi ai voti delle verifiche sia orali che scritte di
un studente.

qh Descrivere una struttura che contenga i dati anagrafici di una persona.

qj Aggiungere alla struttura dell’esercizio precedente i dati relativi a uno studente.

208
Sezione 5
Classi e oggetti


Obiettivi ◊ Risolvere un problema individuando gli oggetti
e le loro interazioni
◊ Definire una classe attraverso i suoi dati
e i suoi metodi
◊ Realizzare classi flessibili attraverso il polimorfismo

&
◊ Strutturare gerarchie di classi sfruttando lÕerditarietˆ

Questa sezione contiene


U.D.12 Concetti generali
U.D.13 Polimorfismo ed ereditarietˆ
Unitˆ didattica
12
Concetti generali

CHE COSA IMPARERAI A FARE

$ Associare dati e codice in unÕunica struttura


$ Definire pi• metodi individuati da una sola intestazione
$ Estendere dati e metodi da una classe di tipo generale
ad altre classi derivate
$ Utilizzare un metodo standard per descrivere una classe

CHE COSA DOVRAI STUDIARE

$ Concetto generale di incapsulazione


$ Metodologia e sintassi per lÕoverloading
$ Concetto generale di polimorfismo
$ Definizione di derivazione e di ereditarietˆ
$ Terminologia e diagrammi utilizzati nella OOP
$ Sintassi di base per la dichiarazione degli oggetti
Unitˆ didattica 12
Concetti generali

12.1 Introduzione alla OOP


Con la sigla OOP si indica la metodologia per lÕorganizzazione dei programmi denominata pro-
grammazione orientata agli oggetti (Object Oriented Programming).
In generale, un programma pu˜ essere organizzato in due modi: ponendo al centro il codice (Òci˜
che accadeÓ) o ponendo al centro i dati (Ògli attori interessatiÓ). Utilizzando le tecniche della pro-
grammazione strutturata, i programmi vengono tipicamente organizzati intorno al codice. Questo
approccio prevede che il codice operi sui dati. Per esempio, un programma scritto con un linguaggio
di programmazione strutturato come il Pascal • definito dalle sue funzioni, le quali operano sui dati
usati dal programma. I programmi a oggetti seguono un approccio opposto: infatti sono organizza-
ti intorno ai dati e si basano sul fatto che sono questi a controllare lÕaccesso al codice. In un linguag-
gio a oggetti si definiscono i dati e le routine che sono autorizzate ad agire su tali dati. Pertanto, so-
no i dati a stabilire quali sono le operazioni che possono essere eseguite.

I linguaggi che consentono di attuare i principi della programmazione a oggetti han-


no tre fattori in comune: lÕincapsulazione, il polimorfismo e lÕereditarietˆ.

12.2 Incapsulazione
In un linguaggio a oggetti, il codice e i dati possono essere raggruppati in modo da creare una sorta
di Òscatola neraÓ. Quando il codice e i dati vengono raggruppati in questo modo, si crea un ogget-
to. In altre parole, un oggetto • un ÒdispositivoÓ che supporta lÕincapsulazione.

LÕincapsulazione permette di associare ai dati del programma il codice autorizzato


ad agire su tali dati.

AllÕinterno di un oggetto, il codice, i dati o entrambi possono essere definiti privati per tale oggetto
oppure pubblici. Il codice o i dati privati sono noti e accessibili solo da parte degli elementi dellÕog-
getto stesso. Questo significa che il codice e i dati privati non risultano accessibili da parte di elementi
del programma che si trovano allÕesterno dellÕoggetto. Se il codice o i dati sono pubblici, risulteran-
no accessibili anche da altre parti del programma che non sono definite allÕinterno dellÕoggetto. Ge-
neralmente, le parti pubbliche di un oggetto sono utilizzate per fornire unÕinterfaccia controllata agli
elementi privati dellÕoggetto stesso. Un oggetto •, in tutto e per tutto, una variabile di un tipo de-
finito dallÕutente. Pu˜ sembrare strano pensare a un oggetto, che contiene codice e dati, come a
una variabile; tuttavia, nella programmazione a oggetti avviene proprio questo. Ogni volta che si
definisce un nuovo tipo di oggetto, si crea implicitamente un nuovo tipo di dato. Ogni specifica
istanza di questo tipo • una variabile composta.

Esempio Rettangolo1 .........................................................................................................


Definire una classe per gestire con oggetti che abbiano le caratteristiche di un ret-
tangolo.

211
Sezione 5 - Classi e oggetti

Si pu˜ assegnare alla classe indicata dal problema il nome di Rettangolo. I dati essenziali di un ret-
tangolo sono le misure della base e dellÕaltezza: base e altezza sono quindi i dati che necessaria-
mente fanno parte della classe Rettangolo. Inoltre, • interessante conoscere di un rettangolo lÕa-
rea e la misura del perimetro, quindi, alla classe Rettangolo associamo anche due metodi: uno per
calcolare lÕarea e lÕaltro per calcolare il perimetro. Pertanto fanno parte della classe Rettangolo
quattro membri:
base 
altezza  dati

area 
perimetro  metodi o funzioni

...........................................................................................................................................

12.3 Polimorfismo
I linguaggi di programmazione a oggetti supportano il polimorfismo, che • caratterizzato dalla fra-
se ÒunÕinterfaccia, pi• metodiÓ. In altri termini, vale lÕaffermazione riportata di seguito.

Il polimorfismo consente a unÕinterfaccia di controllare lÕaccesso a una categoria ge-


nerale di azioni.

La specifica azione selezionata • determinata dalla natura della situazione.

Esempio Termostato...........................................................................................................
Un esempio di polimorfismo tratto dal mondo reale • il termostato.

Una caratteristica tipica di tutti i termostati • che non serve conoscere il tipo di combustibile uti-
lizzato (gas, petrolio, elettricitˆ e cos“ via): il termostato funziona sempre nello stesso modo. In
questo caso, il termostato (che • lÕinterfaccia) • sempre lo stesso qualsiasi sia il tipo di combusti-
bile (metodo) utilizzato. Per esempio, se si desidera raggiungere una temperatura di 20 gradi, si
imposta il termostato a 20 gradi. Non • necessario sapere quale sia il meccanismo che fornisce il
calore.
...........................................................................................................................................

Questo stesso principio si pu˜ applicare anche in programmazione: per esempio, un programma po-
trebbe definire tre diversi tipi di array: un array per i valori interi, uno per i caratteri e uno per valori
in virgola mobile. Grazie al polimorfismo, • possibile creare solo due metodi (carica() e scri-
vi()), utilizzabili per i tre tipi di array.
Nel programma vengono create tre diverse versioni di questi metodi, una per ogni tipo di array, ma
il nome delle funzioni rimane lo stesso. Il compilatore seleziona automaticamente la funzione cor-
retta sulla base del tipo dei dati memorizzati, pertanto lÕinterfaccia dellÕarray (ovvero le funzioni ca-
rica() e scrivi()) rimane invariata, qualunque sia il tipo di array utilizzato. Naturalmente le sin-
gole versioni di queste funzioni definiscono implementazioni (metodi) specifiche per ciascun tipo di
dati. Il polimorfismo aiuta a ridurre la complessitˆ del programma consentendo di utilizzare la stes-
sa interfaccia per accedere a una categoria generale di azioni. é compito del compilatore selezionare
lÕazione specifica (ovvero il metodo) da applicare in una determinata situazione. Il programmatore
non deve pi• fare questa selezione in modo specifico, ma deve semplicemente ricordare e utilizzare
lÕinterfaccia generale.

212
Unità didattica 12 - Concetti generali

12.4 Ereditarietà
L’ereditarietà è il processo grazie al quale un oggetto acquisisce le proprietà di un altro oggetto. Questo
è un concetto fondamentale poiché chiama in causa il concetto di classificazione. Se si prova a riflettere,
la maggior parte della conoscenza è resa più gestibile da classificazioni gerarchiche. Per esempio, una me-
la rossa Delicious appartiene alla classificazione “mela”, che a sua volta appartiene alla classe “frutta”, che
a sua volta si trova nella classe più estesa “cibo”. Senza l’uso della classificazione, ogni oggetto dovrebbe
essere definito esplicitamente con tutte le proprie caratteristiche. L’uso della classificazione consente di
definire un oggetto sulla base delle qualità che lo rendono unico all’interno della propria classe.

L’ereditarietà è lo strumento che permette di costruire nuove classi utilizzando quelle


già realizzate.

È il meccanismo di ereditarietà a rendere possibile per un oggetto essere una specifica istanza di un caso
più generale. Come si vedrà, l’ereditarietà è un importante aspetto della programmazione a oggetti.

Esempio Auto e autocarro .................................................................................................


Definire la classe automobile e derivare la classe autocarro.
Un possibile diagramma per la classe automobile è riportato a lato: E Automobile
marca
velocità
colore
numero porte
livello carburante
cilindrata
Nella prima casella dello schema “Automobile”, è indicato il nome della clas- parti
se, nella seconda casella sono indicati i dati e nella terza i metodi. La classe Au- accelera
tocarro può essere definita estendendo le proprietà e i comportamenti pre- fermati
senti nella classe Auto. H fai il pieno

Automobile Autocarro
marca marca
velocità velocità
colore simbolo colore
numero porte per l’ereditarietà numero porte
livello carburante livello carburante
cilindrata cilindrata
parti tara
accelera portata massima
fermati parti
fai il pieno accelera
fermati
fai il pieno
carica
scarica

I membri ereditati sono riportati in corsivo.


...........................................................................................................................................

213
Sezione 5 - Classi e oggetti

La classe che è stata derivata da un’altra usando l’ereditarietà viene detta sottoclas-
se. La classe generatrice di una sottoclasse prende il nome di sopraclasse.

La relazione di ereditarietà permette di individuare una gerarchia di classi che può essere descritta
graficamente usando un grafo ad albero. Esso è costituito da un grafo orientato i cui nodi sono rap-
presentati dalle classi, o meglio dai diagrammi delle classi, e gli archi individuano la presenza di una
relazione di ereditarietà.

Esempio Mezzi di trasporto ................................................................................................


Ipotizzare un grafico per la gerarchia delle classi relativa ai mezzi di trasporto.

Mezzi di trasporto

A motore Senza motore

Moto Auto Bus Barca Bici Cavallo Vela

Nell’esempio, la classe Moto è sottoclasse della classe dei veicoli A motore che a sua volta è sotto-
classe di Mezzi di trasporto.
In alto ci sono le classi più generiche che diventano più specializzate man mano si scende lungo la ge-
rarchia.

Nel diagramma sono state indicate le classi solo attraverso il loro nome, tralasciando l’elenco degli at-
tributi e dei metodi.

...........................................................................................................................................

La sottoclasse eredita dalla sopraclasse tutti gli attributi e tutti i metodi, evitando di ripetere la de-
scrizione degli elementi comuni nelle sottoclassi.
L’ereditarietà consente di condividere le similarità tra le classi e di inserire le differenze.
La nuova classe si differenzia dalla sopraclasse in due modi:

: per estensione, quando la sottoclasse aggiunge nuovi attributi e metodi che si sommano a
quelli ereditati, come nell’esempio della sottoclasse Autocarro che arricchisce di nuovi
membri la sopraclasse Auto;
: per ridefinizione, quando la sottoclasse ridefinisce i metodi ereditati. In pratica, viene data
un’implementazione diversa di un metodo. Si crea un nuovo metodo che ha lo stesso nome
del metodo ereditato da una sopraclasse, ma con funzionalità diversa. Quando per un ogget-
to della sottoclasse viene invocato un metodo ridefinito, viene eseguito il nuovo codice e non
quello che era stato ereditato.

214
Unitˆ didattica 12 - Concetti generali

12.5 Introduzione alle classi


Questo paragrafo introduce il concetto di classe che rappresenta la funzionalitˆ pi• importante del
linguaggio C++. Per avere unÕidea immediata di che cosa sia una classe possiamo usare come esem-
pio il fatto che tutti gli oggetti o esseri viventi, spesso, sono riconducibili a determinate categorie (per
esempio: computer, automobili, piante, animali e cos“ via). Queste categorie costituiscono le classi.

Una classe • una categoria o un gruppo di oggetti (con questo termine includiamo,
per comoditˆ, anche gli esseri viventi) che hanno attributi simili e comportamenti
analoghi.

In C++ per creare un oggetto si deve innanzitutto definire la sua forma generale utilizzando la paro-
la chiave class. Una classe ha una sintassi simile a una struttura e pu˜ contenere al suo interno ol-
tre che dati anche parte di codice (incapsulamento). Ecco un esempio.

Esempio ClasseRettangolo................................................................................................
Definire la classe Rettangolo che contiene al suo interno i dati relativi alla base b e al-
lÕaltezza h e che racchiude al suo interno anche i due metodi utilizzati per il calcolo del-
lÕarea e del perimetro.

Codice
classeRettangolo.cpp
1 #include <iostream>
2 using namespace std;
3
4 //definisci la classe
5 class Rettangolo
6 {
7 public:
8 //definisci i dati membro della classe
9 float b, h;
10
11 //definisci i due metodi
12 float area() //calcola lÕarea
13 {
14 return b*h;
15 }
16 float perimetro() //calcola il perimetro
17 {
18 return 2*(b+h);
19 }
20 };

Analisi del codice


Alla riga 5 viene introdotta, con la parola class, la classe Rettangolo. Alle righe 6 e 20 le parentesi
graffe delimitano il contenuto della classe. Alla riga 9 vengono definiti i dati relativi alla classe Rettan-
golo. PoichŽ non • specificato alcun livello di protezione per lÕaccesso ai membri della classe si assume,
per default, che b e h siano private; in questo caso, per semplificare lÕesempio successivo, la protezio-
ne • ÒpublicÓ. Le righe 12 e 16 sono le intestazioni dei due metodi della classe, area e perimetro.
...........................................................................................................................................

215
Sezione 5 - Classi e oggetti

Una classe pu˜ contenere parti private e pubbliche. In generale, tutti i membri definiti allÕinterno di
una classe sono privati.

I dati e i metodi private non sono visibili da alcunÕaltra funzione che non sia defini-
ta allÕinterno della classe. In assenza di indicazione (per default) si assume che il li-
vello di protezione sia private.

Questo • uno dei modi in cui si ottiene lÕincapsulazione: lÕaccesso a determinati membri pu˜ essere
controllato in modo rigido mantenendoli private.
NellÕesempio precedente le variabili b e h possono essere utilizzate solo dai metodi definiti allÕinter-
no della classe. Al contrario, i metodi area() e perimetro() possono essere utilizzati in qualsia-
si istruzione posta allÕesterno della definizione della classe.
Anche se questo non viene illustrato dallÕesempio presentato, • possibile definire funzioni private
che possono essere richiamate solo dai membri della classe.
Per rendere pubblica (ovvero accessibile da altre parti del programma) una parte della classe, • ne-
cessario dichiararla esplicitamente come pubblica utilizzando la parola chiave public. Tutte le va-
riabili e le funzioni definite dopo public possono essere utilizzate da tutte le altre funzioni del pro-
gramma. Essenzialmente, la parte rimanente del programma accede a un oggetto utilizzando le sue
funzioni pubbliche.

Anche se • possibile avere variabili pubbliche, si deve in generale cercare di limitarne lÕuso. Quindi si de-
ve cercare di rendere tutti i dati privati e controllare lÕaccesso ai dati utilizzando funzioni pubbliche.

Si ricordi che una classe racchiude metodi e dati. Solo i metodi di una classe hanno accesso ai dati
privati della classe in cui sono dichiarati. Perci˜ solo area() e perimetro() possono accedere
a b e h.

12.6 Terminologia e rappresentazione grafica


Per indicare i componenti di una classe esistono alcune varianti che possono creare qualche piccola
confusione. La definizione e lo schema che seguono si propongono di fare chiarezza.

Tutti gli elementi che compongono la definizione di una classe prendono il nome di
membri.
AllÕinterno dei membri si distinguono i dati membro e le funzioni membro.

Spesso, in luogo di dati membro e di funzioni membro vengono usati i termini pi• immediati di dati e
metodi: anche nel seguito della trattazione verrˆ utilizzata prevalentemente questÕultima dizione.
Per la documentazione delle applicazioni informatiche pu˜ essere usato lo standard UML (Uni-
fied Modeling Language, linguaggio unificato di modellazione). In tale standard si usano i termi-
ni attributi e operazioni corrispondenti, rispettivamente, a dati membro e funzioni membro. Fa-
cendo riferimento alla classe Rettangolo vista nellÕesempio precedente, si pu˜ realizzare lo
schema seguente.

ESEMPIO RETTANGOLO 1a DEFINIZIONE 2a DEFINIZIONE UML


b, h, area(), perimetro() membri membri membri
b, h dati membro dati attributi
area(), perimetro() funzioni membro metodi operazioni

216
Unitˆ didattica 12 - Concetti generali

Lo standard UML definisce anche le modalitˆ per rappresentare graficamente una classe. Una classe
viene rappresentata da un rettangolo. Il nome della classe, per convenzione, • una parola con lÕini-
ziale maiuscola e appare alla sommitˆ del rettangolo. Se il nome della classe consiste di una parola
composta, a sua volta, da pi• parole, allora viene utilizzata la notazione in cui tutte le iniziali di ogni
parola sono scritte in maiuscolo.

NomeClasse

Dopo il nome della classe vengono descritti i dati a essa appartenenti. Un dato rappresenta una pro-
prietˆ di una classe; esso descrive un insieme di valori che la proprietˆ pu˜ avere. Una classe pu˜ ave-
re zero o pi• dati. La lista dei dati di una classe viene separata graficamente dal nome della classe a cui
appartiene tramite una linea orizzontale.
Un dato il cui nome • costituito da una sola parola viene scritto sempre in caratteri minuscoli. Se, in-
vece, il nome del dato consiste di pi• parole (per esempio, informazioniCliente) allora il no-
me del dato viene scritto unendo tutte le parole che ne costituiscono il nome stesso con la particola-
ritˆ che la prima parola viene scritta in minuscolo mentre le successive hanno la loro prima lettera in
maiuscolo.

NomeClasse
tipo1 dato1
tipo2 dato2 = Òvalore defaultÓ
ÉÉ

Come si vede nella figura precedente, • possibile specificare un tipo per ogni dato (string, int,
bool ecc.). é anche possibile specificare il valore di default che un dato pu˜ avere.
Un metodo • unÕazione che gli oggetti di una certa classe possono compiere. Analogamente al nome
degli attributi, il nome di un metodo viene scritto con caratteri minuscoli. Anche qui, se il nome del-
lÕoperazione consiste di pi• parole, allora tali parole vengono unite tra di loro e ognuna di esse, eccet-
to la prima, viene scritta con il primo carattere maiuscolo. La lista dei metodi viene rappresentata gra-
ficamente sotto la lista degli attributi e separata da questa tramite una linea orizzontale.

NomeClasse
tipo1 dato1
tipo2 dato2 = Òvalore defaultÓ
ÉÉ

tipo1 metodo1()
tipo2 metodo2 (lista parametri)

Anche i metodi possono avere informazioni addizionali. Nelle parentesi che seguono il nome di un
metodo, infatti, • possibile mostrare gli eventuali parametri necessari al metodo insieme al loro tipo.
Infine, se il metodo rappresenta una funzione • necessario anche specificare il tipo restituito.

217
Sezione 5 - Classi e oggetti

12.7 Dichiarazione degli oggetti


Dopo avere definito una classe, • possibile creare un oggetto descritto dalla classe semplicemente im-
piegando il nome stesso della classe. In pratica, il nome della classe diviene un nuovo specif“catore di
tipo. Per esempio, la seguente istruzione crea lÕoggetto rett di tipo Rettangolo.

Rettangolo rett;

Si noti lÕanalogia tra la dichiarazione di un oggetto e la dichiarazione di una variabile. Si consideri,


per esempio, la variabile numero di tipo intero

int numero;

Quando si dichiara un oggetto di una classe, si crea unÕistanza di tale classe.

In questo caso, rett • unÕistanza di Rettangolo. é anche possibile creare oggetti nel luogo stes-
so in cui viene definita la classe, specificandone il nome dopo la parentesi graffa di chiusura, esatta-
mente come avviene nel caso delle strutture.

Per ricapitolare: in C++ class crea un nuovo tipo di dati che pu˜ essere utilizzato per
creare oggetti appartenenti alla classe.

Pertanto, un oggetto • unÕistanza di una classe esattamente come altre variabili sono istanze, per
esempio, del tipo int. In altre parole, una classe • unÕastrazione logica, mentre un oggetto • reale
(ovvero esiste allÕinterno della memoria del computer).
Quando si fa riferimento a un membro di una classe da una parte di codice che non si trova allÕinter-
no della classe stessa, lÕoperazione deve essere eseguita sempre in congiunzione con un oggetto di ta-
le classe. A tale scopo, si deve utilizzare il nome dellÕoggetto seguito dallÕoperatore punto seguito a
sua volta dal membro.
Questa regola si applica quando si deve accedere sia a dati membro sia a funzioni membro. Per esem-
pio, il frammento di codice seguente richiama il metodo area() per lÕoggetto rett.

Rettangolo rett; // dichiara lÕoggetto rett


. . .
rett.area(); // richiama il metodo area()

AllÕinterno di una classe, un metodo pu˜ richiamare un altro metodo oppure pu˜ fare riferimento di-
rettamente ai dati senza utilizzare lÕoperatore punto (.).
Il nome dellÕoggetto e lÕoperatore punto (.) devono essere utilizzati solo quando lÕaccesso a un
membro avviene da parte di codice che non appartiene alla classe.

Siamo ora in grado di esaminare il primo esempio completo di programma OOP.

Esempio Rettangolo2 .........................................................................................................


Scrivere lÕarea e il perimetro di un oggetto appartenente alla classe Rettangolo.

Indicazioni per la soluzione


La classe Rettangolo • giˆ stata definita nel paragrafo 12.5 ÒIntroduzione alle classiÓ, nellÕesem-
pio ClasseRettangolo; resta da specificare come utilizzare i metodi relativi alla classe.

218
Unitˆ didattica 12 - Concetti generali

Rettangolo2.cpp
1 #include <iostream>
2 using namespace std;
3
4 //definisci la classe
5 class Rettangolo
6 {
7 public:
8 //definisci i dati membro della classe
9 float b, h;
10
11 //definisci i due metodi
12 float area() //calcola lÕarea
13 {
14 return b*h;
15 }
16 float perimetro() //calcola il perimetro
17 {
18 return 2*(b+h);
19 }
20 };
21
22 //INIZIO
23 int main ()
24 {
25 //definisci lÕoggetto rett
26 Rettangolo rett;
27
28 //inizializza le variabili b, h
29 rett.b = 10;
30 rett.h = 6;
31
32 //scrivi lÕarea e il perimetro invocando i relativi metodi
33 cout << "\nArea del rettangolo = " << rett.area();
34 cout << "\nPerimetro rettangolo = " << rett.perimetro();
35
36 //fine programma
37 cout << "\n\nFine ";
38 system ("pause");
39 return 0;
40 }

Prova di esecuzione

Analisi del codice


Alla riga 5 viene introdotta la classe Rettangolo, seguita dalla definizione dei suoi membri. Alla ri-
ga 9 sono definiti i dati membro della classe; per semplificare il primo esempio e per non introdurre

219
Sezione 5 - Classi e oggetti

ulteriori precisazioni di tipo teorico, i dati membro hanno livello di protezione public, anche se ta-
le scelta risulta decisamente sconsigliabile.
Alle righe 12 e 16 sono definiti i metodi legati alla classe Rettangolo.
Alla riga 20 la parentesi graffa chiusa e il punto e virgola indicano la fine della definizione della
classe.
Alla riga 26 viene dichiarato lÕoggetto rett che appartiene alla classe Rettangolo.
Alle righe 29 e 30 • indicata lÕinizializzazione delle variabili b e h che fanno parte dellÕoggetto rett.
Alle righe 33 e 34, allÕinterno delle operazioni di scrittura a video, • presente lÕinvocazione dei me-
todi area() e perimetro().

La scelta di assegnare ai dati membro b e h il livello di protezione public • da sconsigliare: infatti i da-
ti membro di una classe non devono essere disponibili per le parti del programma esterne alla definizio-
ne della classe, vale a dire devono avere livello di protezione private. In questo modo si garantisce che
solo i metodi definiti allÕinterno della classe possono intervenire sui dati della classe stessa.

...........................................................................................................................................

Viene presentato ora un esempio dove i dati della classe vengono definiti private.

Esempio Rettangolo3 .........................................................................................................


Scrivere lÕarea e il perimetro di un oggetto appartenente alla classe Rettangolo. Le di-
mensioni del rettangolo sono acquisite da tastiera.

Indicazioni per la soluzione


Si riprenda lÕesempio Rettangolo.cpp visto sopra e si aggiunga, allÕinterno della classe, un nuo-
vo metodo per acquisire la base e lÕaltezza.

Rettangolo3.cpp
1 #include <iostream>
2 using namespace std;
3
4 //definisci la classe
5 class Rettangolo
6 {
7 //definisci i dati membro con protezione private
8 float b, h;
9
10 public:
11 //definisci i due metodi
12 float area() // calcola lÕarea
13 {
14 return b*h;
15 }
16 float perimetro() // calcola il perimetro
17 {
18 return 2*(b+h);
19 }
20
21 //leggi base e altezza da console
22 void leggi()
23 {
24 //leggi base

220
Unitˆ didattica 12 - Concetti generali

25 cout << "\nInserisci la base => ";


26 cin >> b;
27
28 //leggi altezza
29 cout << "\nInserisci altezza => ";
30 cin >> h;
31
32 return;
33 }
34 };
35
36 //INIZIO
37 int main ()
38 {
39 //definisci lÕoggetto rett
40 Rettangolo rett;
41
42 //invoca il metodo leggi
43 rett.leggi();
44
45 //scrivi lÕarea e il perimetro invocando i relativi metodi
46 cout << "\nArea del rettangolo = " << rett.area();
47 cout << "\nPerimetro rettangolo = " << rett.perimetro();
48
49 //fine programma
50 cout << "\n\nFine ";
51 system ("pause");
52 return 0;
53 }

Analisi del codice


Alla riga 8 vengono definiti i dati b e h; non • indicato il livello di protezione, quindi devono essere
considerati con livello di protezione private.
Alla riga 22 • stato inserito il nuovo metodo leggi, che acquisisce da tastiera la base e lÕaltezza. Il
metodo • stato inserito allÕinterno della classe Rettangolo, ed • quindi abilitato a lavorare sui
membri private (b e h) della classe.
Alla riga 43 • invocato il metodo leggi, che acquisisce i dati relativi allÕoggetto rett appartenen-
te alla classe Rettangolo.
Le righe da 40 a 52 contengono istruzioni esterne alla definizione della classe Rettangolo, per-
tanto in esse non compaiono le variabili b e h: queste possono essere manipolate solo allÕinterno dal-
la classe, dato che sono state definite private.
...........................................................................................................................................

221
Sezione 5 - Classi e oggetti

Esercizi
Unità didattica 12

1 Definire i dati e i metodi di un oggetto video. Disegnare il diagramma della classe.

2 Definire i dati e i metodi dell’oggetto lampada. Disegnare il diagramma della classe.

3 Definire i dati e i metodi dell’oggetto hardisk. Disegnare il diagramma della classe.

4 Definire i dati e i metodi dell’oggetto CPU. Disegnare il diagramma della classe.

5 Definire i dati e i metodi dell’oggetto pompaBenzina. Disegnare il diagramma della classe.

6 Definire i dati e i metodi dell’oggetto televisore. Disegnare il diagramma della classe.


7 Definire i dati e i metodi dell’oggetto distributore_automatico. Disegnare il diagramma della classe.
Per gli esercizi che seguono: disegna il diagramma della classe e realizza in C++ il programma che la
utilizza.

8 Definire la classe punto del piano cartesiano con i metodi sposta_a_destra, sposta_a_sini-
stra, sposta_in_alto e sposta_in_basso.

9 Definire la classe verifica con i dati materia, data e voto e con il metodo cambiaVoto. Disegna il
diagramma della classe e realizza in C++ il programma che utilizza tale classe.

q0 Definire la classe data_di_nascita e realizza il metodo che restituisce l’età.

qa Definire la classe NumeroReale con i metodi che restituiscono la parte intera e la parte decimale.
qs Definire la classe Approssimazione con il dato valoreIniziale e con tre metodi:
A perDifetto
B perEccesso
C interoPiuVicino

qd Definire la classe Segmento individuata dalle coordinate sul piano cartesiano dei suoi vertici e che pos-
siede i metodi per restituire la lunghezza del segmento e le coordinate del punto medio.

Indicare con un diagramma ad albero che sfrutta l’ereditarietà la gerarchia delle classi per le seguen-
ti categorie:

qf Periferiche di un calcolatore;
qg Elettrodomestici;

qh Specie animali;

qj Sport;
qk Poligoni.

ql Per ciascuno delle gerarchie indicate negli esercizi dal numero 14 al numero 18, individuare almeno un
dato e un metodo comune a tutte le classi.

222
Unitˆ didattica
13
Polimorfismo
ed ereditarietˆ
CHE COSA IMPARERAI A FARE

$ Utilizzare i costruttori per definire un oggetto


$ Parametrizzare un costruttore
$ Individuare i membri static di una classe
$ Definire e gestire un array di oggetti
$ Utilizzare un unico identificatore per invocare metodi con funzioni simili
$ Definire classi polimorfe
$ Sfruttare la possibilitˆ di derivare una classe da unÕaltra

CHE COSA DOVRAI STUDIARE

$ Sintassi dei costruttori parametrizzati


$ Definizione di un membro static
$ Metodologia per la definizione di array di oggetti
$ Sintassi per sfruttare lÕoverloading dei metodi
e dei costruttori
$ Procedimento per ereditare in una nuova classe
i membri di una classe pi• generale
Unitˆ didattica
Polimorfismo ed ereditarietˆ
13
13.1 Costruttori
é molto comune che una parte di un oggetto debba essere inizializzata prima dellÕuso. Per esempio,
ripensando alla classe Rettangolo sviluppata nella precedente Unitˆ didattica, per cominciare a la-
vorare con lÕoggetto rett, devono essere inizializzate le dimensioni b e h. Questo si ottiene richia-
mando il metodo leggi(). PoichŽ capita molto spesso di dover inizializzare un oggetto, C++ con-
sente di inizializzare gli oggetti al momento della creazione. Tale inizializzazione automatica • otte-
nuta grazie allÕimpiego di un costruttore.

Un costruttore • un particolare metodo di una classe: porta lo stesso nome della clas-
se e permette lÕinizializzazione automatica dellÕoggetto su cui si intende operare.

Esempio Costruttore...........................................................................................................
Con riferimento al codice dellÕesempio Rettangolo2 dellÕUnitˆ didattica 12, modificare il
modulo leggi() in modo da farlo rientrare come costruttore nella classe Rettangolo.

Codice
Costruttore.cpp
1 #include <iostream>
2 using namespace std;
3
4 //definisci la classe
5 class Rettangolo
6 {
7 //definisci i dati membro con protezione private
8 float b, h;
9
10 public:
11 //definisci i due metodi
12 float area() //calcola lÕarea
13 {
14 return b*h;
15 }
16 float perimetro() //calcola il perimetro
17 {
18 return 2*(b+h);
19 }
20 //COSTRUTTORE
21 //inizializza base e altezza leggendo dati da console
22 public: Rettangolo()
23 {
24 //leggi base
25 cout << "\nInserisci la base => ";

224
Unità didattica 13 - Polimorfismo ed ereditarietà

26 cin >> b;
27
28 //leggi altezza
29 cout << "\nInserisci altezza => ";
30 cin >> h;
31
32 return;
33 }
34 };

Analisi del codice


Alla riga 22 è definito il costruttore Rettangolo(). Si noti che:
: il costruttore è un metodo, quindi segue la relativa sintassi;
: l’identificatore della classe e quello del costruttore devono essere uguali;
: per il costruttore non viene specificato il tipo di dato restituito;
: poiché il costruttore deve essere invocato all’esterno della classe, è definito public.
...........................................................................................................................................

Nel caso specifico dell’esempio precedente, il costruttore ha il compito di acquisire dati da tastiera,
ma nella pratica la maggior parte dei costruttori non ha bisogno di input né di output: semplice-
mente, il metodo si occupa di eseguire varie inizializzazioni.
Il costruttore di un oggetto viene richiamato automaticamente nel momento in cui deve essere crea-
to l’oggetto.
Questo significa che viene richiamato al momento della dichiarazione dell’oggetto. Se si è abituati a
pensare che una dichiarazione sia un’istruzione passiva, occorre prepararsi a cambiare idea: in C++,
una dichiarazione è un’istruzione che viene eseguita come qualunque altra. La distinzione non è pu-
ramente accademica: il codice eseguito per costruire un oggetto può essere anche molto pesante,
pertanto, il costruttore di un oggetto viene richiamato una sola volta per ogni oggetto globale; nel
caso di oggetti locali, il costruttore viene richiamato ogni volta che si incontra la dichiarazione di un
nuovo oggetto.

L’operazione complementare del costruttore è svolta dal distruttore.

In molte circostanze, un oggetto deve eseguire una o più azioni nel momento in cui finisce la pro-
pria esistenza.
Gli oggetti locali vengono costruiti nel momento in cui si entra nel blocco ove si trovano e vengono
distrutti all’uscita dal blocco; gli oggetti globali vengono distrutti nel momento in cui termina il pro-
gramma. Quando viene distrutto un oggetto, viene automaticamente richiamato il relativo distrut-
tore (se presente).
Vi sono molti casi in cui è necessario utilizzare un distruttore. Per esempio, potrebbe essere neces-
sario de-allocare la memoria precedentemente allocata dall’oggetto oppure potrebbe essere necessa-
rio chiudere un file aperto. In C++ è il distruttore a gestire gli eventi di disattivazione.

Il distruttore ha lo stesso nome del costruttore ma è preceduto dal carattere ~.

Esempio Rettangolo4 .........................................................................................................


Modificare il codice dell’esempio Rettangolo2 dell’Unità didattica 12, inserendo il costrut-
tore e il distruttore per la classe Rettangolo.

225
Sezione 5 - Classi e oggetti

Codice
Rettangolo4.cpp
1 #include <iostream>
2 using namespace std;
3
4 //definisci la classe
5 class Rettangolo
6 {
7 //definisci i dati membro con protezione private
8 float b, h;
9
10 public:
11 //definisci i due metodi
12 float area() //calcola lÕarea
13 {
14 return b*h;
15 }
16 float perimetro() //calcola il perimetro
17 {
18 return 2*(b+h);
19 }
20 //COSTRUTTORE
21 //inizializza base e altezza leggendo dati da console
22 public: Rettangolo()
23 {
24 //leggi base
25 cout << "\nInserisci la base => ";
26 cin >> b;
27
28 //leggi altezza
29 cout << "\nInserisci altezza => ";
30 cin >> h;
31
32 //segnala costruzione completa
33 cout << "\nCostruzione rett \n ";
34
35 return;
36 }
37
38 //DISTRUTTORE
39 ~Rettangolo()
40 {
41 //segnala distruzione
42 cout << "\n\nDistruzione rett \n";
43 }
44 };
45
46 //INIZIO
47 int main ()
48 {
49 //definisci lÕoggetto rett
50 Rettangolo rett;
51
52 //scrivi lÕarea e il perimetro invocando i relativi metodi

226
Unitˆ didattica 13 - Polimorfismo ed ereditarietˆ

53 cout << "\nArea del rettangolo = " << rett.area();


54 cout << "\nPerimetro rettangolo = " << rett.perimetro();
55
56 rett.~Rettangolo();
57
58 //fine programma
59 cout << "\n\nFine ";
60 system ("pause");
61 return 0;
62 }

Prova di esecuzione

Analisi del codice


La riga 50 risulta ora completamente comprensibile: lÕoggetto rett viene creato con la struttura
definita dalla classe Rettangolo, viene riservata lÕarea di memoria necessaria per contenerlo
e viene invocato il metodo costruttore Rettangolo() che inizializza i dati membro definiti nel-
la classe.
Le righe 33 e 42, rispettivamente nei metodi costruttore e distruttore, sono state inserite per poter
controllare il flusso di elaborazione, ma non influiscono sullÕefficienza del programma. Pu˜ succe-
dere, invece, che il costruttore sia vuoto.
Alla riga 39 • presente il metodo distruttore. Si noti che anche i distruttori, come i costruttori, non
restituiscono alcun valore.
...........................................................................................................................................

13.2 Costruttori parametrizzati


I costruttori possono ricevere argomenti. Normalmente, questi argomenti aiutano a inizializzare un
oggetto al momento della creazione.
Per creare un costruttore parametrizzato, basta aggiungervi parametri cos“ come si fa con qualsiasi
altro metodo. Quando si definisce il corpo del costruttore si possono utilizzare i parametri per ini-
zializzare lÕoggetto.

Esempio Persona................................................................................................................
Dichiarare una classe di nome Persona con i dati cognome e nome e con due metodi: uno
per scrivere prima il cognome poi il nome, lÕaltro per scrivere prima il nome poi il cognome.
Utilizzare un costruttore parametrizzato che consegni a un oggetto della classe i dati ac-
quisiti da tastiera.

227
Sezione 5 - Classi e oggetti

Diagramma di classe

Persona
nome
cognome
cognomeNome()
nomeCognome()

Indicazioni per la soluzione


Definire un oggetto amico che appartiene alla classe Persona, acquisire il cognome e il nome da
tastiera allÕinterno del main e utilizzare tali dati come parametri del metodo costruttore per inizia-
lizzare un nuovo oggetto di nome amico.

Codice
Persona.cpp
1 #include <iostream>
2 #include <string>
3 using namespace std;
4
5 class Persona
6 {
7 //definisci i dati membro della classe con protezione private
8 string n; //Nome
9 string c; //Cognome
10 string tutto;
11
12 //concatena cognome e nome
13 public: string cognomeNome()
14 {
15 tutto = c+" "+n;
16 return tutto;
17 }
18
19 //concatena nome e cognome
20 public: string nomeCognome()
21 {
22 tutto = n+" "+c;
23 return tutto;
24 }
25
26 //COSTRUTTORE : inizializza c e n
27 public: Persona(string co, string no)
28 {
29 c = co;
30 n = no;
31 }
32 };
33
34 //INIZIO
35 int main ()
36 {
37 //dati di input

228
Unitˆ didattica 13 - Polimorfismo ed ereditarietˆ

38 string cognome;
39 string nome;
40
41 //leggi il cognome
42 cout << "\nInserisci cognome => ";
43 cin >> cognome;
44
45 //leggi nome
46 cout << "\nInserisci nome => ";
47 cin >> nome;
48
49 //dichiara l'oggetto amico di classe Persona
50 Persona amico(cognome,nome);
51
52 //scrivi prima il cognome e poi il nome
53 cout << "\nCognome e nome = " << amico.cognomeNome();
54
55 //scrivi prima il nome e poi il cognome
56 cout << "\nNome e cognome = " << amico.nomeCognome();
57
58 //fine programma
59 cout << "\n\nFine ";
60 system ("pause");
61 return 0;
62 }

Prova di esecuzione

Analisi del codice


Alla riga 5 viene definita la classe Persona. Alle righe 8 e 9 sono definiti i dati membro della classe
Persona. Alla righe 13 e 20 sono presenti i due metodi richiesti: entrambi concatenano le due
stringhe, inseriscono tra esse uno spazio bianco.
Alla riga 27 viene introdotto il metodo costruttore con i due parametri co e no che corrispondono
ai dati cognome e nome indicati. Dalla riga 42 alla riga 47 vengono acquisiti da tastiera il cognome
e il nome. Alla riga 50 viene invocato il costruttore che utilizza gli argomenti cognome e nome.
La tabella seguente riassume lo scambio di dati tra il main e la classe Persona.

MAIN PARAMETRI DEL COSTRUTTORE MEMBRI DELLA CLASSE


cognome → co → c
nome → no → n
...........................................................................................................................................

229
Sezione 5 - Classi e oggetti

I costruttori parametrizzati sono molto utili poichŽ evitano di dover eseguire una nuova chiamata di un
metodo semplicemente per inizializzare una o pi• variabili in un oggetto: ogni chiamata di un metodo evi-
tata rende il programma pi• efficiente.

13.3 Membri static di una classe


I metodi e i dati che sono membri di una classe possono essere resi static. Questo paragrafo spie-
ga cosa ci˜ significhi per ogni tipo di membro.

Quando la dichiarazione di una variabile membro • preceduta dalla parola chiave


static si chiede al compilatore di creare una sola copia di tale variabile e di utiliz-
zare tale copia per tutti gli oggetti della classe.

A differenza dei comuni dati membro, non viene creata una singola copia di una variabile membro
static per ogni oggetto.
Indipendentemente dal numero di oggetti creati per una classe, esiste una sola copia dei dati mem-
bro static. Pertanto, tutti gli oggetti di tale classe utilizzano la stessa variabile. Tutte le variabi-
li static vengono inizializzate a zero nel momento in cui viene creato il primo oggetto.
Quando si dichiarano i dati membro static allÕinterno di una classe, non si definiscono tali da-
ti. Questo significa che non si sta allocando spazio di memoria per tali dati (in C++, una dichiara-
zione descrive qualcosa e una definizione crea qualcosa). Si deve pertanto fornire una definizione
globale per i dati membro static in un altro punto, allÕesterno della classe.
LÕutilizzo di variabili dichiarate static comporta anche lÕintroduzione di un nuovo operatore, che
permette di specificare il campo dÕazione di una variabile.

LÕoperatore ::, chiamato operatore di risoluzione del campo di azione, specifi-


ca a quale classe appartiene un dato membro di tipo static o una funzione
membro.

Prima che una variabile dichiarata static possa essere utilizzata • necessario specificare lÕambito a
cui la variabile appartiene, utilizzando la sintassi riportata di seguito.
NomeClasse :: variabile;
Per comprendere lÕuso e gli effetti dei dati membri static, si consideri il seguente esempio.

Esempio Static....................................................................................................................
Definire una classe Esempio con due dati membro interi: a e b, con a dichiarato static.
Dichiarare e costruire due oggetti con valori iniziali diversi.
Verificare il comportamento di a e di b.

Diagramma di classe

ClasseEsempio
static a
b
mostra

230
Unitˆ didattica 13 - Polimorfismo ed ereditarietˆ

Static.cpp
1 #include <iostream>
2 using namespace std;
3
4 //dichiara la classe ClasseEsempio
5 class ClasseEsempio
6 {
7 //dichiara i dati membro della ClasseEsempio
8 static int a;
9 int b;
10
11 //inizializza a e b
12 public:
13 void imposta (int i, int j)
14 {
15 a = i;
16 b = j;
17 }
18
19 //mostra su quale oggetto si sta lavorando
20 //e il contenuto delle variabili
21 public: void mostra(int numOggetto)
22 {
23 cout << "\nIn oggetto " << numOggetto;
24 cout << "\nvariabile statica a = " << a;
25 cout << "\nvariabile non st. b = " << b<<Ó\nÓ;
26 }
27 };
28
29 int ClasseEsempio :: a; //definisci a
30
31 //INIZIO
32 int main ()
33 {
34 //dichiara due oggetti della classe ClasseEsempio
35 ClasseEsempio oggetto1;
36 ClasseEsempio oggetto2;
37
38 //inizializza a e b
39 oggetto1.imposta(10,10);
40
41 //mostra il contenuto di oggetto1
42 oggetto1.mostra(1);
43
44 //inizializza a e b
45 oggetto2.imposta(20,20);
46
47 //mostra il contenuto di oggetto2
48 oggetto2.mostra(2);
49
50 //mostra il contenuto di oggetto1
51 oggetto1.mostra(1);
52
53 //fine programma
54 cout << "\n\nFine ";

231
Sezione 5 - Classi e oggetti

55 system ("pause");
56 return 0;
57 }

Prova di esecuzione

Analisi del codice


Alla riga 29 viene utilizzato lÕoperatore ::, che segnala al compilatore che la variabile a appartiene
alla classe ClasseEsempio o, in altre parole, che a • nel campo dÕazione di ClasseEsempio.
Alla riga 39 il costruttore di oggetto1 assegna ad a e b il valore 10 allÕinterno di oggetto1 (come
evidenziato dal metodo mostra(): si vedano le prime tre righe dellÕoutput creato dalla prova di ese-
cuzione). Alla riga 45 il costruttore di oggetto2 assegna ad a e b il valore 20 allÕinterno di oggetto2
(come evidenziato dal metodo mostra(): si vedano le righe 4, 5 e 6 dellÕoutput creato dalla prova).
Alla riga 51 il metodo mostra() visualizza che anche in oggetto1 b ha assunto il valore 20, ma
evidenzia che a in oggetto1 contiene ancora 10 perchŽ ÒstaticoÓ, come indicato alla riga 8.
...........................................................................................................................................

Un altro interessante uso di una variabile membro static consiste nel registrare il numero di og-
getti esistenti per una determinata classe.

Esempio Counter................................................................................................................
Definire pi• oggetti per ClasseEsempio e mostrarne il numero.

Diagramma della classe

ClasseEsempio
a
b
ClasseEsempio()
mostra()

Codice
Counter.cpp
1 #include <iostream>
2 using namespace std;
3
4 //dichiara la classe Contatore
5 class Contatore

232
Unitˆ didattica 13 - Polimorfismo ed ereditarietˆ

6 {
7 //definisci i dati membro della classe Contatore
8 int a;
9 int b;
10 static int ctr; //contatore
11
12 //COSTRUTTORE, inizializza a e b
13 public:
14 Contatore (int i, int j)
15 {
16 a = i;
17 b = j;
18 ctr++;
19 }
20
21 //DISTRUTTORE
22 ~Contatore ()
23 {
24 ctr--;
25 }
26
27 //mostra numero degli oggetti
28 void mostra()
29 {
30 cout << "\nContatore oggetti = " << ctr;
31 }
32 };
33
34 int Contatore :: ctr; //definisci contatore
35
36 //INIZIO
37 int main ()
38 {
39 //dichiara un oggetto di classe Contatore
40 Contatore oggetto1(10,10);
41
42 //mostra numero oggetti
43 oggetto1.mostra();
44
45 //dichiara un oggetto di classe Contatore
46 Contatore oggetto2(20,20);
47
48 //mostra numero oggetti
49 oggetto1.mostra();
50
51 //distruggi oggetto2
52 oggetto2.~Contatore();
53
54 //mostra numero oggetti
55 oggetto1.mostra();
56
57 //fine programma
58 cout << "\n\nFine ";
59 system ("pause");
60 return 0;
61 }

233
Sezione 5 - Classi e oggetti

Prova di esecuzione

Analisi del codice


Alla riga 10 la variabile ctr • definita static.
Alla riga 18 la variabile ctr viene incrementata; questa istruzione • allÕinterno del metodo costrut-
tore, quindi ctr viene incrementata ogni volta che viene definito un oggetto.
Alla riga 24 la variabile ctr viene decrementata; lÕistruzione si trova allÕinterno del distruttore e,
quindi, segnala che un oggetto • stato escluso dal conteggio.
Alle righe 43 e 49 il metodo mostra() visualizza il numero di oggetti presenti.
...........................................................................................................................................

LÕimpiego di variabili membro static dovrebbe consentire di eliminare la necessitˆ di utilizzare variabi-
li globali. Il problema derivante dallÕuso di variabili globali in tecniche di programmazione a oggetti consi-
ste nel fatto che quasi sempre esse violano il principio di incapsulazione.

13.4 Overloading
Esaminiamo ora gli argomenti dellÕoverloading dei metodi e del polimorfismo.
LÕoverloading dei metodi • uno degli aspetti fondamentali del linguaggio di programmazione C++.
Infatti, esso non solo fornisce il supporto per il polimorfismo in fase di compilazione ma aggiunge al
linguaggio flessibilitˆ e comoditˆ. Tra i metodi modificati tramite overloading, quelli pi• importanti
sono i costruttori.

LÕoverloading consiste nellÕimpiegare lo stesso nome per due o pi• metodi.

Il ÒsegretoÓ dellÕoverloading • il fatto che ogni ridefinizione del metodo deve utilizzare parametri di
tipo differente oppure in numero differente.
Grazie a queste diversitˆ, il compilatore • in grado di scegliere quale metodo utilizzare in una deter-
minata situazione.

Esempio ValoreAss .............................................................................................................


Calcolare il valore assoluto di un numero indipendentemente dal suo tipo.

Indicazioni per la soluzione


Per ottenere il valore assoluto di un numero, basta cambiare il suo segno quando • negativo.
Si devono costruire diversi metodi che restituiscono il valore assoluto in funzione del tipo di dato che
ad essi viene passato dal programma chiamante: pertanto, si deve avere a disposizione un metodo per il
valore assoluto di un intero, di un intero lungo, di un double e cos“ via.
Sfruttando le proprietˆ dellÕoverloading, tutti i diversi metodi possono avere il medesimo nome, in mo-
do che nel programma principale si possa utilizzare un unico identificatore senza preoccuparsi del tipo
di dato su cui lavorare.

234
Unitˆ didattica 13 - Polimorfismo ed ereditarietˆ

Codice
ValoreAss.cpp
1 #include <iostream>
2 using namespace std;
3
4 class ValoreAssoluto
5 {
6 //restituisci il valore assoluto di un intero
7 public:
8 int abs(int intero)
9 {
10 if (intero<0)
11 {
12 intero = -intero;
13 }
14 return intero;
15 }
16 //restituisci il valore assoluto di un double
17 double abs(double doppio)
18 {
19 if (doppio<0)
20 {
21 doppio = -doppio;
22 }
23 return doppio;
24 }
25
26 //restituisci il valore assoluto di un intero lungo
27 long abs(long lungo)
28 {
29 if (lungo<0)
30 {
31 lungo = -lungo;
32 }
33 return lungo;
34 }
35 };
36
37 //INIZIO
38 int main ()
39 {
40 //definisci l'oggetto calcola della classe ValoreAssoluto
41 ValoreAssoluto calcola;
42
43 //scrivi il valore assoluto di un intero
44 //richiamando il metodo abs di calcola
45 cout << "\n" << calcola.abs(-10);
46
47 //scrivi il valore assoluto di un double
48 //richiamando il metodo abs di calcola
49 cout << "\n" << calcola.abs(-11.1);
50
51 //scrivi il valore assoluto di un int lungo
52 //richiamando il metodo abs di calcola

235
Sezione 5 - Classi e oggetti

53 cout << "\n" << calcola.abs(-10L);


54
55 //fine programma
56 cout << "\n\nFine ";
57 system ("pause");
58 return 0;
59 }

Prova di esecuzione

Analisi del codice


Alla riga 4 • definita la classe ValoreAssoluto per contenere tre versioni diverse del metodo abs
che ha la funzione di restituire il valore assoluto di un numero. Alle righe 8, 17 e 27 sono presenti le
intestazioni di tre metodi diversi, tutti con il nome uguale: il primo • in grado di operare su un para-
metro intero e restituisce un valore intero, il secondo svolge la medesima funzione, ma su valori
double e il terzo su valori di tipo intero lungo.
Alle righe 45, 49 e 53 viene richiamato il metodo abs con tre parametri diversi: rispettivamente per
un intero, per un double e per un intero lungo.
AllÕinterno della classe ValoreAssoluto sono definiti tre metodi diversi; allÕinterno del main si richia-
ma uno dei metodi definiti nella classe ValoreAssoluto utilizzando sempre il medesimo nome. LÕe-
sempio precedente cita solo tre tipi di dato, ma il programma pu˜ essere esteso per comprendere tutti i
tipi di dato numerici previsti da C++.

...........................................................................................................................................

Come si • detto, la caratteristica principale dellÕoverloading dei metodi • il fatto che questi devono
differire per quanto riguarda il tipo e/o il numero dei parametri.
Dunque due metodi non possono differire solo per il tipo di dato restituito. Per esempio, ecco un
modo errato per eseguire lÕoverloading: se per il metodo mioMetodo() si scrive:
int mioMetodo(int 1);
float mioMetodo(int 1);
si compie un errore perchŽ le due intestazioni differiscono solo per il tipo del valore restituito.

13.5 Polimorfismo
I costruttori possono essere modificati tramite overloading e, nella pratica comune, questo avvie-
ne molto spesso. I motivi principali che spingono a utilizzare il polimorfismo dei costruttori sono
la maggiore flessibilitˆ che pu˜ essere data alla definizione di una classe e la possibilitˆ di creare og-
getti tra loro simili appartenenti alla medesima classe.
Spesso si crea una classe per la quale esistono due o pi• modi per costruire un oggetto: in tal caso,
• opportuno fornire una funzione costruttore modificata tramite overloading per entrambi questi
metodi.

236
Unità didattica 13 - Polimorfismo ed ereditarietà

Questa è una regola fondamentale in quanto, se si cerca di creare un oggetto per il quale non esi-
ste un costruttore, viene prodotto un errore in fase di compilazione.
Offrendo un costruttore per ognuna delle modalità in cui un utilizzatore della classe può voler co-
struire un oggetto, si aumenta la flessibilità della classe.
L’utente è libero di scegliere il modo migliore per costruire un oggetto in una determinata circo-
stanza.

Il polimorfismo trova la sua realizzazione nell’uso di metodi e di costruttori modificati


tramite overloading.

Esempio Retta ....................................................................................................................


L’equazione di una retta sul piano cartesiano può essere definita in due modalità: attra-
verso la sua equazione in forma esplicita con una equazione del tipo:

ax + by + c = 0

oppure utilizzando la forma implicita con un’equazione del tipo:

y = mx + q

Nel primo caso, la retta è individuata da tre parametri: a, b, c ; nel secondo, dai para-
metri m, q.
Definire la classe retta con due costruttori, uno per la forma esplicita e uno per la forma im-
plicita.
Aggiungere alla classe Retta i metodi:

: mostraImplicita() per scrivere l’equazione della retta in forma esplicita (si ricordi
che m = -a/b e che q = –c/b);
: mostraIntersezioni() che visualizza le coordinate dei punti di intersezione della
retta con gli assi cartesiani.

Diagramma della classe

Retta
a, b, c // coefficienti retta in forma esplicita
m, q // coefficienti retta in forma implicita
Retta(a, b, c) //costruttore retta esplicita
Retta(m, q) //costruttore retta implicita
mostraImplicita()
mostraIntersezioni()

Codice
Retta.cpp
1 #include <iostream>
2 using namespace std;
3
4 //definisci la classe
5 class Retta

237
Sezione 5 - Classi e oggetti

6 {
7 double a, b, c; //coefficienti retta in forma esplicita
8 double m, q; //coefficienti retta in forma implicita
9
10 public:
11 //COSTRUTTORE della retta in forma esplicita
12 Retta(double c1, double c2, double c3)
13 {
14 a = c1;
15 b = c2;
16 c = c3;
17 m = -a/b;
18 q = -c/b;
19 }
20
21 //COSTRUTTORE della retta in forma implicita
22 Retta(double c1, double c2)
23 {
24 m = c1;
25 q = c2;
26 }
27
28 //scrivi la retta in forma implicita
29 void mostraImplicita()
30 {
31 cout << "\nY = " << m << "X + " << q << "\n";
32 }
33
34 //scrivi le coordinate delle intersezioni con gli assi cartesiani
35 void mostraIntersezioni()
36 {
37 cout << "\nIntersezioni asse X: (" << -q/m << "," << 0 << ")";
38 cout << "\nIntersezioni asse Y: (" << 0 << "," << q << ")";
39 }
40 };
41
42 //INIZIO
43 int main ()
44 {
45 double c1, c2, c3; //coefficienti della retta
46
47 //acquisisci i coefficienti della retta in forma esplicita
48 cout << "\nInserisci a ";
49 cin >> c1;
50 cout << "\nInserisci b ";
51 cin >> c2;
52 cout << "\nInserisci c ";
53 cin >> c3;
54
55 //dichiara l'oggetto esplicita della classe Retta
56 Retta esplicita(c1,c2,c3);
57
58 //invoca il metodo per visualizzare la retta
59 //anche in forma implicita
60 esplicita.mostraImplicita();

238
Unitˆ didattica 13 - Polimorfismo ed ereditarietˆ

61
62 //invoca il metodo per visualizzare le intersezioni con gli assi
63 esplicita.mostraIntersezioni();
64
65 //acquisisci i coefficienti della retta in forma esplicita
66 cout << "\n\nInserisci m ";
67 cin >> c1;
68 cout << "\nInserisci q ";
69 cin >> c2;
70
71 //definisci l'oggetto implicita della classe Retta
72 Retta implicita(c1,c2);
73
74 //invoca il metodo per visualizzare le intersezioni con gli assi
75 implicita.mostraIntersezioni();
76
77 //fine programma
78 cout << "\n\nFine ";
79 system ("pause");
80 return 0;
81 }

Prova di esecuzione

Osservazioni sulla prova di esecuzione


Il programma inizia con la richiesta dei valori dei coefficienti a, b, c (righe da 48 a 53). Successiva-
mente, visualizza lÕequazione della retta in forma implicita (riga 60) e le coordinate dei punti di in-
tersezione con gli assi cartesiani (riga 63).
In un secondo momento vengono richiesti i coefficienti m e q di una nuova retta, questa volta in
forma implicita (righe 66 e 68); lÕutente pu˜ inserire i valori di m e q leggendoli dallÕequazione
della retta precedente.
Il calcolo delle coordinate dei punti di intersezione dˆ risultati identici ai precedenti per lÕovvia ra-
gione che viene usato sempre lo stesso metodo definito una volta per tutte allÕinterno della classe.

Analisi del codice


Alla riga 5 viene definita la classe Retta. Le righe 7 e 8 descrivono i dati membro della classe e
hanno protezione private. La riga 12 • lÕintestazione del costruttore della classe con tre para-

239
Sezione 5 - Classi e oggetti

metri (a, b, c); tale costruttore • in overload con il costruttore definito alla riga 22, il quale con-
tiene solo due parametri (m e q) ed • in alternativa con il costruttore precedente. Sarˆ il compila-
tore a scegliere il costruttore opportuno in base al numero dei parametri specificati al momento
della definizione di un oggetto appartenente alla classe. Alla riga 29 viene dichiarato il metodo mo-
straImplicita(), che scrive lÕequazione della retta. Alla riga 35 viene dichiarato il metodo
mostraIntersezioni(), che scrive le coordinate dei punti di intersezione della retta con gli
assi cartesiani. Alla riga 56 viene dichiarato e definito lÕoggetto esplicita che appartiene alla
classe Retta: il suo costruttore utilizza tre parametri. Alle righe 60 e 63 vengono invocati dal-
lÕoggetto esplicita i metodi mostraImplicita() e mostraIntersezioni(). Alla riga
72 viene dichiarato e definito lÕoggetto implicita che appartiene alla classe Retta: il suo co-
struttore utilizza due parametri. Alla riga 75 viene invocato dallÕoggetto implicita il metodo
mostraIntersezioni().
...........................................................................................................................................

NellÕesempio precedente lo scopo dellÕoverloading del costruttore di Retta • quello di rendere pi• fles-
sibile e semplice il suo uso. Tale maggiore flessibilitˆ e facilitˆ dÕuso • particolarmente importante quan-
do si creano librerie di classi che possono essere utilizzate anche da altri programmatori.

13.6 Ereditarietˆ
LÕereditarietˆ costituisce una delle pietre angolari della programmazione orientata agli oggetti, in
quanto consente di creare classificazioni gerarchiche.
Utilizzando lÕereditarietˆ, • possibile creare una classe generale che definisce le caratteristiche comu-
ni a una serie di oggetti correlati. Tale classe pu˜, in seguito, essere ereditata da una o pi• classi,
ognuna delle quali aggiunge alla classe ereditata solo elementi specifici.

Per conservare la terminologia standard di C++, la classe ereditata viene chiamata


classe base. La classe che ÒriceveÓ lÕereditˆ • detta classe derivata. A sua volta, una
classe derivata pu˜ fungere da classe base per unÕaltra classe derivata: in questo
modo, • possibile riprodurre a pi• livelli il meccanismo di ereditarietˆ.

Il supporto dellÕereditarietˆ del C++ • ricco e flessibile. Quando una classe ne eredita unÕaltra, i mem-
bri della classe base divengono anche membri della classe derivata.
Per definire una classe derivata da unÕaltra si utilizza la sintassi riportata di seguito.
class Nome-classe-derivata : Nome-classe-base
{
corpo_della_classe
}

Esempio Ordine1 ................................................................................................................


Una societˆ commerciale vende un unico prodotto che viene descritto con la sigla Òpc
AMD quickÓ. Per il controllo e la gestione degli ordini ricevuti dalla societˆ, si devono de-
finire tre classi: Ordine, Spedizione, Fattura. Definire la classe Ordine attraver-
so gli attributi: descrizione, quantitˆ , prezzo, dataOrdine, indirizzo-
Cliente. Definire il suo costruttore e il metodo per visualizzare gli attributi. Derivare
dalla classe Ordine la classe Spedizione aggiungendo lÕattributo dataSpedizio-
ne. Derivare da questÕultima la classe Fattura aggiungendo il metodo per il calcolo del
totale della fattura.

240
Unitˆ didattica 13 - Polimorfismo ed ereditarietˆ

Indicazioni per la soluzione


Il problema verrˆ diviso in due parti: la prima riguarda la definizione della classe Ordine e la deri-
vazione della classe Spedizione; la seconda la derivazione della classe Fattura. La prima parte
consiste nel definire la classe Ordine: tale classe • composta dai dati membro richiesti dal proble-
ma: descrizione, quantitˆ , prezzo, dataOrdine, indirizzoCliente e possiede tre
metodi: due costruttori in overload e showOrdine.
Il metodo showOrdine mostra lÕimporto dellÕordine; i due costruttori hanno funzioni diverse:
uno • vuoto e privo di parametri e serve per lÕoperazione di derivazione, lÕaltro, con parametri, im-
posta i dati iniziali.

Diagramma delle classi

Ordine
descrizione
quantitˆ
prezzo
dataOrdine
indirizzoCliente
costruttore
showOrdine

Spedizione
dataSpedizione
showSpedizione

Codice
Ordine1.cpp
1 #include <iostream>
2 #include <string>
3 using namespace std;
4
5 //descrivi la classe Ordine
6 class Ordine
7 {
8 string descrizione;
9 int quantitˆ ;
10 double prezzo;
11 string dataOrdine;
12 string nomeCliente;
13
14 //metodo per la scrittura del contenuto di Ordine
15 public:
16 void showOrdine()
17 {
18 cout << "\nOrdine";
19 cout << "\nArticolo venduto " << descrizione;

241
Sezione 5 - Classi e oggetti

20 cout << "\nQuantitˆ venduta " << quantitˆ ;


21 cout << "\nPrezzo di vendita " << prezzo;
22 cout << "\nData ordine " << dataOrdine;
23 cout << "\nImporto " << quantitˆ *prezzo;
24 cout << "\nCliente " << nomeCliente;
25 }
26
27 //costruttore indispensabile per la derivazione
28 Ordine()
29 {
30 //metodo vuoto e senza parametri
31 }
32
33 //costruttore effettivo per l'impostazione dei dati
34 Ordine(string d, int q, double p, string dO, string nC)
35 {
36 descrizione = d;
37 quantitˆ = q;
38 prezzo = p;
39 dataOrdine = dO;
40 nomeCliente = nC;
41 }
42 };
43
44 //descrivi la classe Spedizione
45 class Spedizione : Ordine
46 {
47 string dataSpedizione;
48
49 public:
50 Spedizione(string dS)
51 {
52 dataSpedizione = dS;
53 }
54
55 //scrivi i dati di spedizione
56 void showSpedizione()
57 {
58 cout << "\nData spedizione " << dataSpedizione;
59 }
60 };
61
62 //INIZIO
63 int main ()
64 {
65 string d; //descrizione dell'articolo ordinato
66 int q; //quantitˆ ordinata
67 double p; //prezzo articolo
68 string dO; //data Ordine
69 string nC; //nome Cliente
70
71 //acquisisci i dati di Ordine
72
73 cout << "\nInserisci Descrizione "; cin >> d;
74 cout << "\nInserisci Quantitˆ "; cin >> q;
75 cout << "\nInserisci Prezzo "; cin >> p;

242
Unitˆ didattica 13 - Polimorfismo ed ereditarietˆ

76 cout << "\nInserisci data ordine "; cin >> dO;


77 cout << "\nInserisci nome cliente "; cin >> nC;
78
79 //dichiara e definisci l'oggeto ord
80 Ordine ord (d, q, p, dO, nC);
81
82 //scrivi il contenuto di ord
83 ord.showOrdine();
84
85 string dS; //data di spedizione
86
87 //acquisisci la data di spedizione
88 cout << "\n\nInserisci data spedizione "; cin >> dS;
89
90 //dichiara e definisci l'oggetto sped
91 //derivato da ord
92 Spedizione sped(dS);
93
94 //scrivi il contenuto di sped
95 sped.showSpedizione();
96
97 //fine programma
98 cout << "\n\nFine ";
99 system ("pause");
100 return 0;
101 }

Prova di esecuzione

Analisi del codice


Alla riga 6 inizia la descrizione della classe Ordine secondo le indicazioni del diagramma della clas-
se visto in precedenza.
Alla riga 28 • presente un costruttore vuoto in overload: tale costruttore non ha parametri e non con-
tiene istruzioni allÕinterno del corpo, ma deve comunque essere presente per rendere possibile la de-
rivazione della classe Spedizione dalla classe Ordine.

243
Sezione 5 - Classi e oggetti

Alla riga 34 è definito il costruttore effettivo della classe Ordine: ha la funzione di assegnare un va-
lore ai dati che appartengono alla classe.
Alla riga 45 inizia la descrizione della classe Spedizione e, attraverso il carattere “:” viene specifi-
cato che Spedizione è derivata da Ordine.
Dalla riga 47 alla riga 59 è presente il corpo della classe Spedizione: è molto semplice, ma va ri-
cordato che i suoi membri costituiscono una estensione dei membri della classe Ordine.
Dalla riga 73 alla riga 77 si trovano le istruzioni necessarie all’acquisizione dei dati per istanziare, at-
traverso il costruttore della classe, un nuovo oggetto della classe Ordine.
Alla riga 80 viene creato l’oggetto ord appartenente alla classe Ordine.
Alla riga 92 viene creato l’oggetto sped appartenente a Spedizione.

Va sottolineato il fatto che la riga 92 del codice invoca implicitamente il costruttore Ordine(). Nella
classe Ordine sono definiti in overloading due costruttori; in par ticolare, quello che viene invocato al
momento della creazione dell’istanza della classe derivata è il costruttore che è privo di parametri.
La conseguenza di tutto ciò è che i dati membro di Ordine vengono inizializzati una seconda volta fa-
cendo in modo che i dati numerici risultino nulli e i dati stringa diventino vuoti. Nell’esempio prece-
dente non si nota questo fatto, ma risulta evidente quando si vogliono utilizzare i dati della classe ba-
se. Vedremo nel prossimo esempio come ovviare a quanto sopra.

Per semplicità e per ridurre le righe di codice, le date sono state definite come stringhe compiendo, co-
sì, una piccola scorrettezza di programmazione.
...........................................................................................................................................

Viene affrontata ora la seconda parte del problema della gestione degli ordini: quello di produrre i
dati relativi alla fattura che la società deve emettere a fronte dell’ordine spedito. Per tale problema, ci
limitiamo a calcolare il totale della fattura.

Esempio Ordine2 ................................................................................................................


Calcolare il totale della fattura relativa a un ordine ricevuto. Ordine

Indicazioni per la soluzione descrizione


quantità
Il calcolo del totale della fattura è abbastanza semplice: basta calcola- prezzo
re il prodotto della quantità ordinata per il prezzo unitario e aggiun- dataOrdine
gere l’IVA. Si ipotizzi che l’IVA sia costante al 20%. La fattura è defi- indirizzoCliente
nita come una nuova classe che aggiunge il dato del totale da pagare
ai dati relativi all’ordine e alla spedizione. ordine()
In altri termini, definiamo la classe Fattura come derivata da Spe- showOrdine
dizione che, a sua volta, deriva da Ordine. Da quanto detto si ot- getImporto
tiene il seguente diagramma.
Spedizione
Diagramma delle classi E
dataSpedizione
spedizione()
showSpedizione

Fattura

totaleFattura

244
Unitˆ didattica 13 - Polimorfismo ed ereditarietˆ

Codice
Ordine2.cpp
1 #include <iostream>
2 #include <string>
3 using namespace std;
4
5 const int iva = 20;
6
7 //descrivi la classe Ordine
8 class Ordine
9 {
10 string descrizione;
11 static int quantitˆ ;
12 static double prezzo;
13 string dataOrdine;
14 string nomeCliente;
15
16 //metodo per la scrittura del contenuto di Ordine
17 public:
18 void showOrdine()
19 {
20 cout << "\nOrdine";
21 cout << "\nArticolo venduto " << descrizione;
22 cout << "\nQuantitˆ venduta " << quantitˆ ;
23 cout << "\nPrezzo di vendita " << prezzo;
24 cout << "\nData ordine " << dataOrdine;
25 cout << "\nImporto " << quantitˆ *prezzo;
26 cout << "\nCliente " << nomeCliente;
27 }
28
29 //costruttore indispensabile per la derivazione
30 Ordine()
31 {
32 //metodo vuoto e senza parametri
33 }
34
35 //costruttore effettivo per l'impostazione dei dati
36 Ordine(string d, int q, double p, string dO, string nC)
37 {
38 descrizione = d;
39 quantitˆ = q;
40 prezzo = p;
41 dataOrdine = dO;
42 nomeCliente = nC;
43 }
44
45 //restituisci l'importo dell'ordine
46 double getImporto()
47 {
48 return quantitˆ *prezzo;
49 }
50 };
51

245
Sezione 5 - Classi e oggetti

52 //descrivi la classe Spedizione


53 class Spedizione : public Ordine
54 {
55 string dataSpedizione;
56
57 public:
58 Spedizione(string dS)
59 {
60 dataSpedizione = dS;
61 }
62
63 //scrivi i dati di spedizione
64 void showSpedizione()
65 {
66 cout << "\nData spedizione " << dataSpedizione;
67 }
68
69 //costruttore indispensabile per la derivazione di Fattura
70 Spedizione()
71 {
72 //metodo vuoto e senza parametri
73 }
74 };
75
76
77 class Fattura : public Spedizione
78 {
79 public:
80 double totaleFatt()
81 {
82 const int iva = 20;
83
84 //aggiungi l'IVA all'importo dell'ordine
85 return getImporto()*(100+iva)/100;
86 }
87 };
88
89 double Ordine :: prezzo;
90 int Ordine :: quantitˆ ;
91
92 //INIZIO
93 int main ()
94 {
95 string d; //descrizione dell'articolo ordinato
96 int q; //quantitˆ ordinata
97 double p; //prezzo articolo
98 string dO; //data Ordine
99 string nC; //nome Cliente
100
101 //acquisisci i dati di Ordine
102 cout << "\nInserisci Descrizione "; cin >> d;
103 cout << "\nInserisci Quantitˆ "; cin >> q;
104 cout << "\nInserisci Prezzo "; cin >> p;
105 cout << "\nInserisci data ordine "; cin >> dO;

246
Unitˆ didattica 13 - Polimorfismo ed ereditarietˆ

106 cout << "\nInserisci nome cliente "; cin >> nC;
107
108 //Dichiara e definisci l'oggeto ord
109 Ordine ord (d, q, p, dO, nC);
110
111 //scrivi il contenuto di ord
112 ord.showOrdine();
113
114 string dS; //data di spedizione
115
116 //acquisisci data di spedizione
117 cout << "\n\nInserisci data spedizione "; cin >> dS;
118
119 //dichiara e definisci l'oggetto sped
120 //derivato da ord
121 Spedizione sped(dS);
122
123 //scrivi il contenuto di sped
124 sped.showSpedizione();
125
126 //dichiara e definisci l'oggetto fattura
127 //derivato da sped
128 Fattura fatt;
129
130 //scrivi il totale della fattura
131 //invocando il metodo totaleFattura
132 cout << "\nTotale fattura = " << fatt.totaleFatt();
133
134 //fine programma
135 cout << "\n\nFine ";
136 system ("pause");
137 return 0;
138 }

Prova di esecuzione

247
Sezione 5 - Classi e oggetti

Analisi del codice


Sono state segnalate in grassetto soltanto le parti nuove rispetto al listato Ordine.cpp.
Alle righe 11 e 12 i dati quantitˆ e prezzo vengono definiti static per evitare che i costrutto-
ri definiti alle righe 30 e 70 inizializzino i valori assegnati ai dati della classe Ordine.
Dalla riga 46 alla riga 49 viene definito il nuovo metodo che restituisce lÕimporto dellÕordine. Alla ri-
ga 70 viene inserito il costruttore di Spedizione che, come giˆ detto, ha la sola funzione di con-
sentire la derivazione della classe Fattura dalla classe Spedizione. Alla riga 77 inizia la descri-
zione della classe Fattura derivata da Spedizione.
Alla riga 80 troviamo il metodo che restituisce il totale della fattura: tale metodo appartiene alla clas-
se Fattura e utilizza il metodo getImporto appartenente alla classe Ordine. Alle righe 89 e 90
viene utilizzato lÕoperatore ::, che segnala al compilatore che le variabili prezzo e quantitˆ ap-
partengono alla classe Ordine o, in altre parole, che le variabili sono nel campo di azione della clas-
se Ordine.
Dalla riga 93 alla riga 127 il main non presenta novitˆ rispetto a quanto visto nellÕesempio Ordine.
La riga 128 dichiara e definisce lÕoggetto fatt, appartenente alla nuova classe Fattura, sul quale,
alla riga 132, viene invocato il metodo totaleFattura().
...........................................................................................................................................

248
Unità didattica 13 - Polimorfismo ed ereditarietà

Esercizi
Unità didattica 13
Per gli esercizi da 1 a 4 definire il costruttore.

1 Definire la classe punto del piano cartesiano con i metodi sposta_a_destra, sposta_a_sinistra,
sposta_in_alto e sposta_in_basso.

2 Definire la classe verifica con i dati materia, data e voto e con il metodo cambiaVoto. Disegna
il diagramma della classe e realizza in C++ il programma che utilizza tale classe.

3 Definire la classe data_di_nascita e realizza il metodo che restituisce l’età.

4 Definire la classe NumeroReale con i metodi che restituiscono la parte intera e la parte decimale.

5 Definire la classe Approssimazione con il dato valoreIniziale e con i seguenti tre metodi:
A perDifetto
B perEccesso
C interoPiuVicino

6 Definire la classe Segmento individuata dalle coordinate sul piano cartesiano dei suoi vertici e che pos-
siede i metodi per restituire la lunghezza del segmento e le coordinate del punto medio.

7 Definire la classe ArticoloInVendita con i dati prezzoDiVendita (in euro) e descrizione. Do-
po, definire i due oggetti:
: Articolo1 (30, “maglione”)
: Articolo2 (11, “camicia”)
Definire anche una variabile static per rappresentare la percentuale di un possibile sconto sul prezzo
di vendita.

8 Definire una classe che permetta la conversione da euro in lire e viceversa (si approssimi il fattore di
conversione a 2000. Si definiscano due costruttori: il primo con il parametro long (per le lire) il secon-
do con il parametro float (per gli euro).

9 Definire una classe che, per la misura degli angoli, permetta la conversione da gradi in radianti: si defi-
niscano due costruttori: uno per la misura in gradi che ha come parametri tre interi (gradi, minuti, se-
condi) e uno per la misura in radianti che ha un unico parametro di tipo float.

q0 Definire una classe Pesata che contiene i seguenti dati: nome, altezza (in metri) e peso (in kg) e che
possegga il metodo che valuta se una persona è normolinea. Una persona è normolinea se vale la rela-
zione 23 < peso / altezza2 < 27.

qa Definire per la classe Retta di equazione y = mx + q, i due membri funzione traslazione_lun-


go_l’asse_x e traslazione_lungo_l’asse_y. Derivare la classe Parabola di equazione y =
ax2 + bx + c, che eredita i metodi per le traslazioni.

qs Data una parabola y = ax2 + bx + c, definire una classe Parabola utilizzando i suoi tre coefficienti a, b,
c. Crea il suo costruttore e i metodi ascissaDelVertice(), ordinataDelVertice() e conca-
vità ().

249
Sezione 5 - Classi e oggetti

Esercizi
Unità didattica 13

qd Definire la classe Motore attraverso gli attributi cilindrata, marca, valvole. Definire l'intestazio-
ne dei metodi avvia(), accelera() e interrompi() senza implementarli. Derivare la classe Mo-
toreDiesel da Motore e ridefinire il metodo avvia().

qf Definire la classe IndirizzoGenerico con il metodo getIndirizzo(), che restituisce una stringa
vuota. Definire la classe IndirizzoEmail come classe derivata da IndirizzoGenerico, con l'at-
tributo account. Ridefinire il metodo getIndirizzo() per restituire il valore di account.

qg Definire la classe IndirizzoWeb come classe derivata di IndirizzoGenerico dell’esercizio pre-


cedente. IndirizzoWeb possiede l'attributo URL. Ridefinire il metodo getIndirizzo() per resti-
tuire l’URL.

qh Definire la classe IndirizzoFisico come classe derivata da IndirizzoGenerico. Indirizzo-


Fisico contiene gli attributi via, numero, cap, località . Ridefinisci il metodo getIndirizzo()
per restituire un'unica stringa che raccolga via, numero, cap e località .

qj Definire una classe Triangolo attraverso le misure a, b, c dei suoi tre lati. Definire il suo costruttore
e il metodo per calcolare il perimetro. Derivare da Triangolo la classe TriangoloIsoscele e defi-
nire il suo costruttore. Derivare da Triangololsoscele la classe TriangoloEquilatero e defini-
re il suo costruttore.

qk Data una parabola y = ax2 + bx + c, definire una classe Parabola utilizzando i suoi tre coefficienti a,
b, c. Crea il suo costruttore e i metodi ascissaDelVertice(), ordinataDelVertice() e con-
cavità ().

250
Sezione 6
Operare con gli archivi


Obiettivi
generali
◊ Riconoscere lÕimportanza dellÕarchiviazione dei dati
◊ Organizzare i file di dati in modo funzionale
◊ Comprendere la necessitˆ degli archivi di dati
nelle applicazioni gestionali
◊ Conoscere le caratteristiche dei file di testo

& Questo modulo contiene


U.D.14
U.D.15
Archivi
File di testo
Unitˆ didattica
14
Archivi

CHE COSA IMPARERAI A FARE

$ Riconoscere gli elementi fondamentali della definizione di un archivio


$ Definire la struttura di un archivio di dati
$ Descrivere gli aspetti fondamentali delle operazioni di I/O
$ Distinguere le caratteristiche fondamentali delle diverse organizzazioni
dei file
$ Distinguere le caratteristiche fondamentali dei diversi tipi di accesso

CHE COSA DOVRAI STUDIARE

$ Definizione di archivio
$ Definizione di record
$ Operazioni fondamentali sugli archivi
$ I/O standard e su memoria di massa
$ Tipi di archivio
$ Tipi di accesso
Unitˆ didattica 14 Archivi

I computer sono parte integrante della nostra vita da diversi anni. La loro integrazione con le nostre
attivitˆ quotidiane • talmente vasta e radicata che ormai non ci rendiamo nemmeno pi• conto della
loro presenza e di come contribuiscano, nel bene e nel male, alla nostra esistenza.
Siamo circondati dai computer praticamente in ogni momento della giornata. Infatti, pensare ai
computer solo come alle macchine che siamo abituati a vedere sui nostri tavoli al lavoro o nei labo-
ratori delle scuole e delle universitˆ, • piuttosto restrittivo: gran parte degli oggetti che ci circonda-
no possiedono al loro interno un piccolo processore. Nella sola automobile sono presenti, per esem-
pio, diversi computer: la centralina del motore, la centralina degli airbag, la centralina del condizio-
natore, la centralina degli impianti audio ecc.
Oltre ai computer come entitˆ fisiche (lÕhardware) anche i loro programmi (il software) sono par-
te integrante della nostra vita: la radiosveglia che ogni mattina interrompe i nostri sogni, infatti, •
un piccolo computer con un piccolo programma che si occupa di far scattare la suoneria allÕora pre-
stabilita.
Gran parte dei programmi esistenti non si limitano per˜ alla semplice gestione di una radiosveglia.
Pensiamo, per esempio, ai programmi che gestiscono lÕanagrafe di un Comune, oppure, pi• sempli-
cemente, a un programma che tenga aggiornato lÕelenco dei CD e dei DVD in nostro possesso. Una
funzione comune a tutti i programmi di gestione consiste nella memorizzazione, e in seguito nellÕe-
laborazione, di grandi quantitˆ di dati.
Questi dati vengono raggruppati in archivi, che generalmente sono gestiti attraverso un linguaggio
sviluppato ad hoc per interagire con essi, in grado di gestire insiemi di archivi tra loro integrati.
Gli archivi e il loro trattamento sono lÕargomento di questa Unitˆ didattica.

14.1 Definizione di archivio


Una delle prime necessitˆ con cui lÕinformatica ha dovuto confrontarsi • stata la memorizzazione dei
dati su un supporto da dove fosse possibile recuperarli in un secondo tempo.

Si definisce archivio lÕinsieme dei dati che vengono salvati su un supporto.


Un archivio viene anche chiamato generalmente file, che • la parola corrisponden-
te in lingua inglese.

é stata di proposito fornita una definizione di archivio molto generale, che pu˜ dare adito a dubbi e
sollevare domande. Vediamo quindi di approfondire il concetto.
Nel mondo reale ci • ben chiaro, per esperienza diretta, in che cosa consista un archivio. In qualsiasi
ufficio ci sia capitato di passare, per esempio la segreteria della scuola, avremo sicuramente avuto mo-
do di vedere la classica cassettiera in metallo, che contiene le schede personali degli studenti.
Pertanto, un archivio pu˜ essere visto e pensato come una struttura metallica con i cassetti. Negli stes-
si uffici avremmo per˜ potuto osservare, con la stessa probabilitˆ, uno scaffale pieno di grossi racco-
glitori ad anelli con delle etichette sul dorso. In questo caso, i raccoglitori sono archivi di documenti.
Il concetto di archivio in sŽ •, pertanto, un concetto astratto, che pu˜ essere adattato a una serie di
oggetti fisici anche molto diversi lÕuno dallÕaltro. Ci˜ che accomuna tutti gli archivi • lo scopo per cui
sono creati: mantenere raccolte le informazioni.

253
Sezione 6 - Operare con gli archivi

Da un punto di vista informatico, un archivio pu˜ essere semplicemente pensato come un comunis-
simo file.

Un file • lÕunitˆ logica di memorizzazione dei dati allÕinterno di un supporto informati-


co. Per supporto si intende un disco fisso, un floppy, un CD-ROM, un DVD oppure ogni
altra forma di memorizzazione possibile, presente o futura.

Come impareremo nel seguito della trattazione, un singolo file non costituisce una raccolta di dati
sufficiente per risolvere un problema di tipo gestionale, ma inizieremo da questo concetto per af-
frontare il tema della definizione e della strutturazione di una base di dati.

14.2 Dati
Ora che abbiamo definito un archivio in ambito informatico, possiamo esaminare gli elementi che
pu˜ contenere.
Abbiamo infatti visto come sia possibile, in generale, pensare a un archivio come a un contenitore di
dati. Il concetto di dato • un concetto astratto esattamente come quello di archivio. In pratica, in che
cosa consistono i dati che ÒsalviamoÓ in un archivio?

Si pu˜ definire dato una qualsiasi informazione che si vuole registrare o memorizzare
allÕinterno di un computer.

Ancora una volta, la definizione lascia il campo aperto a molte considerazioni, essendo il concetto
molto generale. Vediamo quindi qualche esempio.

Esempio Data .....................................................................................................................


Scrivere un programma che si limita a salvare su un file la data del giorno in cui • stato ese-
guito lÕultima volta.

In questo caso, il dato da salvare • la data di sistema e lÕarchivio consiste in un singolo file di testo,
dove ci si limita a scrivere la data.
...........................................................................................................................................

LÕutilitˆ di un tale programma pu˜ sembrare piuttosto limitata. Tuttavia non dobbiamo dimenticare che
ogni programma, per quanto piccolo e ÒstupidoÓ, pu˜ essere parte di un sistema che diventa molto com-
plesso, se visto nel suo insieme. Quando si accede a una macchina Unix, per esempio, il sistema indica
sul terminale quando lÕutente si • interfacciato con il sistema lÕultima volta.

Esempio Diario ...................................................................................................................


Scrivere un programma per gestire un diario di bordo. é necessario salvare, per ogni anno-
tazione, la data, lÕora e il luogo. Il programma deve poter tenere traccia delle ultime anno-
tazioni.

In questo caso il dato da salvare non • pi• semplice, ma consiste in diversi ÒpezziÓ di informazione.
Anche in questo caso, tuttavia, possiamo limitarci a salvare le varie parti come se si trattasse di co-
munissimo testo, separandole tra loro con un carattere di separazione (generalmente la virgola).
LÕarchivio, ancora una volta, consiste di un singolo file di testo.
...........................................................................................................................................

254
Unità didattica 14 - Archivi

Esempio Studenti................................................................................................................
Scrivere un programma per gestire l’elenco degli studenti di una scuola. Il programma de-
ve essere in grado di associare un elenco di studenti con la classe e la sezione a cui essi
appartengono.

A prima vista, questo caso sembra più semplice dei precedenti. In realtà è leggermente più comples-
so, poiché a una singola informazione (la classe e la sezione, che consideriamo un unico dato), deve
venire associato un elenco più o meno numeroso di nomi (in realtà, di nomi e cognomi che, per sem-
plicità, possiamo pensare come un unico dato).
La soluzione più semplice consiste nel salvare più file, uno per ogni classe, nominandoli con il nome
della classe e della sezione (per esempio, 3A). All’interno di ogni singolo file, i nomi degli alunni sa-
ranno scritti in formato testuale, separando i nomi dai cognomi con un semplice spazio (o con un al-
tro carattere separatore).
In questo caso, quindi, l’archivio è in realtà suddiviso in molti file (uno per ogni classe dell’istitu-
to), e i dati in esso contenuti sono banale testo che, eventualmente, comprende un carattere sepa-
ratore.
...........................................................................................................................................

Ora che sappiamo in che cosa consistono i dati, possiamo approfondire ulteriormente il discorso.

14.3 Definizione di record


Come abbiamo visto negli esempi del paragrafo precedente, quando il problema da risolvere diven-
ta appena più complesso, aumenta anche la complessità dei dati e dell’archivio che li contiene. Spes-
so le informazioni che devono essere salvate sono suddivise logicamente in più parti.

L’unità macroscopica delle informazioni prende il nome di record. Un singolo record


può essere suddiviso logicamente in più parti.

Nell’esempio “Diario” del paragrafo precedente, il singolo record è costituito dall’insieme di infor-
mazioni che sono logicamente raggruppate tra loro, ovvero:
: data;
: ora;
: luogo;
: annotazione.
Dal punto di vista grafico, un record può essere pensato come la riga di una tabella:
Data Ora Luogo Annotazione

Chiaramente, anche le singole unità di informazione che formano un record hanno un nome.

Le parti logiche in cui viene suddiviso un singolo record prendono il nome di campi.
Normalmente, il nome del campo deriva dal tipo di informazione che è associata al
campo stesso.

Risulta piuttosto comodo poter fare riferimento ai singoli campi assegnando loro un nome: nell’e-
sempio “Diario”, i nomi dei campi potrebbero essere proprio “Data”, “Ora”, “Luogo” e “Annota-
zione”.

255
Sezione 6 - Operare con gli archivi

Esempio Studenti e classi ..................................................................................................


Alla luce di quanto appreso in questo paragrafo, trovare un’altra soluzione per il problema
dell’esempio “Studenti” visto nel paragrafo precedente.

Poiché ora siamo in grado di gestire singoli record, possiamo riformulare la soluzione nel modo de-
scritto di seguito. Per ogni iscritto all’istituto conosciamo il nome, il cognome, la classe e la sezione.
Il record in questo caso è (schematicamente) il seguente:
Sezione Classe Nome Cognome

A questo punto, l’archivio potrà consistere in un unico file, che contiene i record sopra descritti, con
i singoli campi separati da un carattere separatore, come per esempio il punto e virgola.
...........................................................................................................................................

Ora che abbiamo imparato un po’ di teoria, possiamo passare alla pratica per gestire queste strutture
di dati.

14.4 Operazioni fondamentali sugli archivi


Finora ci siamo limitati a pensare a come strutturare i nostri archivi e i nostri record. Tuttavia è ne-
cessario pensare anche a come gestire gli archivi e i record nella pratica.
Esistono diverse operazioni standard che occorre prevedere per poter gestire i dati; esse possono es-
sere riassunte schematicamente in questo modo:

: apertura dell’archivio;
: lettura dei record;
: scrittura dei record;
: modifica dei record (compresa la cancellazione);
: chiusura dell’archivio.

Queste operazioni possono sembrare piuttosto banali, ma ancora una volta una riflessione più atten-
ta potrebbe portarci a riclassificare il problema.
Consideriamo, per esempio, la semplice apertura dell’archivio. L’operazione di apertura dell’archi-
vio può cambiare a seconda del tipo e del numero di file che si stanno aprendo. In generale, l’opera-
zione è abbastanza semplice e consiste in una chiamata alla funzione di sistema di apertura del file.
Per completezza, scriviamo una funzione che apre un file il cui nome viene passato come parametro.

Esempio Apertura e chiusura di un file ..............................................................................


1 #include <iostream>
2 #include <fstream>
3 #include <string>
4 using namespace std;
5
6 //INIZIO
7 int main ()
8 {
9 . . . .
10 . . . .
11 //apre il file dati
12 ofstream dati("dati.txt");
13

256
Unitˆ didattica 14 - Archivi

14 //controlla esito apertura


15 if(!dati)
16 {
17 cout << "ERRORE apertura file";
. . . .
. . . .
}

. . . .
. . . .

31 dati.close();

. . . .
. . . .

Analisi del codice


Alla riga 2 • stata aggiunto lÕheader <fstream>, che permette la gestione di file. Alla riga 12 viene
eseguita lÕapertura del file, in particolare va specificato che il file di nome dati • definito come og-
getto della classe ofstream (che • una classe derivata da fstream). In questo caso il prefisso ÒofÓ
indica che il file • aperto in scrittura.
Alla riga 15 viene controllato il buon esito dellÕoperazione di apertura.
Mentre lÕoperazione di apertura crea un collegamento tra il file specificato nel programma e il file
presente nel file system, lÕoperazione di chiusura (indicata alla riga 31) disattiva tale collegamento e
non permette pi• ulteriori operazioni sul file.
...........................................................................................................................................

Al momento dellÕapertura di un file devono essere specificati due nomi: il nome logico a cui farˆ riferi-
mento il programma e il nome fisico, che • quello utilizzato dal file system del sistema operativo.
NellÕesempio precedente il nome logico del file • dati, mentre il suo nome fisico • dati.txt. Ci˜
significa che le operazioni sul file dati specificate nel programma vengono fisicamente eseguite sul fi-
le dati.txt.

14.5 I/O standard e su memoria di massa


Concettualmente, la lettura e la scrittura dei dati in un archivio sono operazioni semplici. Siamo abi-
tuati a leggere e a scrivere fin da bambini, e pertanto i meccanismi di astrazione e i processi mentali
che mettiamo in atto per leggere e scrivere ci sono naturali.
Chiaramente, non • cos“ quando si tratta di insegnare a una macchina a effettuare le stesse operazio-
ni. Come vedremo nel seguito della trattazione, la semplice operazione di lettura presuppone unÕor-
ganizzazione dei dati allÕinterno dellÕarchivio.
Per il momento, limitiamoci a considerare le operazioni di lettura e scrittura nella loro forma pi• ge-
nerale.

Si dice operazione di lettura (o di input) lÕoperazione che permette di trasferire un da-


to o un insieme di dati da una unitˆ periferica nella memoria RAM; si dice operazio-
ne di scrittura (o di output) il trasferimento di un dato o di un insieme di dati dalla me-
moria RAM alla periferica.

257
Sezione 6 - Operare con gli archivi

LÕinterazione tra utente e calcolatore avviene principalmente attraverso un terminale composto da


una tastiera e un video.
Queste due periferiche costituiscono le periferiche standard per le operazioni di input e di output:
in particolare, la tastiera viene detta unitˆ di standard input, mentre il video • lÕunitˆ di standard
output. Ci˜ significa che le istruzioni di lettura e scrittura dei dati possono avere due formati: uno
senza indicazione specifica della periferica usata (e in questo caso si intende che vengono utilizza-
te le unitˆ standard); lÕaltro con lÕindicazione della risorsa (nome file e/o periferica) che viene uti-
lizzata.
Per documentare le risorse che vengono impiegate allÕinterno di unÕapplicazione informatica si pos-
sono usare i simboli descritti nella tabella sottostante.

RISORSA SIMBOLO NOTE


Video Vuole rappresentare un tubo
catodico visto di profilo.

Tastiera

Memoria centrale

Memoria di massa Vuole rappresentare


un diskpack.

Stampante Vuole rappresentare un foglio


di carta strappato a metˆ.

Programma

Flusso dei dati

Utilizzando i simboli descritti sopra, lo standard input e lo standard output sono descritti grafica-
mente nello schema sottostante.

Output

Input

Le operazioni di scrittura su memoria di massa e di lettura da memoria di massa sono simboleggiate


dagli schemi riportati di seguito.

258
Unitˆ didattica 14 - Archivi

Output / scrivi

Input / leggi

Normalmente, nella descrizione delle applicazioni viene indicato anche il programma, mentre il ter-
minale viene rappresentato con il simbolo sottostante.

Se, per esempio, si vuole schematizzare un programma che acquisisce dati da tastiera e li registra su
un disco magnetico, ci si dovrˆ affidare al disegno che segue.

Registra.exe
Dati.dat

Dove ÒRegistra.exeÓ • il nome del programma e ÒDati.datÓ • il nome del file creato dal programma.

14.6 Tipi di archivio


Ora abbiamo una visione pi• chiara di che cosa sia e di come si possa gestire un archivio. Possiamo
quindi concentrarci su come • fatto. Se pensiamo agli esempi riportati in questa Unitˆ didattica, pos-
siamo giˆ capire che gli archivi possono essere di tipo diverso. Il primo tipo di archivio che ci viene in
mente, per esempio, • il semplice file di testo.
In un file di testo le informazioni vengono salvate usando solamente i caratteri standard ASCII 127
(tipicamente), e di norma costituiscono un file che pu˜ essere letto con un qualsiasi editor di testi,
come per esempio il BloccoNote di Windows.
Anche se questo tipo di archivio pu˜ sembrarci banale e di scarsa utilitˆ, non dobbiamo dimenticare
che la maggior parte dei programmi esistenti utilizza proprio questo tipo di archivio per i parametri
di configurazione. Risulta infatti piuttosto semplice scrivere un programma che si modifica in ma-
niera dinamica leggendo i dati da un file di configurazione ogni volta che viene attivato.

In ambiente Windows, i file di configurazione hanno solitamente estensione .ini e si trovano nella di-
rectory del sistema. Possono essere divisi per sezioni, ma contengono tutte le informazioni necessarie
per configurare un programma. Anche se ultimamente si preferisce usare il registro di configurazione del
sistema al posto dei file di inizializzazione, in alcuni programmi lÕelenco dei file utilizzati pi• di recente si
trova nel file di configurazione.

Proseguendo nella lettura degli esempi proposti in questa Unitˆ didattica, possiamo notare come i
dati vengano spesso strutturati sotto forma di record. In questo caso, come viene organizzato lÕar-
chivio? Una metodologia piuttosto semplice per risolvere i problemi consiste nel rifarsi a un caso pre-
cedente di cui si conosce giˆ la soluzione. In questo caso, possiamo rifarci nuovamente al file di te-
sto. Si tratta solamente di decidere come suddividere i dati che fanno parte dei record. Tipicamente,
si sceglie un carattere separatore e lo si utilizza per dividere fisicamente i dati.

259
Sezione 6 - Operare con gli archivi

Il formato di file CSV (Comma Separated Values, cioè valori separati da virgola), uni-
versalmente adottato, prevede proprio che i campi del record vengano separati dal
carattere “,” (virgola).

Quando si sviluppano programmi ad hoc, tuttavia, non sempre si adottano gli standard di sviluppo
internazionali, ma si impiega il carattere meno utilizzato dall’applicazione (per esempio “#”).
La metodologia appena illustrata non è certamente l’unica quando si vuole gestire un file di record,
ma essa offre una certa facilità di implementazione e di gestione. Per questo motivo è largamente uti-
lizzata nelle applicazioni create ad hoc, mentre per le basi di dati commerciali (Oracle, SQLServer,
MySQL) si utilizzano altre tecniche più complesse. L’illustrazione di queste ultime esula dagli scopi
di questo libro, e pertanto si rimanda il lettore alla letteratura specializzata.

Un ultimo metodo per salvare i dati in un archivio consiste nel salvataggio a byte. In
questo caso, i singoli record vengono scritti sull’archivio come un flusso continuo. Poi-
ché la quantità di byte occupata dal singolo record e dai campi che lo compongo-
no è nota a priori, l’estrazione dei dati avviene tenendo conto della lunghezza fissa
dei record.

14.7 Tipi di accesso


È possibile accedere a un file in diversi modi, fra cui i principali sono:
: il sequenziale;
: il diretto.

Si accede a un file in modo sequenziale se tutte le operazioni di lettura e di scrittura


possono avvenire solo scorrendo il file dall’inizio fino al punto in cui vogliamo legge-
re o scrivere i dati.

In altre parole, supponiamo che un database contenga un milione di record, e il nostro obiettivo sia
leggere l’ultimo record contenuto nell’archivio. Non c’è alcun modo per posizionarsi sull’ultimo re-
cord se non quello di leggere preventivamente tutti i 999.999 record che lo precedono. Chiaramen-
te, un file ad accesso sequenziale non è molto utile nel caso in cui si vogliano salvare molti dati, a cau-
sa della pesantezza nella sua elaborazione (è sempre necessario partire dal primo record e scorrerli
tutti). Solitamente, gli archivi di tipo sequenziale sono file di testo dove vengono salvati pochi dati
utili per l’esecuzione di un programma. In questo caso, poiché il programma, per funzionare, deve
leggere tutte le impostazioni di configurazione, diventa agevole per il programmatore utilizzare un
file ad accesso sequenziale, che è molto semplice da gestire.

Si accede a un file in modo diretto se tutte le operazioni di lettura e di scrittura posso-


no avvenire in modo puntuale sul singolo record, indipendentemente dalla sua posi-
zione all’interno dell’archivio.

Supponiamo sempre di avere un archivio con un milione di record al suo interno e che il nostro
obiettivo sia leggere l’ultimo record. Poiché questa volta l’archivio è ad accesso diretto, possiamo po-
sizionarci subito sull’ultimo record e leggerlo senza prima scorrere tutti gli altri record. Questo tipo
di accesso è molto utile nel caso in cui si stia trattando un archivio con dati strutturati organizzati a
record, in quanto, a parte l’apertura del file, non c’è alcuna operazione preventiva da compiere, e
quindi il tempo di accesso a un singolo dato si abbassa.

260
Unità didattica 14 - Archivi

Esercizi
Unità didattica 14

1 Le parti logiche in cui viene suddiviso un singolo record prendono il nome di ......................................

2 Elencare le operazioni fondamentali sui file.

3 Nel primo schema della figura che segue la freccia indicare un’operazione di .....................................
nel secondo un’operazione di .........................................................................................................

4 Indicare i tipi di organizzazione dei file.

5 Completare la definizione:
Un ........................... è l’unità logica di memorizzazione dei dati all’interno di un supporto informatico.
Per supporto si intende un disco fisso, un floppy, un CD-ROM, un DVD oppure ogni .............................

6 Completare la definizione:
Si può definire ...................................... qualsiasi informazione che si vuole registrare o memorizzare
all’interno di un computer.

7 Completare la definizione:
Si dice operazione di .............................. l’operazione che permette di trasferire un dato o un insieme
di dati da una unità periferica nella memoria RAM.
Si dice operazione di ....................................... il trasferimento di un dato o di un insieme di dati dalla
memoria RAM alla periferica.

8 Che cosa si intende per unità standard di input e per unità standard di output?

9 Leggere il seguente schema:

Registra.exe
Dati.dat

q0 Completare la definizione:
Si accede a un file in modo ............................................ se tutte le operazioni di lettura e di scrittura
possono avvenire solo scorrendo il file dall’inizio fino al punto in cui vogliamo leggere o scrivere i dati.

qa Completare la definizione:
Si accede a un file in modo ............................................ se tutte le operazioni di lettura e di scrittura
possono avvenire in modo puntuale sul singolo record, indipendentemente dalla sua posizione all’inter-
no dell’archivio.

261
Unitˆ didattica
15
File di testo

CHE COSA IMPARERAI A FARE

$ Definizione degli elementi fondamentali per la creazione


e la lettura di un file di testo e per lÕaccodamento
di una riga

CHE COSA DOVRAI STUDIARE

$ Concetto di creazione di un file di testo


$ Descrizione delle istruzioni
$ Concetto di lettura di un file di testo
$ Descrizione delle istruzioni
$ Concetto di accodamento
$ Descrizione delle istruzioni
Unità didattica 15File di testo
15.1 Creazione di un file di testo
Vi sono tre tipi di file: file di input, file di output e file di input/output. Per creare un file di input lo
si deve dichiarare come oggetto della classe ifstream; per creare un file di output lo si deve di-
chiarare come oggetto della classe ofstream; i file su cui devono essere eseguite operazioni sia di
input che di output devono essere dichiarati come oggetti di classe fstream. Il frammento di co-
dice che segue, per esempio, crea un file di input di nome in, un file di output di nome out e un fi-
le su cui è possibile eseguire operazioni sia di input sia di output di nome io.

ifstream in; //input


ofstream out; //output
fstream io; //input e output

Esempio CreaDati..............................................................................................................
Registrare in un file sequenziale una serie di numeri inseriti da tastiera.

Indicazioni per la soluzione


Per ogni dato devono essere eseguite le seguenti operazioni:

: leggi un numero da tastiera;


: scrivi un numero nell’archivio di dati.

Poiché il problema non specifica quanti sono i numeri da inserire, per ogni inserimento si deve ripe-
tere anche la domanda se l’elenco è finito. Si deve, quindi, realizzare una ripetizione che contenga le
seguenti istruzioni:

: leggi un numero da tastiera;


: scrivi il numero nell’archivio di dati;
: chiedi “elenco finito ? (s/n)”;
: leggi la risposta.

Queste quattro operazioni devono essere eseguite almeno una volta e devono essere ripetute per
ogni numero “mentre” l’elenco non è finito, ovvero quando la risposta alla domanda “elenco fini-
to?” è uguale a “n”.

Pseudocodifica
INIZIO
Apri il file in scrittura
SE operazione NON ha avuto successo
ALLORA
termina programma
FINE SE

263
Sezione 6 - Operare con gli archivi

RIPETI
chiedi e leggi un numero da tastiera
scrivi un numero nellÕarchivio di dati
chiedi Òelenco finito ? (s/n)Ó
leggi la risposta
MENTRE risposta = ÒnÓ
Chiudi file
FINE

Flusso dei dati

CreaDati.exe
dati.txt

Codice
CreaDati.cpp
1 #include <iostream>
2 #include <fstream>
3 #include <string>
4 using namespace std;
5
6 //INIZIO
7 int main ()
8 {
9 double numero;
10 string risp;
11
12 //apri file dati in scrittura
13 ofstream dati("dati.txt");
14
15 if(!dati) //se l'operazione non ha avuto successo
16 {
17 //termina programma
18 cout << "ERRORE apertura file";
19 //fine programma
20 cout << "\n\nFine ";
21 system ("pause");
22 return 1 ;
23 }
24
25 do //RIPETI
26 {
27 //chiedi e leggi un dato da tastiera
28 cout << "Inserisci un numero: ";
29 cin >> numero;
30
31 //scrivi un dato nellÕarchivio di dati
32 dati << numero << "\n" ;
33
34 //chiedi se elenco finito

264
Unitˆ didattica 15 - File di testo

35 cout << "Elenco finito? (s/n) ";


36 cin >> risp;
37 }
38
39 while (risp == "n"); //MENTRE elenco NON finito
40
41 //chiudi file
42 dati.close();
43
44 //fine programma
45 cout << "\n\nFine ";
46 system ("pause");
47 return 0;
48 }

Analisi del codice


Alla riga 2 viene richiamata la libreria <fstream>, che consente la gestione dei file di testo.
Alla riga 13 lÕoperazione di apertura del file permette di creare il collegamento tra il nome dati
utilizzato nel programma e il file fisico dati.txt, utilizzato dal file system. Il file dati • creato
come oggetto della classe ofstream, che deriva da fstream e che serve per la gestione dei file
in output.
Alla riga 15 viene controllato il buon esito dellÕoperazione di apertura, e in caso negativo il pro-
gramma viene interrotto.
La riga 32 serve per scrivere sul file di dati il numero letto da tastiera. Balza agli occhi lÕanalogia con
le operazioni di scrittura a video: nello specifico, al posto della funzione cout troviamo il nome del
file aperto alla riga 13.

Prova di esecuzione

Per verificare i buon esito della prova di esecuzione si pu˜ leggere il contenuto del file dati.txt,
utilizzando il programma Blocco note di Windows.

265
Sezione 6 - Operare con gli archivi

L’operazione di apertura contiene il nome fisico del file dati.txt, ma non è indicato il percorso di tale
file. La conseguenza è che il file di testo viene creato all’interno della cartella in cui si trova il program-
ma; se si vuole creare il file in un’altra cartella si deve indicare, oltre al nome, anche il percorso: per
esempio “C:\archivi\dati.txt” .

...........................................................................................................................................

15.2 Lettura di un file di testo


Esempio LeggiDati ............................................................................................................
Visualizzare il contenuto del file creato dall’esecuzione dell’esempio “CreaDati”.

Indicazioni per la soluzione


Su ogni riga del file dati.txt devono essere eseguite le operazioni che seguono:
: leggi riga;
: visualizza riga.

Tali operazioni devono essere ripetute fintantoché non si raggiunge la fine delle righe che compon-
gono il file.

Per segnalare che è stata raggiunta la fine del file si deve utilizzare il metodo eof()
(“eof” sta per End of File). Tale metodo restituisce un valore booleano: true se è stato
fatto un tentativo di lettura oltre la fine del file, false altrimenti.

Analisi dei dati


Input
File dati.txt
Output
Serie di numeri

Flusso dei dati

dati.txt LeggiDati.exe

Pseudocodifica
INIZIO
apri il file dati.txt in lettura
SE il file non esiste
termina il programma
leggi un numero dal file
FINE SE
MENTRE il file non è finito
scrivi un numero a video
leggi un numero dal file

266
Unitˆ didattica 15 - File di testo

RIPETI
chiudi il file
FINE
Nella pseudocodifica si nota che la prima operazione di lettura • stata posta al di fuori della struttura
di ripetizione MENTRE-RIPETI (si dice che viene effettuata una Òlettura fuori giroÓ), mentre le al-
tre istruzioni di lettura (compresa lÕultima) si trovano allÕinterno della struttura di ripetizione. La
scelta del costrutto MENTRE-RIPETI porta ad eseguire unÕoperazione di lettura in pi•, quello del
carattere di fine file che fa restituire true al metodo eof(); inoltre, dopo tale lettura non si deve
eseguire alcuna elaborazione. La lettura iniziale (fuori giro) • necessaria per fare iniziare il ciclo di ri-
petizione: infatti, se il file non • vuoto risulta dati.eof() = false.

Codice
LeggiDati.cpp
1 #include <iostream>
2 #include <fstream>
3 #include <string>
4 using namespace std;
5
6 //INIZIO
7 int main ()
8 {
9 double numero;
10 //apri il file in lettura
11 ifstream dati("dati.txt");
12
13 if(!dati) //SE il file non esiste
14 {
15 //termina programma
16 cout << "ERRORE apertura file";
17 //fine programma
18 cout << "\n\nFine ";
19 system ("pause");
20 return 1 ;
21 }
22
23 //leggi il primo numero del file
24 dati >> numero;
25
26 while (!dati.eof()) //MENTRE il file non • finito
27 {
28 //scrivi il numero letto a video
29 cout << numero << "\n";
30 //leggi un numero
31 dati >> numero;
32 } //RIPETI
33
34 //chiudi il file
35 dati.close();
36
37 //fine programma
38 cout << "\nFine ";
39 system ("pause");
40 return 0;
41 }

267
Sezione 6 - Operare con gli archivi

Prova di esecuzione

Analisi del codice


L’apertura del file in lettura indicata alla riga 11 utilizza la classe ifstream. Come si vede nella
pseudocodifica, l’istruzione di lettura dal file è presente in due punti: alla riga 24 (lettura fuori giro)
e alla riga 31 (all’interno del ciclo di ripetizione). In queste due istruzioni si nota come l’operatore
>> è utilizzabile per leggere un flusso di dati, proveniente dalla tastiera o da un file di input.
...........................................................................................................................................

15.3 Accodamento
Oltre alle istruzioni di apertura dei file viste nei due esempi precedenti (in output o in input), esiste
una terza modalità, più accurata delle precedenti, che permette di specificare in maniera più detta-
gliata il criterio di apertura di un file.

La modalità con cui viene aperto un file può essere specificata mediante i flag di mo-
dalità di apertura, i cui valori possono essere scelti tra quelli dell’enumerazione
openmode, che è un membro pubblico della classe base degli stream ios_base.
Eccoli:
: ios_base::app: il file viene aperto posizionandosi in fondo in modo permanente,
cioè i nuovi dati vengono inseriti sempre in fondo al file (modalità “Append”);
: ios_base::ate: il file viene aperto posizionandosi inizialmente in fondo (modalità
“At The End”);
: ios_base::binary: il file viene aperto in modo binario, cioè trattandone il
contenuto come puri dati;
: ios_base::in: il file viene aperto in lettura (modalità “Input”);
: ios_base::out: il file viene aperto in scrittura (modalità “Output”);
: ios_base::trunc: il file viene aperto cancellandone l’eventuale contenuto
esistente, creandolo se non esiste.
Si ricorda che, per default, un file viene aperto posizionandosi sul primo carattere e
trattandone il contenuto come testo, producendo una segnalazione di errore nel ca-
so si stia tentando di aprire un file non esistente.

La tabella che segue presenta qualche esempio chiarificatore.

INVECE DI È POSSIBILE SCRIVERE


ifstream dati(“dati.txt”); fstream dati;
dati.open(“dati.txt”, ios::in);
ofstream dati(“dati.txt”); fstream dati;
dati.open(“dati.txt”, ios::out);

268
Unitˆ didattica 15 - File di testo

Particolare interesse riveste la modalitˆ ios_open::app, che permette di accodare (append, in in-
glese) nuovi dati a un file giˆ esistente.

Esempio Accoda................................................................................................................
Aggiungere altri numeri al file dati.txt, senza cancellare i dati preesistenti.

Codice
Accoda.cpp
1 #include <iostream>
2 #include <fstream>
3 #include <string>
4 using namespace std;
5
6 //INIZIO
7 int main ()
8 {
9 double numero;
10 string risp;
11
12 //apri file dati in append
13 fstream dati;
14 dati.open(Òdati.txtÓ, ios::out|ios::app);
15
16 if(!dati) //se l'operazione non ha avuto successo
17 {
18 //termina programma
19 cout << "ERRORE apertura file";
20 //fine programma
21 cout << "\n\nFine ";
22 system ("pause");
23 return 1 ;
24 }
25
26 do //RIPETI
27 {
28 //chiedi e leggi dato da tastiera
29 cout << "Inserisci un numero: ";
30 cin >> numero;
31
32 //scrivi dato su archivio dati
33 dati << numero << "\n" ;
34
35 //chiedi elenco finito
36 cout << "Elenco finito? (s/n) ";
37 cin >> risp;
38 }
39
40 while (risp == "n"); //MENTRE elenco NON finito
41
42 //chiudi file
43 dati.close();
44
45 //fine programma

269
Sezione 6 - Operare con gli archivi

46 cout << "\n\nFine ";


47 system ("pause");
48 return 0;
49 }

Prova di esecuzione

Anche in questo caso il buon esito del programma pu˜ essere facilmente verificato mediante la lettu-
ra dei dati contenuti nel file dati.txt, aprendolo con Blocco note.

Analisi del codice


Rispetto allÕesempio creaDati.cpp cambia lÕistruzione alla riga 13 che da

13 ifstream dati("dati.txt");

• stata modificata nelle due istruzioni indicate di seguito.

13 fstream dati;
14 dati.open(ÒDati.txtÓ, ios::out|ios::app);

Come si vede, la riga 13 definisce lÕoggetto dati come di classe fstream, la riga 14 specifica che il
file deve essere aperto in scrittura (modalitˆ ios::out) e con lÕobbligo di inserire i nuovi dati in
fondo al file (modalitˆ ios::app). Per lÕapertura di un file possono essere specificate diverse mo-
dalitˆ, combinate tra di loro dallÕoperatore di OR bit a bit (indicato nel codice con il simbolo Ò|Ó):
ogni flag differisce infatti dagli altri per un solo bit.
...........................................................................................................................................

270
Unità didattica 15 - File di testo

Esercizi
Unità didattica 15

1 Le modalità di apertura di un file sono: ............................................................................................

2 Le modalità di apertura di un file di testo in C++ sono: .....................................................................

3 L’operazione di apertura di un file in C++ associa al file fisico uno .....................................................

4 La sintassi di apertura di un file di testo in C++ è: ............................................................................

5 La sintassi di scrittura di una riga in un file di testo in C++ è: ............................................................

6 Trascrivere in un file di testo le prime due terzine della Divina Commedia.

7 Disegnare lo schema del flusso dei dati del programma dell’esercizio precedente.

8 Creare un file di testo per archiviare una lista di nomi con relativo numero telefonico; il nome e il nume-
ro telefonico devono essere separati da un punto e virgola.

9 Aggiungere un nuovo nome e relativo numero telefonico al file creato nell’esercizio precedente.

q0 Trascrivere in un file di testo i dati di una matrice di tre righe e quattro colonne: ogni riga della matrice
occupa una riga del file, e i numeri sono separati da una virgola.

qa Che cosa significa eof? .................................................................................................................

qs A che cosa serve la funzione eof?


A Indica che il programma legge anche la fine del file
B Rileva quando è stata raggiunta la fine di un file
C Controlla che il file abbia una fine
D Aggiunge un segnale di fine al file

qd Che tipo di valore restituisce la funzione eof?


A Intero
B Singola precisione
C Booleano
D Stringa

qf Quale struttura di controllo contiene l’algoritmo usato per leggere tutte le righe di un file di testo?
A Selezione
B Selezione multipla
C Ripetizione precondizionale
D Ripetizione postcondizionale
E Ripetizione enumerativa

qg L’istruzione C++ per leggere una riga da un file di testo ha la seguente sintassi: ................................

271
Sezione 6 - Operare con gli archivi

Esercizi
Unità didattica 15

qh Indicare la sintassi della funzione eof in C++.

qj Trascrivere in un file di testo le prime due terzine della Divina Commedia. Successivamente, con un al-
tro programma, visualizzare le terzine.

qk Disegnare lo schema del flusso dei dati dei programmi dell’esercizio precedente.

ql Creare un file di testo per archiviare una lista di nomi con relativo numero telefonico; il nome e il nume-
ro telefonico devono essere separati da un punto e virgola. Successivamente, con un altro programma,
visualizzare il contenuto del file senza mostrare il punto e virgola inserito come separatore.

w0 Creare un file di testo per archiviare una lista di nomi con relativo numero telefonico; il nome e il nume-
ro telefonico devono essere separati da un punto e virgola. Successivamente, con un altro programma,
mostrare il numero telefonico di un nome inserito da tastiera.

wa Creare un file di testo dove sono archiviati i cognomi e i nomi degli studenti di una classe. Successiva-
mente, mostrare cognome e nome degli studenti (possono essere più di uno) che hanno cognome ugua-
le a un cognome inserito da tastiera.

272
Sezione 7
Le eccezioni


Obiettivi ◊ Costruire programmi robusti
◊ Prevenire gli errori di risposta dellÕutente
◊ Tenere sotto controllo le anomalie del sistema
operativo

& Questa sezione contiene


U.D.16 Gestione delle eccezioni
16
Unitˆ didattica
Gestione delle eccezioni

CHE COSA IMPARERAI A FARE

$ Valutare gli errori che unÕapplicazione pu˜ generare


$ Scrivere codice per la gestione degli errori
$ Controllare la fine di un programma con anomalie

CHE COSA DOVRAI STUDIARE

$ Sintassi per la gestione degli errori


$ Sintassi del costrutto Òtry..catchÓ
Unitˆ didattica
Gestione delle eccezioni
16
16.1 Concetto di anomalia
Questa Unitˆ didattica presenta uno dei meccanismi offerti dal linguaggio C++ per gestire gli erro-
ri, le anomalie e ogni altro imprevisto che si pu˜ verificare durante lÕesecuzione di un programma.
Abbiamo visto che durante la compilazione di un programma una delle funzioni del compilatore
consiste nel controllare che la sintassi e la grammatica di quanto • stato scritto nel codice siano
esatte. Osserviamo cosa succede se proviamo a compilare il codice dellÕesempio riportato di se-
guito.

Esempio ErroreDivisione ....................................................................................................

ErroreDivisione.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 int dividendo;
7 int divisore;
8
9 //chiedi e leggi dividendo
10 cout << "Inserisci il valore del dividendo ";
11 cin >> dividendo;
12
13 //chiedi e leggi divisore
14 cout << "Inserisci il valore del divisore ";
15 cin >> divisore;
16
17 //dichiara la variabile per il risultato
18 int divisione;
19
20 //esegui lÕoperazione
21 divisione=dividendo/divisore;
22
23 //scrivi il risultato
24 cout << "\n Divisione = " << divisione << endl;
25
26 //fine programma
27 cout << "\nFine ";
28 system ("pause");
29 return 0;
30 }

Il compilatore non trova nulla da segnalare, quindi il programma risulta corretto e, se i dati inseriti
sono validi, si ottiene quanto riportato nella prova di esecuzione che segue.

275
Sezione 7 - Le eccezioni

Prova di esecuzione

Nel programma, per˜, non • presente alcun controllo sulla validitˆ dei dati e, se lÕutente inserisce 0
come valore per il divisore, succede quanto riportato nella figura che segue.

Come si pu˜ vedere, si • verificata unÕanomalia che produce un arresto del programma e non per-
mette di procedere nellÕesecuzione dellÕoperazione.
...........................................................................................................................................

UnÕanomalia del codice consiste in una situazione ingestibile e rilevabile solo duran-
te lÕesecuzione del programma.

Le anomalie sono quasi sempre dovute a errori di progettazione o di scrittura del codice: solo la pro-
gettazione e la stesura attenta possono evitarci di incorrere in anomalie del codice.
Un modo per difendersi dalle anomalie consiste nella gestione degli errori.

Per codice di gestione degli errori si intende quella parte di codice che si occupa di
gestire lÕeventualitˆ che si verifichino errori a run-time (cio• in fase di esecuzione).

Nel caso visto sopra della divisione per zero, la gestione degli errori pu˜ essere realizzata modifican-
do il codice ErroreDivisione.cpp come indicato di seguito.

21 if (divisore !=0)
22 {
23 //esegui lÕoperazione

276
Unitˆ didattica 16 - Gestione delle eccezioni

24 divisione=dividendo/divisore;
25
26 //scrivi il risultato
27 cout << "\n Divisione = " << divisione << endl;
28 }
29 else
31 {
32 cout << "\n Divisione impossibile " << endl;
33 }
34 //fine programma
35 . . .
36 . . .
37 }

Il fatto che il codice controlli il valore della variabile divisore fa parte di una buona procedura di
gestione degli errori, perchŽ previene le anomalie di esecuzione.

16.2 Eccezioni
Quanto abbiamo detto fino a questo momento sulla gestione degli errori vale in qualsiasi linguaggio
di programmazione. Il fatto che le procedure per la gestione degli errori non siano standardizzate fa
s“ che ognuno di noi adotti un proprio stile di programmazione, anche se in generale • opportuno
seguire uno schema o unÕindicazione di massima per la stesura del codice; negli anni, per favorire la
creazione di uno standard, • stato introdotto il concetto di ÒeccezioneÓ.

UnÕeccezione • unÕanomalia nellÕesecuzione del codice e la gestione delle eccezioni


consiste in un meccanismo standard definito a livello del linguaggio che permette di
controllare e risolvere le anomalie.

La gestione delle eccezioni consiste in una sintassi standard che definisce le direttive di massima per
il trattamento delle anomalie di esecuzione, lasciando tuttavia il programmatore libero di realizzare
il codice per la loro gestione nel modo che ritiene pi• opportuno.
La gestione delle eccezioni non costituisce quindi, di per sŽ, la gestione degli errori, bens“ fornisce
un metodo per realizzare questÕultima.

Quando il codice • in esecuzione e si verifica una anomalia, viene lanciata (throw,


in inglese) automaticamente unÕeccezione, per gestire la situazione attraverso un co-
dice di controllo che cattura (catch) lÕeccezione e che agisce di conseguenza. é an-
che possibile generare esplicitamente unÕeccezione.

La prima cosa da definire per gestire le eccezioni • il blocco try.

Un blocco try consiste nella parola chiave try seguita da un blocco di codice
racchiuso da parentesi graffe, la cui esecuzione viene controllata dal gestore delle
eccezioni.

Vediamo come riscrivere il codice dellÕesempio precedente, alla luce di quanto ora detto. Per farlo ab-
biamo bisogno, per˜, di affrontare il concetto di generazione delle eccezioni.
La generazione di unÕeccezione avviene attraverso il comando throw, che provoca lÕuscita dal blocco
di programmazione in cui tale parola chiave si trova e trasferisce il controllo al gestore dellÕeccezione.

277
Sezione 7 - Le eccezioni

Il comando throw riceve in input un solo argomento, che pu˜ essere di qualsiasi tipo. In base al ti-
po dellÕeccezione generata il gestore sarˆ diverso. Vediamo un codice dÕesempio chiarificatore.

Esempio Eccezione ............................................................................................................


Riscrivere il programma ÒErroreDivisioneÓ in modo da catturare e gestire lÕanomalia Òdivi-
sione per zeroÓ.

Codice
Eccezione.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 int dividendo;
7 int divisore;
8
9 //chiedi e leggi il dividendo
10 cout << "Inserisci il valore del dividendo ";
11 cin >> dividendo;
12
13 //chiedi e leggi il divisore
10 cout << "Inserisci il valore del divisore ";
15 cin >> divisore;
16
17 //dichiara la variabile per il risultato
18 int divisione;
19
20 try
21 {
22 //controlla divisore
23 if (divisore == 0)
24 {
25 throw 100;
26 }
27
28 //esegui la divisione
29 divisione = dividendo/divisore;
30
31 //scrivi il risultato
32 cout << "\n Divisione = " << divisione << endl;
33 }
34
35 catch (int codice)
36 {
37 cout << "Codice errore = "<< codice << endl;
38 cout << "programma interrotto ";
39 }
40
41 //fine programma
42 cout << "\n\nFine ";
43 system ("pause");
44 return 0;
45 }

278
Unitˆ didattica 16 - Gestione delle eccezioni

Prove di esecuzione

Analisi del codice


Dalla riga 20 alla riga 33 si sviluppa il blocco try. La riga 23 controlla se il divisore • uguale a zero
e, in caso affermativo, viene subito lanciato il gestore dellÕeccezione, attraverso lÕistruzione indicata
alla riga 25 e, inoltre, le righe da 27 a 32 successive allÕistruzione throw vengono ignorate.
Se il divisore • diverso da zero lÕistruzione di riga 25 non viene eseguita e il programma procede con le
istruzioni alle righe 29 e 32, che eseguono il calcolo e la scrittura a video del risultato della divisione.
Dalla riga 35 alla riga 39 si sviluppa il blocco catch, la cui intestazione • simile a quella di una fun-
zione. Il parametro codice contiene il codice di errore specificato dopo il comando throw (in que-
sto caso, il valore Ò100Ó). Sia nel caso in cui non sia stato riscontrato alcun errore, sia nel caso in cui
sia stato eseguito il blocco catch, il controllo passa allÕistruzione contenuta nella riga 42.
...........................................................................................................................................

AllÕinterno di un blocco try possono essere analizzate diverse situazioni di errore e, quindi, posso-
no essere presenti pi• istruzioni throw. Per ogni valore associato a throw deve essere poi presente
un blocco di istruzioni corrispondente, in grado di gestire lÕeccezione generata.

Il gestore dellÕeccezione • un blocco di codice che viene eseguito solo quando il


blocco try a esso associato genera unÕeccezione.

Il blocco di gestione delle eccezioni • caratterizzato da una o pi• parole chiave catch, ciascuna se-
guita dal proprio blocco di programmazione; ogni parola chiave catch deve, perci˜, essere seguita
da una coppia di parentesi graffe.

Esempio ErroriDiversi..........................................................................................................
é dato un vettore di 10 interi, giˆ presente in memoria. Scrivere un programma che richie-
da allÕutente lÕindice di un elemento del vettore e restituisca il logaritmo naturale dellÕele-
mento corrispondente del vettore individuato.

Indicazioni per la soluzione


Le anomalie che il programma deve individuare sono almeno tre:
1. lÕindice specificato dallÕutente • negativo;
2. lÕindice • maggiore di 10;
3. il valore dellÕelemento corrispondente del vettore • negativo.

279
Sezione 7 - Le eccezioni

Le prime due anomalie sono legate a quanto specifica lÕutente, mentre la terza dipende dal contenu-
to del vettore: in tutti e tre i casi la situazione genera errori di esecuzione, quindi deve essere pro-
dotto un programma che controlli ci˜ di cui non si conoscono gli effetti.

Codice
ErroriDiversi.cpp
1 #include <iostream>
2 #include <cmath>
3 using namespace std;
4
5 //INIZIO
6 int main ()
7 {
8
9 int indice;
10
11 //definisce dimensione
12 const int Dim = 10;
13 //definizione e inizializzazione vettore
14 double vett [Dim]={1,-8,-3,7,-6,6,13,-8,9,10};
15
16 try
17 {
18 //Chiedi e leggi indice
19 cout << "indica il numero di indice dellÕelemento richiesto ";
20 cin >> indice;
21 //controlla indice
22 if (indice < 0) throw 1; // anomalia 1 codice errore di tipo int
23 if (indice > Dim) throw 2; // anomalia 2 codice errore di tipo int
24
25 //scrivi lÕelemento individuato dallÕindice
26 cout << "\nElemento di posto " << indice << " = " << vett[indice];
27
28 //controlla se elemento • positivo
29 if (vett[indice] <= 0) throw 0.5; // anomalia 3 errore di tipo double
30
31 //se tutto ok scrive logaritmo
32 cout << "\n\nLogaritmo = " << log(vett[indice]);
33 }
34
35 catch (int codice) // gestisce codici errore di tipo int
36 {
37 switch (codice)
38 {
39 case 1: cout<< "\nErrore = indice negativo";break;
40 case 2: cout<< "\nErrore = indice fuori limite massimo";
41 }
42 }
43
44 catch (double err) // gestisce codici errore di tipo double
45 {
46 cout << "\n\nLogaritmo impossibile ";
47 }
48

280
Unitˆ didattica 16 - Gestione delle eccezioni

49 //fine programma
50 cout << "\n\nFine ";
51 system ("pause");
52 return 0;
53 }

Prove di esecuzione

In questa prima prova il dato inserito • corretto e lÕelemento del vettore • positivo, pertanto non si
rilevano anomalie e lÕesecuzione del programma va a buon fine.

LÕutente ha digitato in questo caso un indice negativo.

LÕutente ha digitato, questa volta, un indice maggiore di 10.

LÕargomento del logaritmo • negativo.

Analisi del codice


Alla riga 14 • definito il vettore a cui il programma fa riferimento. Alle righe 19 e 20 viene richiesto
lÕindice dellÕelemento del vettore di cui si vuole calcolare il logaritmo; subito dopo vengono verifica-
te le condizioni di anomalia.

281
Sezione 7 - Le eccezioni

Alla riga 22 viene individuata lÕanomalia riguardante lÕindice negativo e generato il codice del corri-
spondente gestore di eccezione. Analogamente alla riga 23: viene individuata lÕanomalia riguardan-
te lÕindice superiore a 10 e generato il corrispondente codice di gestione dellÕeccezione.
Da ultimo, prima di calcolare il logaritmo, viene verificato che lÕargomento del logaritmo sia un nu-
mero positivo; in caso contrario, alla riga 29 viene generato il nuovo codice di eccezione, pari a 0.5.
Si noti che i primi due codici delle anomalie sono di tipo intero e il terzo • di tipo double.
Alla riga 35 il primo gestore di eccezioni, introdotto dalla parola chiave catch, ha come parametro
una variabile di tipo intero, quindi raccoglie le anomalie individuate dai codici 1 e 2.
AllÕinterno del blocco di istruzioni compreso tra la riga 36 e la 42 i due valori interi del codice ven-
gono considerati e trattati singolarmente.
LÕaltro gestore di eccezioni si trova alla riga 44, ha come parametro una variabile di tipo double e
gestisce lÕanomalia di esecuzione segnalata con il codice 0.5.
...........................................................................................................................................

282
Unità didattica 16 - Gestione delle eccezioni

Esercizi
Unità didattica 16

1 Completare:
“Un’anomalia del codice consiste in una .................................................................. e rilevabile solo
durante l’esecuzione del programma”.

2 Completare:
“Le anomalie sono quasi sempre dovute a ....................................................... Solo la progettazione
e la stesura attenta possono evitarci di incorrere in anomalie del codice. Un modo per difendersi dalle
anomalie consiste nella gestione degli errori”.

3 Completare:
“La gestione degli errori è quella parte di codice che gestisce l’eventualità di un errore a ....................
..................................................................................................................................................”.

4 Completare:
“In C++ si attiva un’eccezione con la parola chiave ............................................................ si cattura
l’eccezione con la parola chiave ...................................................................................................”.

5 Completare:
“La parola chiave try è seguita da ..............................................................................................”.

6 Scrivere gli elementi della successione S = 2i con i = 1, 2, ....... n. Catturare l’eccezione i > 30. Se S è
definito intero, quali valori assume S per i > 30?

7 Dati in input due numeri x e y, calcolare pow(x,y). x e y siano definiti double. Catturare le eccezio-
ni che si generano quando non vengono rispettate le seguenti condizioni:
: se x > 0, y può essere un valore qualsiasi;
: se x = 0, y deve essere > 0;
: se x < 0, y deve essere intero.

8 Calcolare log(x), con x dato in input. Controllare che x sia > 0 e gestire l’eccezione.

9 Calcolare sqrt(x), con x dato in input. Controllare che x sia > = 0 e gestire l’eccezione.

q0 Dati in input due interi a e b, calcolare a*b. Gestire la situazione anomala che si genera quando a*b ri-
sulta maggiore del massimo consentito per gli interi.

qa Dati in input due reali a e b, calcolare a*b. Gestire la situazione anomala che si genera quando a*b ri-
sulta esterno agli intervalli previsti per i dati di tipo double.

qs Dati in input due reali a e b, calcolare il rapporto a/b. Gestire in modo distinto le due situazioni anomale
a!=0, b=0 e a=0, b=0.

qd È dato un vettore di 10 interi già definito all’interno del programma. Viene richiesto all’utente l’indice del-
l’elemento desiderato e il programma restituisce l’intero corrispondente presente nel vettore. Catturare
l’eccezione “out of range”.

283
Appendice
Riepilogo degli operatori
A
z = x - y ;
significa
z = (x - y) ;
perchŽ Ð ha precedenza rispetto a =.
Gli operatori di pref“sso unario e quelli di assegnazione associano da destra a sinistra.
Tutti gli altri operatori associano da sinistra a destra. Per esempio:
x - y - z
significa
(x - y) - z
perchŽ lÕoperatore Ð associa da sinistra a destra, mentre
x = y = z
significa
x = (y = z)
perchŽ lÕoperatore = associa da destra a sinistra.

OPERATORE DESCRIZIONE OPERATORE DESCRIZIONE


:: Risoluzione della classe / Divisione o divisione tra interi
. Accesso a membro % Resto intero della divisione
-> Deriferimento e accesso a membro + Addizione
[] Vettore o indice di array - Sottrazione
() Chiamata di funzione << Output (o shift bitwise)
++ Incremento >> Input (o shift bitwise)
Ñ Decremento < Minore di
! NOT booleano <= Minore di o uguale a
~ NOT bitwise > Maggiore di
+ (unario) Positivo >= Maggiore di o uguale a
- (unario) Negativo == Uguale a
* (unario) Deriferimento di puntatore != Diverso da
& (unario) Indirizzo di variabile & AND di cortocircuito
new Allocazione dellÕheap ^ XOR di cortocircuito
delete Rilascio dellÕheap | OR di cortocircuito
sizeof Dimensione di una variabile && AND booleano
o tipo || OR booleano
(tipo) Cast ? : Alternativa
.* Accesso al puntatore a membro = Assegnazione
->* Deriferimento e accesso += -= *= /= Operatori combinati con
a puntatore a membro %= &= |= ^= assegnazione
* Moltiplicazione , Sequenza di espressioni

285
Appendice
Sequenze di caratteri escape
B
Queste sequenze di escape possono essere utilizzate nelle stringhe (per esempio Ò\nÓ) e nei tipi char
(per esempio Ô\ÔÔ ).

SEQUENZA DI ESCAPE DESCRIZIONE


\n Nuova riga
\r Invio a capo
\t Tabulazione
\v Tabulazione verticale
\b Backspace
\f Nuova pagina (formfeed)
\a Avviso
\\ Barra retroversa
\Ó Virgolette doppie
\Õ Virgolette singole
\? Punto interrogativo
\xh1h2 Carattere specificato in forma esadecimale
\oo1o2 Carattere specificato in forma ottale

286
Indice analitico

A compilazione 36
errori di 37
funzione 144
membro 216
accesso complemento a due 25-28
ai file 260
ai membri di una classe 202
concatenazione di stringhe 197 G
confronto lessicografico gestione delle eccezioni 277
algoritmo 92, 94-96 di stringhe 201
allocare 42 gestore dellÕeccezione 279
conversione
analisi del problema 91
anomalia 276
dalla base 2 alla base 10 12
dalla base 10 alla base 2 12 I
append 269 costanti 49, 96 immissione (input) 95
archivio 253-254 letterali 99 incapsulazione 211
argomenti 96 costruttore 204, 224 indice di un vettore 171
CSV (Comma Separated inizializzazione
B Values) 260 di una variabile 98
INIZIO (di un algoritmo) 108
base di un sistema
di numerazione 9 D input 95, 99, 257
bit 11 dati 96, 254 -255 istanza di una classe 218
di input 95 istruzioni 93, 96
blocco try 277
di output 95 catch 277
byte 22
membro 216 cin 55
booleani 42, 48
definizione circolare 161 class 218
C dichiarazione di variabili 40
dimensione di un vettore 171
di assegnamento 97
di lettura (di input) 99
campi 255 distruttore 225 di scrittura (di output) 99
caratteri di escape 62 di ripetizione,
caratteristica 29
case sensitive 42 E o iterative 128-138
for 136
casting 48 eccezioni 277-282 if 113-114
esplicito 46 cattura delle 277 while 128-130
cicli 128 editor di testi 35
classe 215 emissione (output) 95
entry point 145
L
base 240 librerie 36
derivata 240 eof() 266
ereditarietˆ 213-214 linker 36
di problemi 94 lunghezza di una stringa 195-197
metodi 216
codice F M
ASCII 22-24 file 253-254
del calcolatore 22 dÕintestazione 36 mantissa 29
di gestione degli errori 276 eseguibile 36 matrici 180-187
macchina 22 sorgente 36 membri di una classe 216
numerico decimale 21 FINE (termine MENTRE ... FINE
UNICODE 24, 45 di un algoritmo) 108 MENTRE 108
codifica 21 flag di modalitˆ di apertura metodo della virgola mobile 29

N
comando 96 di un file 268
commenti 38 forma normalizzata
compilatore 36 di un numero 29 notazione puntata 202

287
Indice analitico

O programma sorgente 36
pseudocodifica 97, 101, 107-108
T
operatore throw 277
di negazione (NOT) 84
di risoluzione del campo
R tipo di dato
bool 48
di azione (::) 230 record 255 char 44-45
logico di valutazione redirezione 44 decimale 42
completa 82 repertorio del calcolatore 22 double 46
operatori ricorsione 161 enumerativo 169
aritmetici composti 73-78 RIPETI ... FINCHƒ 108 float 47
binari 67 ripetizione in virgola mobile 42, 46-47
logici 80-85 postcondizionale 106 int 43
post-fissi 76-77 precondizionale 104 intero 42-44
pre-fissi 76-77 risolvere un problema 91 long 44
relazionali (di confronto) ordinale 169
78-80, 200 S short 44
unari 75-76 salvataggio a byte 260 vettore 171
operazione di lettura SCEGLI ... CASO ... FINE try 277
(di input) 257 SCEGLI 108
operazione di scrittura
(di output) 257
SE ... ALLORA ... U
ALTRIMENTI ... FINE UNICODE 44-45
output 95, 99, 257 SE 108
overloading 234 unitˆ
selettore (variabile) 121
di standard input 258
set di caratteri
P del calcolatore 22
di standard output 258
parola 25
parser 36
setw 59
sistema di numerazione V
passaggio dei parametri binario 11 variabili 40
per valore 153-157 sopraclasse 214 ambiti 148
PER ... DA ... A ... PASSO ... sottoclasse 214 di controllo (selettori) 121
FINE PER 108 stringa 39, 193-206 di lavoro 95
polimorfismo 212, 237 struct 202 globali 149,151
private 216 strutture di ripetizione 104-107 locali 151
procedura 145 substr 198 vettori 172-178

288