dispensa - Università degli Studi di Roma "Tor Vergata"

Vittoria de Nitto Personè
Appunti per il corso
Fondamenti di informatica 1
A.A. 2000 / '01
Facoltà di Ingegneria - Università degli studi di Roma «Tor Vergata»
Premessa
Queste pagine raccolgono degli appunti che ho scritto per il corso di Fondamenti di
Informatica 1. Il materiale è suddiviso per ideali lezioni e alla fine troverete esercizi
consigliati per ogni lezione. Non è detto ci sarà corrispondenza precisa tra ciò che viene
raccolto qui come unica lezione e ciò che si riuscirà a realizzare in classe in un'unica lezione.
Suggerisco alla fine di ogni settimana di provare ad eseguire gli esercizi relativi alle lezioni
svolte in quella settimana.
Il corso si svolge, secondo il nuovo ordinamento degli studi, in un trimestre. Proprio per
questo ho deciso di divulgare del materiale che negli anni precedenti non ho mai divulgato per
la sua naturale frammentarietà. Da quest'anno accademico i tempi sono rapidissimi e tutto ciò
che può agevolare l'apprendimento "rapido" delle conoscenze va utilizzato.
Il rischio, che vorrei scongiurare in ogni modo, è che si possa pensare questo materiale come
sostitutivo del libro (o libri) di testo o sostitutivo della presenza attenta e attiva in classe.
Sarebbe un grave errore che porterebbe senza dubbio ad un "rapido" fallimento.
Mi scuso per l'incompletezza, errori, imprecisioni o quant'altro troverete in questi appunti, e
ringrazio tutti coloro che vorranno segnalarmeli o suggerirmi qualunque miglioramento.
3 settembre 2001
2
Indice
Lezione1:
Codifica dei dati ............................................................................................................................. 5
Anatomia di un computer ............................................................................................................... 6
Dal codice sorgente all'eseguibile .................................................................................................. 7
Lezione2: Il primo programma in C++ ........................................................................................ 8
Lezione3:
Algoritmo ....................................................................................................................................... 11
Macchina C++............................................................................................................................... 12
Linguaggio di programmazione .................................................................................................... 13
Lezione 4:
Lessico del C++, Sintassi e semantica: dichiarazioni .................................................................
Tipi fondamentali, Dichiarazione di variabile .............................................................................
Dichiarazione di costante .............................................................................................................
Comandi: assegnamento ..............................................................................................................
14
15
16
18
Lezione 5: Input / Output ............................................................................................................. 19
Lezione 6:
Comandi ....................................................................................................................................... 22
Controllo del flusso ....................................................................................................................... 23
Lezione 7:
Rappresentazione dei numeri interi .............................................................................................. 31
Conversione di base ...................................................................................................................... 32
Numeri relativi: rappresentazione in modulo e segno .................................................................. 33
Lezione 8:
Rappresentazione dei numeri reali, rappresentazione in virgola mobile ..................................... 34
Errore di arrotondamento ............................................................................................................. 35
Lezione 9:
Rappresentazione dei numeri reali................................................................................................ 38
Conversione di base ...................................................................................................................... 40
3 settembre 2001
3
Lezione 10: Unità funzionali, funzioni senza parametri ............................................................... 41
Lezione 11: Unità funzionali con parametri ................................................................................. 46
Lezione 12:
Definizione nuovi tipi ..................................................................................................................... 49
Tipi strutturati ................................................................................................................................ 50
Lezione 13:
Definizione nuovi tipi .................................................................................................................... 53
Le classi.......................................................................................................................................... 57
Esercizi
Lezione 1: introduzione, architettura, compilazione......................................................................
Lezione 2: primo programma.........................................................................................................
Lezione 4: tipi predefiniti, assegnamento .....................................................................................
Lezione 5: input / output, ambiente e stato ...................................................................................
Lezione 6: espressioni logiche, controllo del flusso .....................................................................
Lezione 7: rappresentazione posizionale, conversione di base .....................................................
Lezione 8: rappresentazione in virgola mobile .............................................................................
Lezione 9: caratteristiche intervallo, conversione di base ............................................................
Lezione 10: funzioni .....................................................................................................................
Lezione 11: passaggio per riferimento .........................................................................................
Lezione 12: array ..........................................................................................................................
Lezione 13: classi .........................................................................................................................
3 settembre 2001
4
61
61
62
62
63
65
65
66
67
68
69
70
Lezione 1
Codifica dei dati
• l'informazione viene codificata in numeri (binari)
numeri naturali
numeri reali
dati non numerici: caratteri
a→0
b→1
c→2
…
es. bac → 102
onde sonore
immagini
esercizio di codifica:
codificare i giorni della settimana {lunedì, martedì, mercoledì, giovedì, venerdì,
sabato, domenica} in forma binaria
lunedì → 0
martedì → 1
mercoledì → 2
giovedì → 3
venerdì → 4
sabato → 5
domenica → 6
forma binaria
→ 000
→ 001
→ 010
→ 011
→ 100
→ 101
→ 110
arriviamo alla stessa codifica "ragionando" su insiemi:
regola
dato un insieme, questo viene suddiviso in due sottoinsiemi di uguale cardinalità
(o al più con differenza pari a 1 se l'insieme di partenza ha cardinalità dispari)
associando al primo sottoinsieme codifica 0 e al secondo sottoinsieme codifica 1
3 settembre 2001
5
insieme di partenza:
{lunedì, martedì, mercoledì, giovedì, venerdì, sabato, domenica}
{lunedì, martedì, mercoledì, giovedì}
{venerdì, sabato, domenica}
{lunedì, martedì}
{mercoledì, giovedì}
{venerdì, sabato}
{domenica}
0
1
lunedì
000
martedì
mercoledì
001
010
giovedì
011
venerdì
100
sabato
domenica
101
110
00
01
10
11
Anatomia di un computer
• CPU (central process unit) unità centrale di elaborazione
• Memoria
CPU:
esegue il programma, svolge funzioni di calcolo e di trasferimento dati
- individua ed esegue le istruzioni del programma
- effettua le operazioni aritmetiche (somme, sottrazioni…)
- reperisce dati dalla memoria o da apparecchiature periferiche e li rimanda indietro
(tutti i dati devono transitare dalla CPU quando vengono spostati da una posizione
all'altra, salvo eccezioni)
Memoria:
memoria primaria: veloce ma costosa
RAM (random access memory)
ROM (read only memory)
3 settembre 2001
6
ROM: contiene quei particolari programmi che devono essere sempre presenti (es. il
codice necessario per avviare il computer)
RAM: contiene programmi e dati in fase di modifica
svantaggi: perde tutti i dati quando si spegne il computer
memoria secondaria: meno costosa e i dati perdurano in assenza di elettricità
disco rigido, dispositivi di memoria esterni: floppy, CD-ROM, nastri
Per interagire con l'utente un computer ha bisogno di altri dispositivi esterni:
tastiera, schermo, altoparlante, stampanti…
CPU, memoria e dispositivi esterni sono interconnessi mediante un bus (insieme
di linee elettriche)
i dati transitano lungo il bus dalla memoria e dai dispositivi periferici verso la
CPU e vicecersa
Dal codice sorgente all'eseguibile
(sintassi, semantica, errori sintattici e errori logici)
3 settembre 2001
7
Lezione 2
Il primo programma in C++
(prerequisiti: concetti minimi di architettura, compilazione, esecuzione)
#include <iostream.h>
class stringa
{ char st[100];
public:
stringa ()
{ char saluto[100]="buon giorno!";
st=saluto;}
void stampa ()
{cout <<st <<'\n';}
};
main()
{
stringa x;
x.stampa();
}
• il programma visualizza un semplice saluto: buon giorno!
esaminiamo la struttura del programma
• il programma è composto da tre parti
• la prima parte è la riga :
#include <iostream.h>
questa "aggiunge" al nostro programma la libreria di nome iostream.h
def
una libreria è un insieme di funzioni scritte nel linguaggio, già compilate e
tradotte in formato eseguibile;
def
una funzione è una "raccolta" (sequenza) di istruzioni del linguaggio
ogni funzione ha un nome a cui segue tra {} la sequenza di istruzioni che
compongono quella funzione (corpo della funzione); ogni istruzione è terminata
da ";"
3 settembre 2001
8
per eseguire la funzione si utilizza il suo nome
la libreria iostream.h raccoglie tutte le funzioni necessarie per l'ingresso/uscita
• la seconda parte è composta dalle righe:
class stringa
{ char st[100];
public:
stringa ()
{ char saluto[100]="buon giorno!";
st=saluto;}
void stampa ()
{cout <<st <<'\n';}
};
è la definizione di una classe
def
una classe serve per "creare" oggetti e definisce le funzioni che creano e
"manipolano" questi oggetti (metodi o primitive)
def
un oggetto è un elemento che un programma può manipolare
nell'esempio la classe definisce oggetti di "tipo" stringa:
nella prima parte della classe la riga char st[100]; definisce come l'oggetto è
"realizzato internamente": un oggetto stringa è realizzato mediante una sequenza
di 100 caratteri e questa sequenza si chiama st
la seconda parte, preceduta dalla parola "public:", definisce le due funzioni che
possono manipolare l'oggetto stringa: le funzioni sono due e si chiamano stringa
(costruttore) e stampa.
In questo primo es non siamo interessati alle classi come "fabbrica di oggetti",
ma alla scrittura di un semplice messaggio di saluto; quindi il costruttore stringa
crea oggetti uguali a buon giorno!
3 settembre 2001
9
e la funzione stampa, mediante l'istruzione cout <<st <<'\n'; , invia st al
dispositivo di output standard cioè lo schermo (vedremo che esistono molti altri
posti ai quali un programma può inviare st, cioè i dati in uscita).
cout è una delle funzioni di uscita della libreria iostream.
• la terza parte è composta dalle righe:
main()
{
stringa x;
x.stampa();
}
è la definizione di una funzione "speciale" di nome main. Una funzione main
deve sempre essere presente in un programma C++ e l'esecuzione del
programma è l'esecuzione della funzione main.
Nel nostro es il corpo della funzione main è formato da due istruzioni:
la prima stringa x; "chiama" (attiva) il costruttore stringa (che viene quindi
eseguito) che crea un oggetto di nome x e di tipo stringa
la seconda x.stampa(); chiama la primitiva stampa sull'oggetto x
OSSERVAZIONI CONCLUSIVE
• in un programma C++ deve essere presente una funzione main che "guida"
l'esecuzione;
• il meccanismo delle classi è centrale per organizzare i programmi, definendo
gli "oggetti" su cui il programma opera e le relative funzioni primitive che
manipolano gli oggetti.
3 settembre 2001
10
Lezione 3
Algoritmo
analisi del problema e identificazione di una soluzione
esempio: determinare il maggiore di n numeri interi
scomposizione in sottoproblemi
Pr1: determinare il maggiore di n numeri interi
P1. trovare il maggiore fra i primi 2 numeri;
P2. trovare il maggiore fra il terzo numero e il risultato del sottoproblema
precedente;
P3. trovare il maggiore fra il quarto numero e il risultato del sottoproblema
precedente;
…
Pn-1. trovare il maggiore fra l'ultimo numero e il risultato del sottoproblema
precedente.
Se l'esecutore sa risolvere il problema di trovare il maggiore fra due numeri,
non bisogna scomporre ulteriormente, il problema di partenza è risolto
possiamo esprimere la soluzione in una forma più sintetica:
Pr1: determinare il maggiore di n numeri interi
P1. trovare il maggiore fra i primi due numeri;
predicato
azione
P2. finchè ci sono numeri da verificare ripetere il passo P3;
P3. trovare il maggiore fra il nuovo numero da esaminare e il più grande trovato
in precedenza.
P2. è una struttura iterativa
Se l'esecutore non sa risolvere il problema di trovare il maggiore fra due numeri,
ma sa valutare la differenza fra due interi e valutare il segno di un numero, il
problema di trovare il maggiore fra due numeri può essere risolto come segue:
Pr2: determinare il maggiore fra due numeri interi x e y
P1. determinare la differenza δ fra x e y (δ ← x-y)
assegnamento
3 settembre 2001
11
predicato
P2. valutare se δ >0
• in caso affermativo la soluzione è x
• in caso negativo la soluzione è y.
δ è una variabile
strutture condizionali
ora l'algoritmo è definito ad un livello di dettaglio sufficiente per il nostro
esecutore
nell'individuazione di procedure risolutive, i seguenti due principi
• scomposizione in sottoproblemi
• individuazione di problemi noti
sono generalmente adottati.
Un algoritmo deve possedere 5 caratteristiche essenziali:
• effettività
• finitezza
• definitezza
• ingresso
• uscita
Macchina C++
• MEMORIA:
insieme di contenitori di "oggetti" (locazioni di memoria)
(rappresentazioni di oggetti)
quali oggetti?
quali rappresentazioni?
(nome, oggetto)
oggetti: valori appartenenti a 4 classi:
Z interi relativi
R numeri reali
C caratteri
B valori logici
3 settembre 2001
12
• ESECUTORE:
entità in grado di eseguire "azioni":
a. calcolare nuovi valori a partire da valori dati
b. modificare il contenuto di locazioni
c. leggere / scrivere (da / verso l'esterno)
• DISPOSITIVO DI COMUNICAZIONE:
utilizzato per ricevere dall'esterno "oggetti" da memorizzare e per
restituire risultati
il linguaggio che useremo per interagire con la Macchina C++ (con l'esecutore)
ci deve consentire di specificare:
1. quali oggetti sono presenti in M e con che nome (dichiarazioni)
2. quali azioni di tipo a., b., c. compiere (comandi)
3. in che ordine eseguire azioni di tipo a., b., c. (controllo del flusso)
Linguaggio (di programmazione):
lessico, sintassi e semantica
• lessico: elementi costitutivi del linguaggio (dizionario per la lingua italiana)
• sintassi: regole per scrivere "frasi" corrette come struttura (il gatto mangia il il)
errore sintattico
• semantica: regole per scrivere "frasi" che hanno "senso" (il latte beve il gatto)
errore logico
3 settembre 2001
13
Lezione 4
Lessico del C ++
cifra:
0, 1, … , 9
carattere: λ, a, b, …, A, B, …, Z
operatori e segni di interpunzione: !, =, %, (, ), …, +, -, ; , , , …
parole chiave: sequenze di simboli il cui significato è stabilito dal linguaggio
Sintassi e semantica
Dichiarazioni
struttura generale della funzione main:
main ()
{comando;
comando;
…
comando;}
parola chiave
; terminatore di comando
{ } blocco
• non è obbligatorio, MA è buona norma mettere i "comandi-dichiarazione" in
cima
• lo scopo delle dichiarazioni è: dichiarare esplicitamente quali sono i dati su cui
il programma opererà, in particolare il dominio (insieme di valori assumibili dai
dati) e le operazioni elementari
def
un tipo di dato è definito da un dominio e un insieme di operazioni elementari
lecite su quel dominio
si distingue tra:
tipi fondamentali → forniti dal linguaggio
tipi derivati → definiti dal programmatore
3 settembre 2001
14
Tipi fondamentali
• interi
parola chiave
int
dominio
intervallo finito
sull'insieme degli interi:
[-2N-1, 2N-1-1]
N=16, 32
operazioni elementari
+, -, *, /, %
dominio
sottoinsieme finito dei
numeri razionali
dipende
dall'implementazione
operazioni elementari
+, -, *, /
dominio
insieme dei caratteri di
stampa (spesso codifica
ASCII, un carattere 8 bit)
operazioni elementari
nulla (non esist. oper. sul
dominio)
si usano operatori di
confronto
• reali
parola chiave
double
• caratteri
parola chiave
char
i caratteri si scrivono tra ' '
Dichiarazione di variabile
SINTASSI
2 forme:
tipo nome; o tipo nome1, nome2, …;
tipo nome=valore; o tipo nome1=valore1, nome2=valore2, …;
le due forme possono anche "mischiarsi": tipo nome1, nome2=valore2;
dove:
3 settembre 2001
15
tipo → int, double, char
nome → sequenza di caratteri e cifre che inizia con carattere
valore → "espressione" costante dello stesso tipo
SEMANTICA
• crea una associazione (nome, cella di memoria)
• l'associazione nasce all'istante di dichiarazione e muore alla chiusura del
blocco che la contiene
esempi:
main ()
{int a;
…
}
varianti: int a=5;
double x1, x2=0.142;
char iniziale, ch='z';
Dichiarazione di costante
SINTASSI
const tipo nome=valore;
dove:
o const tipo nome1=valore1, nome2=valore2, …;
tipo → int, double, char
nome → sequenza di caratteri e cifre che inizia con carattere
valore → "espressione" costante dello stesso tipo
SEMANTICA
• crea una associazione permanente (nome, valore costante)
• l'associazione nasce all'istante di dichiarazione e muore alla chiusura del
blocco che la contiene
il compilatore controlla che gli oggetti dichiarati costanti non vengano "mai" modificati
3 settembre 2001
16
esempi:
main ()
{const int a=5;
…
}
varianti: const int kilo=1024;
const double pigreco=3.14;
const char c='c';
Lo scopo delle dichiarazioni è:
- predisporre la memoria della macchina per le operazioni che verranno eseguite
dal programma (dati)
- verificare la correttezza staticamente (CONTROLLO SUI TIPI)
esempio:
int a,b;
char c;
… l'espressione a+c
è scorretta errore semantico (o logico)
si verifica che non ci siano espressioni "senza senso", questo può essere scoperto
grazie alla dichiarazione dei tipi (tipi incompatibili)
esempio (altri errori semantici):
int a,b;
double a;
const int z=0;
int z, r;
regola semantica delle dichiarazioni:
ogni nome può comparire una sola volta all'interno delle dichiarazioni di un
blocco
ATT.NE
non "usare" nomi di variabili non inizializzati;
il C non segnala errore ma interpreta l'informazione che trova a seconda del tipo
della variabile.
Come "si danno" valori a variabili??
3 settembre 2001
17
Comandi
Assegnamento
è il comando basilare, consente di definire o modificare il valore di una variabile
(già usato nelle dich.)
SINTASSI
nome=espressione;
SEMANTICA
• valuta espressione
• memorizza il valore di espressione nella cella di memoria associata a nome
quindi regole semantiche:
1. nome deve essere stato dichiarato come variabile;
2. nome e espressione devono essere dello stesso tipo
esempio (errori semantici):
main ()
{const int a=15;
int b;
a=1000;
viola la regola 1
b='B';
viola la regola 2 (consistenza tra tipi)
}
3 settembre 2001
18
Lezione 5
Input / Output
(lo abbiamo già usato nel primo programma)
la comunicazione in ingresso e in uscita avviene tramite dei "flussi" (stream) che
possiamo immaginare come sequenze di caratteri con accesso sequenziale
cin
cout
flusso di ingresso
flusso di uscita
SINTASSI (ingresso)
cin >> variabile;
SEMANTICA
• preleva dal flusso di ingresso la sequenza di caratteri a partire dalla "posizione
corrente" (si ferma sul primo carattere non previsto da quel tipo)
• verifica la consistenza tra tipi (errore a tempo di esecuzione)
• assegna a variabile il valore della sequenza
SINTASSI (uscita)
cout << espressione;
SEMANTICA
• valuta espressione
• trasferisce il valore sul flusso in uscita a partire dalla posizione corrente
cin e cout fanno parte della libreria di I/O iostream.h che deve essere "aggiunta"
al programma
esempio:
3 settembre 2001
19
#include <iostream.h>
main ()
{int a, b;
double media;
cout <<"Questo programma calcola la media di due valori interi dati in
ingresso;\n";
cout <<"scrivi i due interi:\n";
cin >>a >>b;
media=a+b;
media=media/2;
cout <<"la loro media e':\n" <<media <<'\n';
}
NB
ruolo di media nell'ultimo assegnamento, l-value/r-value
utilizziamo questo programma per introdurre due importanti concetti
Le dichiarazioni definiscono l' ambiente del programma (o meglio di quel
blocco)
def
L' ambiente è l'insieme di coppie (nome, oggetto) definite dalle dichiarazioni
A={(nome1, oggetto1), (nome2, oggetto2), …}
dove
nomei è il nome usato nella dichiarazione
oggettoi è valore se nomei è const
è cella di memoria se nomei è variabile
altre cose che vedremo
informalmente è l'insieme di tutti i nomi che è lecito usare nella parte comandi
che segue le dichiarazioni
l'uso di qualunque nome estraneo all'ambiente genera errore
l'ambiente del programma d'esempio è:
A={(a, c. di m.), (b, c. di m.), (media, c. di m.)}
- unici nomi leciti, ambiente attivo
3 settembre 2001
20
l'ambiente si definisce "staticamente";
"dinamicamente" cioe' a tempo di esecuzione si definisce lo stato delle variabili
def
lo stato di un programma ad un certo istante della sua esecuzione è l'insieme dei
valori assunti dalle variabili dell'ambiente a quell'istante di esecuzione
3 settembre 2001
21
Lezione 6
Comandi
mentre le dichiarazioni definiscono i dati del problema, i comandi servono a
specificare l'algoritmo risolutivo del problema in modo che sia eseguibile dalla
macchina C++
per quanto visto finora siamo in grado di scrivere programmi costituiti da una
lista di comandi da eseguire in sequenza:
1. I passi sono eseguiti uno alla volta
2. Ogni passo è eseguito esattamente una volta: nessuno è ripetuto
nessuno è omesso
3. L'ordine in cui i passi sono eseguiti è esattamente quello in cui sono
scritti
4. La terminazione dell'ultimo passo implica la terminazione
dell'algoritmo
è estremamente poco flessibile, il suo corso di esecuzione è fisso e non può
essere modificato dalle circostanze
questo è insufficiente, ci servono:
- costrutti per "scegliere" tra blocchi diversi di istruzioni: SELEZIONE
la "selezione" permette ad un processore di seguire diversi cammini
attraverso un algoritmo in accordo alle circostanze;
esempio: confrontare x, y e z per determinare il maggiore supponendo che il
processore può confrontare solo 2 alla volta
se (x>y)
allora (scegli tra x e z)1
altrimenti (scegli tra y e z)2
1
se (x>z)
allora scegli x
altrimenti scegli z
2
se (y>z)
allora scegli y
altrimenti scegli z
3 settembre 2001
22
• senza la selezione sarebbe impossibile scrivere algoritmi di alcuna significativa
utilità pratica
- costrutti per "ripetere" blocchi di istruzioni: ITERAZIONE
per esprimere algoritmi la cui lunghezza varia in accordo alle
circostanze; c'è la necessità di poter ripetere certi passi in un algoritmo
un numero arbitrario di volte
esempio: calcolare il primo numero primo maggiore di un numero dato
ottieni il numero di partenza
aggiungi 1
controlla se il numero è primo
se il numero è primo
allora scrivilo
altrimenti aggiungi 1
controlla se il numero è primo
se il numero è primo
allora scrivilo
altrimenti aggiungi 1
controlla se il numero è primo
…
• la potenza dell'iterazione è che consente ad un algoritmo di lunghezza finita di
descrivere un processo di durata indeterminata
Controllo del flusso
comandi iterativi:
SINTASSI
while (espressione) comando;
espressione deve essere una espressione logica
SEMANTICA
1. valuta espressione
3 settembre 2001
23
2. se espressione è vera - esegui comando
- torna a 1
se espressione è falsa termina
come si crea una espressione logica??
operatori di confronto
operatori logici
molti linguaggi hanno tra i tipi predefiniti i booleani (valori logici {vero, falso})
in C++ non esistono MA si creano valori logici mediante questi operatori
operatori di confronto: ==, !=, >, >=, <, <=
esempi: a==b ecc. (consistenza tra tipi)
operatori logici: &&, ||, !
esempi: a==b && a==0 ecc.
il comando del while spesso è un comando-composto:
SINTASSI
{comando;
comando;
…
comando;}
SEMANTICA
considerare la sequenza comandi tra {} una unità indivisibile, cioè un blocco
c'è un altro costrutto per l'iterazione, molto simile al while:
SINTASSI
do comando while (espressione);
3 settembre 2001
24
espressione deve essere una espressione logica
SEMANTICA
1. esegui comando
2. valuta espressione:
se è vera torna a 1
se è falsa termina
NB
differenza con il while: il comando viene eseguito sempre almeno una volta
esempio: se volessimo scrivere un programma per calcolare la media tra 5 interi
#include <iostream.h>
main ()
{int a, b, c, d, e;
double media;
cout <<"Questo programma calcola la media di cinque valori interi dati in
ingresso;\n";
cout <<"scrivi i cinque interi:\n";
cin >>a >>b >>c >>d >>e;
media=a+b+c+d+e;
media=media/5;
cout <<"la loro media e':\n" <<media <<'\n';
}
e se poi volessimo cambiare il numero di interi?? Dovremmo scriverne un altro!!
3 settembre 2001
25
#include <iostream.h>
main ()
{int a, N, i;
double media=0;
cout <<"Questo programma calcola la media di N valori interi dati in ingresso;\n";
cout <<"Quale e' il valore di N?\n";
cin >>N;
i=N;
1
while (i>0)
2
{cout <<"scrivi il prossimo intero:\n";
cin >>a;
media=media+a;
i=i-1;}
3
media=media/N;
cout <<"la loro media e':\n" <<media <<'\n';
}
(robustezza: e se N fosse 0???)
in questo ciclo iterativo
1
2
3
è la inizializzazione
è la condizione di terminazione
è il decremento (o incremento)
c'è un costrutto che consente di esprimere in maniera compatta 1, 2 e3
SINTASSI
for (assegnamento; condizione; espressione)
comando;
• la variabile utilizzata viene detta variabile di controllo;
(il campo d’azione della variabile di controllo si estende a tutto il blocco che contiene il for; è
come se fosse definita immediatamente prima del for)
• condizione deve essere una espressione logica e rappresenta la condizione che
controlla l’esecuzione del ciclo
SEMANTICA
for (nome=exp; exp1; exp2) comandoX;
3 settembre 2001
26
1.
inizializza la variab. di controllo tramite nome=exp;
2.
valuta exp1: se è vera si esegue comandoX;
se è falsa il for termina;
3.
valuta exp2, torna a 2.
#include <iostream.h>
main ()
{int a, N, i;
double media=0;
cout <<"Questo programma calcola la media di N valori interi dati in ingresso;\n";
cout <<"Quale e' il valore di N?\n";
cin >>N;
for (i=N; i>0; i=i-1;)
{cout <<"scrivi il prossimo intero:\n";
cin >>a;
media=media+a;
}
media=media/N;
cout <<"la loro media e':\n" <<media <<'\n';
}
Comandi selettivi:
SINTASSI
a) if (espressione) comando;
b) if (espressione) comando1;
else comando2;
espressione deve essere una espressione logica
SEMANTICA
a)
1. valuta espressione
2. se espressione è vera - esegui comando
- termina
3 settembre 2001
27
se espressione è falsa termina
b)
1. valuta espressione
2. se espressione è vera - esegui comando1
se espressione è falsa - esegui comando2
3. termina
esempio: determinare il maggiore tra due interi x e y
int x, y, max;
… input
if (x>y) max=x;
else max=y;
ATTENZIONE
il com. condizionale può essere ambiguo
if (e1) if (e2) comandox else comandoy
che significa?? se e1 è falsa: termina o esegue comandoy?? cioè
b)
if (e1) {if (e2) comandox}
else comandoy
oppure
a)
if (e1) {if (e2) comandox
else comandoy}
se non si usano le parentesi il compilatore C++ "sceglie" la semantica a), cioè:
dalla fine del comando, andando a ritroso, ogni else viene legato al primo if che
si incontra
Consideriamo un caso in cui molti if risultano annidati, condizionati al valore di
una stessa variabile
esempio:
frammento di un programma che legge numeri in caratteri romani e ne calcola il
valore, stampandolo in notazione decimale (esercizio: completare il programma)
3 settembre 2001
28
.
.
.
if (ch=='I') n=1;
else if (ch=='V') n=5;
else if (ch=='X') n=10;
else if (ch=='L') n=50;
else .
.
.
.
il C++ dispone di un comando più compatto ed elegante per queste scelte
multiple:
SINTASSI
switch (espressione)
{ case costante1: comando1; break;
case costante2: comando2; break;
case costante3: case costante4: case costante5: comando3; break;
…
default: comando4; break;}
• l’espressione deve essere di tipo discreto, e le costanti dello stesso tipo (i tipi
discreti che per ora conosciamo sono int e char);
SEMANTICA
1.
2.
valuta espressione;
se espressione=costante1 esegui comando1 termina
se espressione=costante2 esegui comando2 termina
se espressione=costante3 or espressione=costante4 or
espressione=costante5 esegui comando3 termina
…
altrimenti esegui comando4 termina
3 settembre 2001
29
esempio: la cascata di if-else dell’es. precedente diventa
switch (ch)
{ case 'I': n=1; break;
case 'V': n=5; break;
case 'X': n=10; break;
case 'L': n=50; break;
.
.
.
.
default: cout <<"il carattere non appartiene alla notazione romana"; break;}
3 settembre 2001
30
Lezione 7
Rappresentazione dei numeri interi
343
sequenza di simboli che noi interpretiamo come l'intero 343 secondo la
rappresentazione posizionale:
ogni simbolo ha un significato (valore) a seconda della posizione che occupa
più in generale:
B
base
{0, 1, 2, …, B-1}=SB alfabeto di simboli
(cp-1 … c1c0)B
rappresentazione in base B, con ci∈SB ∀ i
cp-1Bp-1 + … c1B1 + c0B0 valore rappresentato
esempio:
B=10, S B={0, 1, …, 9}
343
(c2c1c0)10 → 3*102 + 4*101 + 3*100 = 3*100 + 4*10 + 3
con B simboli su p "posti" posso rappresentare Bp diverse configurazioni
esempio: B=10 simboli, p=3 posti 103=1000 diverse configurazioni, gli interi
da 0 a 999
se le diverse configurazioni sono interi a partire da 0 il max è Bp-1
B=2, max= 23-1=7 (111)2
B=5, max= 53-1=124 (444)5
B=10, max= 103-1=999 (999)10
la stessa sequenza di simboli, cambiando base, può avere significati diversi
esempio: 101
B=2, (101)2 = 1*22 + 1*20 = 5 (valore "in base 10")
B=5, (101)5 = 1*52 + 1*50 = 26 (valore "in base 10")
B=10, (101)10 = 1*102 + 1*100 = 101 (valore "in base 10")
3 settembre 2001
31
Conversione di base
(da 10 a B)
dati del problema: N valore in base 10
risultato: vogliamo determinare
(cp-1 … c1c0)B, con ci ∈ SB={0, 1, … B-1} ∀ i
tale che cp-1Bp-1 + … c1B + c0 = N
- notiamo che
ci<B ∀ i perciò (cp-1Bp-1 + … c1B) > B e c0<B
- se calcoliamo la divisione intera N/B otteniamo
quoziente = c p-1Bp-2 + … c2B + c1
resto = c0
- riapplichiamo a quoziente e otteniamo c1 ecc.
algoritmo
regola dei resti
(metalinguaggio C++)
B=2
i=0
Ni=N
while (Ni>0)
{ci=Ni% 2
i=i+1
Ni=Ni-1 /2}
esempio:
N=13
i
0
1
2
3
4
Ni
13
6
3
1
0
ci
1
0
1
1
(1101)2 = (13)10
3 settembre 2001
32
Numeri relativi
Rappresentazione in modulo e segno
+ 343
- 343
in base 2 utilizziamo una cifra per il segno: convenzione →
0 rappresenta +
1 rappresenta -
esempio: +13 → 01101
- 13 → 11101
se utilizziamo p cifre: 1 per il segno e p-1 per il modulo
il max è + Bp-1-1
il min è - (Bp-1-1)
con B=2 e p=16 l'intervallo rappresentato è: [-(215-1) , +215-1]
ma avremmo una doppia rappresentazione per lo zero: +0…0 -0…0
anche per questo nei calcolatori attuali la rappresentazione adottata è diversa
(complemento alla base); valori tipici per gli interi sono p=16, 32,
esempio: p=16 rappresentiamo 216 diverse configurazioni; sono gli interi
dell'intervallo [-215, +215-1] (sottoinsieme finito)
15
-2
-1 0
15
+ 2 -1
Z
analogamente per p=32
3 settembre 2001
33
Lezione 8
Rappresentazione dei numeri reali
la rappresentazione posizionale può essere usata per rappresentare i numeri reali:
B
base
{0, 1, 2, …, B-1}=SB alfabeto di simboli
(cp-1 … c1c0,c-1c-2c-3…)B rappresentazione in base B, con ci∈SB ∀ i
cp-1Bp-1 + … c1B1 + c0B0 + c-1B-1 + c-2B-2 + c-3B-3+… valore rappresentato
esempio:
B=10, S B={0, 1, …, 9}
3,52
(c0,c-1c-2)10 → 3 + 5*10-1 + 2*10-2= 3+
5
2
+
10 100
Rappresentazione in virgola mobile
perchè "mobile"?? partiamo da un esempio (B=10)
0,00035 →
0,35 * 10-3
vogliamo rappresentare l'informazione 0,00035
in questa forma, ma potremmo anche scrivere:
3,5 * 10 -4 oppure 35 * 10-5
per convenzione si sceglie la prima:
0,00035
viene rappresentato dalla coppia
(+0.35, -3)
(m, e)
mantissa e esponente sono rappresentati in modulo e segno secondo la rappr.
posizionale
analogamente
10,22
quindi
viene rappresentato dalla coppia
(+0.1022, +2)
B-1≤ m <1
3 settembre 2001
34
quando m rispetta questo vincolo si dice che è normalizzata
problema
con un numero finito di caratteri non solo, come per gli interi, rappresentiamo
un sottoinsieme finito, MA poichè un reale può avere infinite cifre la sua
rappresentazione può essere approssimata → errore
errore di arrotondamento
∞
x=sgn(x) B e ∑ c −i B−i
x∈R
i=1
 1 se x ≥ 0
con sgn(x)= 
 −1 se x < 0
si definisce
k
tr(x) = sgn(x) Be ∑ c −i B−i
troncamento a k cifre
i=1
B

tr(x)
se
0
≤
c
<
k+1

2
arrotondamento a k cifre
rd(x) = 
B
e-k
se ≤ c k+1 < B
 tr(x) + sgn(x)B
2

l'arrotondamento significa incrementare di 1 la k-esima cifra, infatti:
k
tr(x)+sgn(x)Be-k=sgn(x)Be ∑ c −i B−i +sgn(x)Be-k = sgn(x)Be
i=1
k
( ∑ c−iB−i +B-k)
i=1
k−1
( ∑ c−iB−i +(ck+1) B-k)
=sgn(x)Be
i=1
esempio: B=10 k=3
4 5…
x1=105 * 0.3874
8 1…
x2=105 * 0.3878
tr(x1)=105 * 0.387
tr(x2)=105 * 0.387
rd(x1)=105 * 0.387
rd(x2)=105 * 0.387+105-3=105(0.387+10-3)=
=105(0.387+0.001)=105 0.388
Qual'è l'errore commesso?
3 settembre 2001
35
Per valutare l'errore ci sono due "modi":
sia x *∈R e x una sua approssimazione
def
errore assoluto: εa = | x-x*|
εa
x − x*
r
errore relativo: ε = * =
x*
x
definito solo per x*≠0
l'errore relativo dà un'idea più precisa dell'ampiezza dell'errore:
esempio: εa = 10-3 ha un effetto diverso se x*=350 oppure se x*=0.00035
infatti il dato approssimato sarebbe
x=350 +- 10-3 → εr=2.8*10-6=2.8*10 -4%
oppure
x=0.00035 +- 10-3 → εr=2.8=280%
nel caso dell'errore di arrotondamento, in generale si ha:
εa < Be-k
1
εa ≤ Be-k
2
troncamento a k cifre
arrotondamento a k cifre
Intervallo di rappresentazione
con un numero finito di cifre rappresentiamo un insieme finito, quindi un
sottoinsieme dei numeri reali
nelle attuali macchine per rappresentare i numeri reali si utilizzano 32 cifre
(binarie):
k=24
cifre per la mantissa
h=7
cifre per l'esponente (1 per il segno)
1
cifra per il segno
1
24
7
+- e
+m
questa è la rappresentazione in singola precisione
3 settembre 2001
36
spesso è disponibile una rappresentazione in doppia precisione con mantissa di
56 cifre
3 settembre 2001
37
Lezione 9
Rappresentazione dei numeri reali
caratteristiche dell'intervallo di rappresentazione:
• è simmetrico rispetto allo 0
esaminiamo la parte positiva (B=2)
Xmax=+mmax
2emax
mmax=+0.11…1
k
∑2
→
i=1
k
utilizzando la serie nota
∑ ai =
i=0
a
k+1
−i
k
1
= ∑ 
 
i=1 2
i
−1
a k+1 − 1
e quindi ∑ a i =
−1
a −1
a
−
1
i=1
k
otteniamo
mmax=1-2-k
per l'esponente sappiamo che con h-1 cifre il max intero rappresentabile è:
emax=2h-1-1
Xmin=+mmin 2 emin
mmin=+0.10…0=2-1
emin=-(2h-1-1)
esempio: k=24, h=7
emax=26-1=63
emin=-(26-1)=-63
Xmax=+(1-2-24) 263=263-239
Xmin=+2-1 2 -63=2-64
0 +X
- X max
- Xmin
min
+ X max
3 settembre 2001
38
l'insieme dei numeri rappresentati è:
F ⊂ ({0} ∪ {X | Xmin ≤ |X| ≤ Xmax})
e gli altri reali rappresentati come sono distribuiti?
calcoliamo
X'=min{X | X>Xmin}
X"=max{X | X<X max}
X'=(0.10…01) 2emin=(2 -1+2-24) 2-63=2-64+2-87
X"=(0.11…10) 2emax=(1-2-24-2-24) 263=(1-2-24) 263-239
le "distanze" da Xmin e Xmax rispettivamente sono:
2-87 e 239
-87
2
2 39
+ X max
X"
X'
Xmin
• i numeri rappresentati sono un sottoinsieme dei razionali, in particolare sono i
numeri frazionari con una potenza di 2 al denominatore: es. 1/3, 1/10 non sono
rappresentati
• i numeri non in F vengono approssimati con il numero in F "più vicino"
• i numeri piccoli sono "meglio" rappresentati
inoltre consideriamo X1, X2 ∈ F e op ∈ {+, -, *, /}, può succedere
∈F
X1 op X2 
∉F
• l'aritmetica in virgola mobile è intrinsecamente approssimata
in generale dati a, b, c, d ∈ F
a(b+c)/d
(a/d)(b+c)
(ab+ac)/d
danno valori diversi se calcolate in F
3 settembre 2001
39
Conversione di base
(numeri reali)
per la parte intera si usa l'algoritmo già visto; per la parte frazionaria l'algoritmo
è simile a quello per la conversione di interi, ma la spiegazione è più complicata
vediamolo tramite un esempio: x=0.625
0.625*2=1.25
0.25*2=0.5
0.5*2=1.0
termina!!!
c-1=1
c-2=0
c-3=1
il dato del passo successivo è 0.25
il dato del passo successivo è 0.5
il dato del passo successivo è 0
NB
le cifre si ottengono in ordine crescente di significatività
0.625 in virgola mobile e base 2 diventa
(0.101, 0) la mantissa è già normalizzata
esempio (infinite cifre): 0.6
0.6*2=1.2
0.2*2=0.4
0.4*2=0.8
0.8*2=1.6
0.6 … !!!!!
c-1=1 il dato del passo successivo è 0.2
c-2=0 il dato del passo successivo è 0.4
c-3=0 il dato del passo successivo è 0.8
c-4=1 il dato del passo successivo è 0.6
in base 2 ci servirebbero infinite cifre 0.10011001…
supponiamo di avere h=4 cifre per la mantissa
(0.1001, 0)
vediamo l'errore commesso
1 1
9
1*2-1+1*2-4= +
=
= 0.5625
2 16 16
quindi
εa = |0.6-0.5625| = 0.0375
εr = 6.25 %
3 settembre 2001
40
Lezione 10
Unità funzionali
Vantaggi:
• fornire un supporto esplicito alla realizzazione modulare dei programmi;
• consentire il riuso di funzionalità già sviluppate (in punti diversi del programma o
in programmi diversi);
• rendere più leggibili i programmi (rendere più evidente la loro struttura logica,
questo li rende anche più manutenibili);
come già detto all'inizio, un programma C è un insieme di funzioni, "main" è una
funzione speciale
Funzioni senza parametri
SINTASSI:
definizione di funzione
tipo nome ()
{comando;
…
comando;}
corpo funzione
tipo: void , int, double, char ………
esempio in classe stringa:
void stampa ()
{cout <<st <<'\n';}
REGOLE SEMANTICHE:
• una funzione ha un tipo oppure è void (in altri linguaggi le funzioni senza tipo sono
chiamate "procedure");
3 settembre 2001
41
• una funzione con tipo diverso da void deve restituire un valore di quel tipo
mediante una istruzione
return espressione;
• una funzione deve essere dichiarata prima di essere "usata" (NOTA: questa è la
stessa regola semantica di qualunque oggetto dell’ambiente);
come vengono fatte le attivazioni?
SINTASSI (chiamata di funzione)
• una funzione viene "chiamata" utilizzando il suo nome seguito dalla coppia di
parentesi tonde vuote;
nome_fun ()
SEMANTICA
la sua chiamata corrisponde all’esecuzione del blocco che costituisce il corpo della
funzione
MA
se la funzione ha un tipo:
la sua chiamata restituisce un valore (del tipo specificato) mediante
l’istruzione "return";
quindi la sua chiamata è una espressione oppure è un termine di una
espressione;
se la funzione è "void":
la sua chiamata è un comando;
NB
le attivazioni dei metodi delle classi seguono una sintassi particolare: il metodo viene
chiamato su un oggetto della classe
3 settembre 2001
42
esempio:
class contobancario
{double saldo;
public:
contobancario()
{saldo=0;}
double estrattoconto()
{return saldo;}
void preleva (double quantita)
{if (saldo>=quantita)
saldo=saldo-quantita;}
void versa (double quantita)
{saldo=saldo+quantita;}
}
scriviamo una funzione che calcoli gli interessi di un contobancario x dato un
certo tasso
double interessi()
{return x.estrattoconto()*tasso;}
l'intero programma sarà:
3 settembre 2001
43
#include <iostream.h>
class contobancario
{double saldo;
public:
contobancario()
{saldo=0;}
double estrattoconto()
{return saldo;}
void preleva (double quantita)
{if (saldo>=quantita)
saldo=saldo-quantita;}
void versa (double quantita)
{saldo=saldo+quantita;}
}
contobancario x;
double tasso;
double interessi()
{return x.estrattoconto()*tasso;}
main()
{ contobancario vittoria;
const double tassofisso=0.05;
double inter;
vittoria.versa(100000);
cout <<"Il suo saldo e': " <<vittoria.estrattoconto() <<'\n';
cout <<"Gli interessi annuali ad un tasso dello " <<tassofisso <<" sono: ";
x=vittoria;
tasso=tassofisso;
inter=interessi();
cout <<inter <<'\n';
cout <<"Dopo un anno il suo saldo sarebbe: " <<vittoria.estrattoconto()+inter <<'\n';
}
• importanza della parametrizzazione (riuso e interfaccia es. di funzione
matematica)
SINTASSI dei parametri
nella definizione della funzione si ha la lista dei parametri formali
tipo nomefunzione (T1 PF1, T2 PF2, …)
{…}
PF1 è il nome del primo par for che è di tipo T1
PF2 è il nome del secondo par for che è di tipo T2
3 settembre 2001
44
double interessi(contobancario x, double tasso)
{return x.estrattoconto()*tasso;}
nella chiamata si ha la lista dei parametri attuali
nomefunzione (PA1, PA2, …)
PA1 è una espressione di tipo T1
PA2 è una espressione di tipo T2
inter=interessi(vittoria, tassofisso);
SEMANTICA
1. data una definizione di funzione, con una certa lista di parametri formali, in ogni
chiamata di quella funzione deve essere specificata una lista di parametri attuali
esattamente corrispondente, per numero, tipo e posizione a quella dei parametri
formali;
2. se nella def si "dichiara" il parametro PF di tipo T significa dichiarare una
variabile locale alla funzione;
3. al momento della chiamata, l’espressione PA viene valutata e il suo valore viene
assegnato a PF;
il legame viene perso, alla prossima
chiamata si potrebbe usare un’altra
locazione
 tasso  ↔


 double 
 tasso  ↔ 0.05


 double 
 tasso  ↔ 0.05


 double 
 tassofisso  ↔0.05


 double 
 tassofisso  ↔0.05


 double 
 tassofisso  ↔0.05


 double 
prima della chiamata
al momento della chiam.
alla fine della chiamata
3 settembre 2001
45
Lezione 11
Unità funzionali con parametri
Supponiamo di voler scrivere una funzione che calcoli gli interessi annuali ed
aggiorni il saldo di un contobancario:
void fineanno(contobancario x, double tasso)
{x.versa(interessi(x, tasso));}
verrebbe utilizzata nel main nel modo seguente:
main()
{contobancario vittoria;
const double tassofisso=0.05;
double inter;
vittoria.versa(100000);
cout <<"Il suo saldo e': " <<vittoria.estrattoconto() <<'\n';
fineanno(vittoria, tassofisso);
cout <<"Dopo la capitalizzazione degli interessi annuali al tasso di "
<<tassofisso;
cout <<" il suo saldo e': " <<vittoria.estrattoconto() <<'\n';
}
se provate ad eseguire vedrete che il saldo non cambia!!!
La spiegazione è nella semantica data del passaggio parametri: la modifica è
stata fatta sul PF (x) e non riportata sul PA (vittoria)!
Questa è la modalità di passaggio per valore, che non va bene quando vogliamo
che le modifiche permangano sul PA;
dovremmo invece utilizzare la modalità per riferimento:
SINTASSI
nella definizione della funzione si ha la lista dei parametri formali, il nome è
preceduto dal simbolo &
tipo nomefunzione (T1 &PF1, T2 PF2, …)
{…}
PF1 è il nome del primo par for passato per riferimento
PF2 è il nome del secondo par for passato per valore
3 settembre 2001
46
void fineanno(contobancario &x, double tasso)
{x.versa(interessi(x, tasso));}
nella chiamata si ha la lista dei parametri attuali
nomefunzione (PA1, PA2, …)
PA1 è una variabile di tipo T1
(non può essere una costante o una espressione)
PA2 è una espressione di tipo T2
SEMANTICA
1. un parametro dichiarato per riferimento implica una duplicazione di riferimenti
alla memoria al momento della chiamata;
quindi assegnamenti fatti a PF nel corpo della funzione equivalgono ad assegnamenti
fatti a PA
fineanno(vittoria, tassofisso);
x




 contoba. 
x




 contoba. 
x




 contoba. 
 vittoria  ↔ saldo = 0  vittoria  ↔ saldo = 0




 contoba. 
 contoba. 
 vittoria  ↔ saldo =


 contoba.  105000
prima della chiamata
alla fine della chiamata
al momento della chiam.
valore: solo input
riferimento: input-output
NOTA BENE
E’ buona regola usare per quanto possibile il passaggio per valore, specialmente
nelle funzioni con tipo che sono delle espressioni (o parti di) e non devono
perciò produrre effetti collaterali
3 settembre 2001
47
Vediamo degli esempi di funzioni per chiarire i concetti di ambiente, campo
d’azione e località/globalità di un identificatore.
int y;
void P()
{ int x;
.
.
y è globale a P e a main, la portata di y è estesa a tutto
il programma
x è locale a P, la portata di x è limitata al corpo di P;
la memoria allocata per x viene rilasciata al termine
del blocco di P
x=3;
.
.
}
main ()
{
.
.
P();
nelle attivazioni di P l’associazione (x, cella di memoria)
.
.
viene ogni volta ricreata
P();
.
.
}
const int a=8;
int x;
void P()
{ const int a=3;
.
.
la costante a e la variabile x sono globali all’intero
programma, la loro portata è estesa a tutto il programma
escluso il corpo di P il in cui il nome a è ridichiarato, quindi
in P x è globale e a è una costante locale di valore 3;
(la memoria allocata per a viene rilasciata al termine
del blocco di P)
x=a-1;
.
.
}
main ()
{
.
.
P();
x=x-a;
per effetto di questa attivazione la variabile globale x
assume il valore 2; dopo l’assegnamento x è aggiornata
.
.
.
.
sottraendole il valore globale 8, quindi vale -6
}
3 settembre 2001
48
Lezione 12
Definizione nuovi tipi
oltre ai tipi base, l'utente può definire nuovi "domini"
tipi elementari
(gli oggetti appartenenti al dominio sono "semplici")
tipi base (int, double, char)
enum
SINTASSI
enum <nome> {nome1, nome2, …};
SEMANTICA
• definisce un insieme di costanti intere identificate dai nomi specificati nella lista
(enumeratori);
• agli enumeratori sono associati valori interi consecutivi a partire da 0; (si possono
anche associare valori diversi;)
• generalmente le variabili di tipo enumerato vengono usate solo per operazioni di
confronto; (si possono usare tutte le oper. definite sugli interi, che agiscono sulla loro
codifica)
esempi:
enum giorni_settimana {Domenica, Lunedì, Martedì, Mercoledì, Giovedì, Venerdì,
Sabato};
.
.
.
gli vengono associati i valori da 0 a 6
giorni_settimana oggi;
è lecito scrivere espressioni come:
Lunedì < Mercoledì Lunedì < Domenica
queste vengono valutate considerando l’intero associato, quindi la prima è vera, la
seconda è falsa.
3 settembre 2001
49
Potremmo usare l’enumerazione per definire il tipo booleano che in C++ manca:
enum booleano {falso, vero};
Tipi strutturati
tipi strutturati
array
struct
class
(gli oggetti appartenenti al dominio sono "complessi")
Con i meccanismi che conosciamo finora (tipi base + enumerazione), siamo in grado
di definire domini che sono insiemi di oggetti semplici, singoli;
spesso ci occorre poter definire domini i cui elementi sono agglomerati di oggetti,
legati fra loro secondo una qualche struttura
SINTASSI
array
tipo nome [<espressione>] ...[<espressione>];
ATT.NE
stiamo dichiarando un oggetto (variabile) array non un tipo!
Per dichiarare il tipo dovremmo usare il typedef
SEMANTICA
• un oggetto di tipo array è una collezione di elementi (in numero fissato) dello stesso
tipo; fisicamente viene riservata un’area contigua di memoria;
• le espressioni che definiscono le dimensioni devono essere costanti intere;
• ogni elemento è individuabile esattamente specificando uno o più indici;
sintassi per indicare un elemento appartenente ad un oggetto di tipo array:
nome_array [espressione 1]…[espressionen];
3 settembre 2001
50
semantica:
• nome_array è il nome dell’array e espressione i deve essere una espressione
costante;
esempi:
int A[10];
abbiamo definito un array di 10 interi; questi sono identificati dagli indici 0 - 9;
quindi se scriviamo:
A[0]=0;
A[10]= …
stiamo assegnando 0 al primo elemento dell’array;
è un errore!!! L’ultimo elemento è indicato da A[9]
double B [3] [4];
stiamo definendo un array di array, cioè un array
bidimensionale, con 3 righe e 4 colonne;
assegnamo al primo elemento il valore 1.2;
assegnamo all’elemento di riga 2 e colonna 3 (è l'ultimo
elemento) il valore 5;
B[0][0]= 1.2;
B[2][3]=5;
NOTA BENE
• Un array non può essere restituito da una funzione.
• Quando si passa un array come parametro ad una funzione, il passaggio avviene
sempre per riferimento e quindi l’array viene modificato
3 settembre 2001
51
esempio:
#include <iostream.h>
const int N=2, M=3;
void mult (int A [N][M], int n)
{ for (int i=0; i<N; i=i+1)
for (int j=0; j<M; j=j+1) A[i][j]=A[i][j]*n;
}
main ()
{ int x;
int B [N][M]={1, 2, 3,
4, 5, 6};
cout <<"Scrivi il valore intero per cui vuoi moltiplicare gli elementi della tabella\n";
cin >>x;
mult (B, x);
for (int i=0; i<N; i=i+1)
{ for (int j=0; j<M; j=j+1) cout <<B[i][j] <<" ";
cout <<'\n';};
}
stringhe
• una stringa è un array di char
• l'abbiamo vista nel primo es. di programma:
class stringa
{ char st[100];
public:
stringa ()
{ char saluto[100]="buon giorno!";
st=saluto;}
void stampa ()
{cout <<st <<'\n';}
};
• una costante stringa si indica tra " "
• si può assegnare una costante stringa solo nella definizione
• la libreria string.h contiene funzioni per l'elaborazione di stringhe
3 settembre 2001
52
Lezione 13
Definizione nuovi tipi
Il meccanismo “array” che abbiamo visto consente di definire agglomerati di oggetti
tutti dello stesso tipo
agglomerati omogenei
ogni elemento dell’agglomerato è individuato da un valore particolare degli indici
dell’array.
Il C++ fornisce un altro meccanismo per definire
agglomerati non omogenei
SINTASSI
struct
struct nome {dichiarazione; … dichiarazione;};
ATT.NE
Stiamo dichiarando un tipo! (Quindi definendo un nuovo dominio)
vincoli:
• nei comandi dichiarazione ci può essere qualunque tipo, anche una struttura purchè
diversa da quella che la contiene;
SEMANTICA
• si dichiara un aggregato di oggetti di tipo diverso, in particolare gli oggetti sono
dichiarati nella lista tra parentesi, sono detti membri;
esempio:
struct data { int giorno, mese, anno;}
3 settembre 2001
53
in questo modo abbiamo dichiarato un tipo data che è l’aggregazione dei tre membri
di tipo intero e di nomi giorno, mese e anno;
data oggi;
in questo modo dichiariamo una variabile di tipo data; per questa viene allocata
un’area di memoria pari a tre interi (ai suoi membri);
oggi
giorno
mese
anno
SELEZIONE DI MEMBRO: per indicare un membro di una struttura si usa
nome_strutt . nome_membro
oggi.giorno
OPERAZIONI:
• è possibile l’assegnamento tra strutture dello stesso tipo, l’assegnamento avviene
mediante la copia membro a membro
es.:
data oggi, domani;
oggi=domani;
equivale a
oggi.giorno=domani.giorno
oggi.mese=domani.mese
oggi.anno=domani.anno
• le operazioni di confronto non sono def. sulle strutture
NOTA BENE
• Una funzione può assumere il tipo struttura
• Una struttura può essere passata per valore come argomento di funzione
esempio: programma che calcola le radici di un’equazione di secondo grado
3 settembre 2001
54
#include <iostream.h>
#include <math.h>
struct sol {int quante; double r1,r2;};
sol radici (double a, double b, double c)
{ double delta;
sol t;
if (a==0 && b==0) t.quante=0;
else if (a==0) {t.quante=1; t.r1=-c/b;}
else { delta=b*b-4*a*c;
if (delta<0) t.quante=0;
else { t.quante=2;
delta=sqrt(delta);
t.r1=(-b+delta)/(2*a);
t.r2=(-b-delta)/(2*a);}
}
return t;
}
main ()
{ double aa, bb, cc;
sol s;
cout <<"Scrivi i coefficienti dell'equazione:\n";
cin >>aa >>bb >>cc;
s=radici(aa, bb, cc);
switch (s.quante)
{ case 0: cout <<"L'equazione non ha radici\n"; break;
case 1: cout <<"La radice dell'equazione e': " <<s.r1 <<'\n'; break;
case 2: cout <<"Le radici dell'equazione sono:\n" <<s.r1 <<' '
<<s.r2 <<'\n'; break;
}
}
)))
Abbiamo visto che un tipo di dato è individuato da
• un insieme di valori e
• un insieme di operazioni su tali valori (operazioni primitive)
Le operazioni primitive vengono realizzate facendo uso della rappresentazione degli
elementi del tipo;
utilizzando le funzioni primitive, possono essere definite altre operazioni, ma queste
non dovrebbero mai fare riferimento alla rappresentazione interna;
la creazione di un oggetto (tramite la def. di variabile) di un dato tipo, è una delle
operazioni elementari valide per tutti i tipi (istanza di quel tipo)
3 settembre 2001
55
def
un tipo di dato la cui struttura interna è inaccessibile e i cui oggetti sono manipolabili
solo attraverso le operazioni primitive del tipo, è detto tipo di dato astratto
• i tipi fondamentali del linguaggio realizzano “bene” il concetto di tipo astratto
• quando utilizziamo i meccanismi per i tipi derivati (tipicamente array e struct) il
linguaggio non ci “garantisce” la “protezione” della rappresentazione interna
esempio: consideriamo i numeri complessi rappresentati in forma cartesiana
mediante una coppia (parte reale, parte immaginaria); possiamo realizzarli mediante i
tipi derivati
struct comp {double re,im;};
comp iniz_compl (double re, double im)
{ comp x;
x.re=re; x.im=im;
return x;}
double reale (comp x)
{ return x.re;}
double immag (comp x)
{ return x.im;}
comp somma (comp x, comp y)
{ comp z;
z.re=x.re+y.re; z.im=x.im+y.im;
return z;}
definizione delle funzioni primitive
nel programma è possibile dichiarare variabili di tipo complesso ed utilizzare le
primitive secondo il principio di occultamento dell’informazione (information
hiding):
comp z;
comp x1=iniz_compl (0.1, 0.2);
comp y1=iniz_compl (1.0, 2.0);
z=somma (x1, y1);
→
z.re=x1.re+y1.re; z.im=x1.im+y1.im;
le regole di visibilità non impediscono al programmatore
di utilizzare la struttura interna
3 settembre 2001
56
in C++ i tipi di dato astratti vengono realizzati mediante le classi che "estendono" il
concetto di struttura
Le classi
SINTASSI
class nome
{
parte privata
public:
parte pubblica
};
i MEMBRI della classe sono divisi in
membri PRIVATI e membri PUBBLICI
la parte privata contiene le strutture dati che
realizzano il tipo (si può anche usare lo specificatore
di accesso private)
la parte pubblica contiene le dichiarazioni delle
funzioni primitive (dette funzioni membro) che
realizzano l’INTERFACCIA
• una dichiarazione di membro può essere:
- una dichiarazione di tipo;
- una dichiarazione di campo dato (variab non inizializzata)
- una dichiarazione (o definizione) di funzione
- una dichiarazione di classe;
non si possono dichiarare oggetti costanti
SEMANTICA
• realizza un tipo di dato astratto; è accessibile solo la parte pubblica
esempio:
class complesso
{
double re,im;
public:
complesso (double r, double i) { re=r; im=i;};
double reale () { return re;};
double immag () { return im;};
…
};
3 settembre 2001
57
Operatori predefiniti
• le funzioni membro possono usare i nomi dei campi (es. re e im) della parte privata,
senza alcuna indicazione dell’oggetto di cui fanno parte;
questo perchè una funzione membro viene sempre applicata ad un oggetto della
classe, di conseguenza la chiamata ha la seguente SINTASSI:
nome_ogg_classe.nome_fun (PA1, PA2, …)
OP. DI SELEZIONE
parametri attuali
esempio:
complesso z(0.1, 0.2);
double x;
x=z.reale ();
• l’operatore di selezione viene anche usato per selezionare campi dato pubblici
altre operazioni predefinite (come per le strutture)
un oggetto (di una classe, anche detto ISTANZA) può essere
• inizializzato (nella dichiarazione con un altro oggetto della stessa classe);
• assegnato ad un altro oggetto;
• usato come argomento di una funzione;
• restituito da una funzione;
l’assegnamento e il passaggio per valore avvengono (come per le strutture)
mediante copia membro a membro dei campi dato
opzioni
• tra le funzioni membro è possibile definire la funzione costruttore:
è una funzione che ha lo stesso nome della classe e inizializza gli
oggetti della classe quando vengono creati (istanziati mediante
3 settembre 2001
58
una dichiarazione)
complesso x(0.1, 0.2);
complesso x;
la variabile x viene creata e inizializzata;
ERRORE, def un costruttore le variab vanno sempre inizializzatze
esempio:
#include <iostream.h>
main()
{
class complesso
{
double re,im;
public:
complesso (double r, double i){ re=r; im=i; };
double reale () { return re;};
double immag () { return im;};
void somma (complesso x)
{re=re+x.reale();
im=im+x.immag();}
};
complesso x1(0.1, 0.2);
complesso x2(1.0, 2.0);
x1.somma(x2);
cout <<"Parte reale = " <<x1.reale() <<"Parte immaginaria = " <<x1.immag()
<<'\n';
}
3 settembre 2001
59
Esercizi
3 settembre 2001
60
Lezione 1
(introduzione, architettura, compilazione)
Esercizio 1.1 Spiegare la funzione della codifica dell'informazione in formato
binario e fare due esempi di codifica.
Esercizio 1.2 Che cosa distingue un computer da un comune elettrodomestico?
Esercizio 1.3 Quali parti di un computer possono conservare il codice dei
programmi? E quali possono conservare i dati dell'utente?
Esercizio 1.4 Quali parti di un computer servono per fornire le informazioni
all'utente? Quali accettano l'input dell'utente?
Esercizio 1.5 Classificare i dispositivi di memoria che possono fare parte del
sistema di un computer, ordinati per (a) velocità, (b) costo e (c) capacità di
immagazzinamento.
Esercizio 1.6 Spiegare la differenza tra codice sorgente e codice eseguibile.
Esercizio 1.7 In che modo si scoprono gli errori di sintassi? Come si scoprono
gli errori logici?
Lezione 2
(primo programma)
Esercizio 2.1 Spiegare la differenza tra un oggetto e una classe.
Esercizio 2.2 Spiegare la differenza tra una classe e un metodo.
Esercizio 2.3 Cosa è una libreria? A cosa serve e come si usa?
Esercizio 2.4 Scrivere tre versioni del programma "buon giorno!" con tre diversi
errori di sintassi.
3 settembre 2001
61
Lezione 4
(tipi predefiniti, assegnamento)
Esercizio 4.1 Cosa è un tipo di dato?
Esercizio 4.2 Che differenza c'è tra i tipi fondamentali e i tipi derivati di un
linguaggio di programmazione?
Esercizio 4.3 Quali sono i tipi fondamentali del C+ +? Descrivere le loro
caratteristiche.
Esercizio 4.4 Quale è lo scopo delle dichiarazioni? Elencare almeno 2
motivazioni.
Esercizio 4.5 Quale è la differenza tra una "variabile" e una "costante"?
Esercizio 4.6 Scrivere un semplice programma che utilizzi variabili e costanti.
Scriverne una versione con 2 errori sintattici e 2 errori semantici. Verificare in
quale fase gli errori vengono individuati e controllare i messaggi di errore.
Esercizio 4.7 Descrivere la semantica del comando assegnamento. Scrivere un
semplice programma in cui si violino le due regole semantiche
dell'assegnamento.
Esercizio 4.8 Spiegare perchè è possibile utilizzare una stessa variabile a sinistra
e a destra dell'assegnamento. Spiegare il diverso ruolo che il nome della
variabile svolge nei due "posti".
Lezione 5
(input/output, ambiente e stato)
Esercizio 5.1 Cosa è l' "ambiente" di un programma? E lo "stato" di un
programma? Elencare almeno 2 differenze tra ambiente e stato.
Esercizio 5.2 Scrivere un programma per visualizzare sullo schermo del
terminale il vostro nome all'interno di un rettangolo come segue:
3 settembre 2001
62
+-----------+
| Vittoria |
+-----------+
Esercizio 5.3 Scrivere un programma che calcoli una espressione aritmetica a
partire da dati in ingresso (interi o reali) e stampi il risultato. Curare l'interfaccia
del programma: l'esecuzione del programma deve essere esplicativa per l'utente.
Farne una versione con un errore di inconsistenza tra tipi.
Lezione 6
(espressioni logiche, controllo del flusso)
Esercizio 6.1 Completate la seguente tabella dei valori vero e falso, inserendo i
valori logici delle espressioni booleane per tutte le combinazioni degli input
booleani p, q e r.
p
falso
falso
falso
q
falso
falso
vero
…
r
falso
vero
falso
(p && q) || !r !(p && (q || !r))
altre 5 combinazioni
Esercizio 6.2 Spiegate la differenza tra i seguenti frammenti di codice:
s=0;
if (x>0) s++;
if (y>0) s++;
s=0;
if (x>0) s++;
else if (y>0) s++;
Esercizio 6.3 Scrivete un programma per ricevere dall'utente un input che
descrive una carta da gioco, mediante le abbreviazioni seguenti:
A
2 … 10
J
Asso
Punteggi delle carte
Jack
3 settembre 2001
63
D
R
Q
C
P
F
Donna
Re
Quadri
Cuori
Picche
Fiori
Il programma deve stampare la descrizione completa della carta, come in questo
esempio:
Inserisci la sigla della carta:
DP
Donna di picche
Esercizio 6.4 Un anno con 366 giorni è detto anno bisestile. Un anno è bisestile
se è divisibile per 4 (per es. il 1980), salvo nei casi in cui è divisibile per 100
(per es. il 1900). Tuttavia, è un anno bisestile se è divisibile per 400 (per es. il
2000). Però, non esistono eccezioni prima dell'adozione del calendario
gregoriano, avvenuta il 15 Ottobre 1582 (per es. il 1500 non è stato bisestile).
Scrivete un programma che chieda all'utente di inserire un anno, e che calcoli se
l'anno è bisestile.
Esercizio 6.5 Quante ripetizioni eseguono i cicli seguenti, se i non viene
modificato nel corpo del ciclo?
For
For
For
For
For
For
For
(i = 1; i <= 10; i++) …
(i = 0; i < 10; i++) …
(i = 10; i > 0; i--) …
(i = -10; i <= 10; i++) …
(i = 10; i >= 0; i++) …
(i = -10; i <= 10; i = i+2) …
(i = -10; i <= 10; i = i+3) …
Esercizio 6.6 Riscrivete il codice seguente, sostituendo il ciclo for con un ciclo
while.
int s=0;
for (int i=1; i <= 10; i++) s=s+i;
3 settembre 2001
64
Lezione 7
(rappresentazione posizionale, conversione di base)
Esercizio 7.1 Dati i seguenti numeri in base 10 n1=17, n2=128, n3=75, n4=23,
n5=44, darne la loro rappresentazione in base 2. Discutere la necessità di un
numero di cifre diverso per rappresentare i cinque valori.
Esercizio 7.2 Dati i numeri interi n1=(1101)2, n2=(1101)5, n3=(23)5,
n4=(201)3, n5=(3012)8, rappresentati rispettivamente in base B=2, B=5, B=5,
B=3, B=8, darne la loro rappresentazione in base B=10.
Esercizio 7.3 Spiegare la differenza tra "valore" e "rappresentazione" di un
numero. Spiegare anche tramite un esempio.
Esercizio 7.4 Quale è il massimo numero intero rappresentabile con 5 cifre in
base 8? E con 4 cifre e basi 2, 4, 10? Dare una spiegazione per il caso generale h
cifre e base B.
Esercizio 7.5 Scrivete un programma che riceve in input un numero intero
rappresentato in base 10 e stampi la sua rappresentazione in base 2. Il
programma può controllare che il dato in input rispetti certi vincoli in modo che
il numero di cifre in output non sia eccessivo.
Lezione 8
(rappresentazione in virgola mobile)
Esercizio 8.1 Spiegare il significato della "normalizzazione" della mantissa
nella rappresentazione in virgola mobile.
Esercizio 8.2 Rappresentare in virgola mobile normalizzata i seguenti numeri
(base 10): x1=0.67, x2=12.83, x3=0.00024, x4=1170, x5=4.0047.
Esercizio 8.3 Spiegare la differenza tra errore assoluto e errore relativo. Fare un
esempio in cui a parità di errore assoluto, l'errore relativo sia significativamente
diverso.
Esercizio 8.4 Dati i numeri (base 10) x1=0.56733, x2=102.89, x3=0.00017234,
dare per ognuno di essi la rappresentazione troncata e arrotondata a tre cifre
3 settembre 2001
65
decimali. Valutare gli errori relativi commessi. Utilizzare in ogni caso la
rappresentazione normalizzata.
Esercizio 8.5 Spiegare perchè l'aritmetica in virgola mobile è "intrinsecamente"
approssimata. Fare tre esempi di numeri rappresentabili con un numero finito di
cifre in base 10 e non rappresentabili con un numero finito di cifre in base 2.
Esercizio 8.6 Scrivete un programma per convertire la lettera di un voto
scolastico nel numero corrispondente. Le lettere sono A, B, C, D e F,
eventualmente seguite dai segni + o -. I loro valori numerici sono 4, 3, 2, 1 e 0.
F+ e F- non esistono. Un segno + o - incrementa o decrementa il valore
numerico di 0.3. Tuttavia, A+ è uguale a 4.0.
Inserisci la lettera di un voto:
BIl valore numerico è 2.7.
Lezione 9
(caratteristiche intervallo, conversione di base)
Esercizio 9.1 Descrivere e spiegare le caratteristiche dell'intervallo di
rappresentazione di numeri reali per base B, k cifre per la mantissa (escluso il
segno) e h cifre per l'esponente (incluso il segno).
Esercizio 9.2 Determinare il massimo e il minimo numero positivo
rappresentabile in virgola mobile in base 10, k=4 cifre per la mantissa (escluso il
segno) e h=3 cifre per l'esponente (incluso il segno).
Esercizio 9.3 Determinare la "distanza" tra i due numeri più grandi e tra i due
numeri più piccoli positivi, rappresentati in base 10 virgola mobile con k=8 cifre
per la mantissa (escluso il segno) e h=5 cifre per l'esponente (incluso il segno).
Esercizio 9.4 Convertire in base 2 virgola mobile i seguenti numeri
rappresentati in base 10: x1=0.67, x2=12.835, x3=0.083. Utilizzare 4 cifre per la
mantissa (normalizzata) e 3 cifre per l'esponente. Valutare l'errore assoluto
commesso.
Esercizio 9.5 Determinare:
1) i parametri h e k di un intervallo di rappresentazione F in base 10
3 settembre 2001
66
2) a, b, c, d ∈ F
3) una espressione aritmetica a piacere
tali che:
il risultato della espressione dia risultati diversi a seconda dell'ordine di
applicazione degli operatori. Per esempio a(b+c)/d ≠ (a/d)(b +c).
Esercizio 9.6 Scrivete un programma che legga una serie di numeri in virgola
mobile e che stampi:
- il valore massimo
- il valore minimo
- il valore medio.
Lezione 10
(funzioni)
Esercizio 10.1 Fornite esempi realistici per le seguenti funzioni:
- una funzione con un parametro di tipo double e con un valore restituito di tipo
double
- una funzione con un parametro di tipo int e con un valore restituito di tipo
double
- una funzione senza parametri e con un valore restituito di tipo int
Descrivete solamente che cosa fanno queste funzioni, senza scrivere il codice.
Esercizio 10.2 Queste affermazioni sono vere o false?
- Una funzione ha esattamente un enunciato return.
- Una funzione ha almeno un enunciato return.
- Una funzione ha al massimo un valore restituito.
- Una funzione di tipo void non ha mai un enunciato return.
- Quando si esegue un enunciato return, la funzione termina immediatamente.
- Una funzione senza parametri ha sempre un effetto collaterale.
- Una funzione di tipo void ha sempre un effetto collaterale.
- Una funzione senza effetti collaterali restituisce sempre lo stesso valore,
quando viene chiamata con gli stessi parametri.
Esercizio 10.3 Scrivete una funzione printSorted (int a, int b, int c) che
stampi i suoi tre input in ordine crescente.
3 settembre 2001
67
Esercizio 10.4 Scrivete un programma che riceva in ingresso un numero intero e
restituisca in uscita il suo fattoriale. Il fattoriale deve essere calcolato mediante
una funzione di tipo int.
Esercizio 10.5 La sequenza di Fibonacci è definita dalla regola seguente. I primi
due valori della sequenza sono 1 e 1. Ciascun valore successivo è costituito dalla
somma dei due valori che lo precedono. Se fn indica l'ennesimo valore nella
sequenza di Fibonacci, allora abbiamo:
f1 = 1
f2 = 1
fn = fn-1 + fn-2
se n > 2
Scrivete un programma che chieda all'utente di inserire il valore di n e che
stampi l'ennesimo valore della sequenza di Fibonacci (calcolato mediante una
funzione).
Suggerimento: non occorre registrare tutti i valori di fn. Vi servono solamente
gli ultimi due valori per calcolare quello successivo.
Esercizio 10.6 Scrivete una funzione che verifichi se un numero intero positivo
è primo. La funzione deve avere il prototipo int test_primo (int n) dove n è
l'intero che deve essere controllato; la funzione restituisce 1 (vero) se n è primo
e 0 (falso) se n non è primo.
Scrivete una seconda funzione, void list_primi (int m) , che utilizzi test_primo
() per stampare tutti i numeri primi fino a m. Scrivete un programma, utilizzando
le funzioni test_primo () e list_primi (), che richieda in input un intero positivo
e stampi tutti i numeri primi minori o uguali di tale intero. Confrontare i propri
risultati con la tavola dei numeri primi.
Suggerimento: semplici divisioni per successivi interi sono sufficienti per
realizzare test_primo.
Lezione 11
(passaggio per riferimento)
Esercizio 11.1 Descrivere le differenze semantiche delle due modalità di
passaggio parametri per valore e per riferimento.
Esercizio 11.2 Scrivete una funzione che scambia due interi, cioè la sua
chiamata swap (a, b) provoca lo scambio dei valori di a e b (es. prima della
chiamata a=1, b=111, dopo la chiamata a=111, b=1).
3 settembre 2001
68
Scrivete un programma che legge quattro interi e, utilizzando swap, scambia i
valori del primo con il quarto e del secondo con il terzo.
Esercizio 11.3 Scrivete una funzione che calcoli la media aritmetica di N valori
interi letti da tastiera. N è un parametro della funzione. Scrivete un programma
che chieda in ingresso il valore N, chiami la funzione media e infine stampi il
risultato. Fare due versioni:
1) la funzione calcola la media e la restituisce al chiamante mediante un
parametro per riferimento;
2) la funzione è di tipo double, calcola la media e la restituisce al chiamante
mediante la sua stessa chiamata.
Evidenziare vantaggi/svantaggi delle due soluzioni.
Lezione 12
(array)
Esercizio 12.1 Per ciascuno dei seguenti gruppi di valori, scrivete un codice che
riempia un array a con i valori:
- 1 2 3 4 5 6 7 8 9 10
- 0 2 4 6 8 10 12 14 16 18
- 1 4 9 16 22 38 43 56 62 71
-0000000000
- 1 4 9 16 9 7 4 9 11 21
Quando è il caso, usate un ciclo.
Esercizio 12.2 Che cos'è l'indice di un array? Quali sono i limiti di un array?
Che cos'è un errore di limiti?
Esercizio 12.3 Scrivete una funzione che riceva come parametro un array di
interi e restituisca come risultato il massimo e il minimo elemento nell'array.
Esercizio 12.4 Scrivete una funzione di tipo double che calcoli il prodotto
scalare di due vettori matematici (rappresentati come array e passati come
parametri). Se a e b sono i vettori, il loro prodotto scalare è:
a0b0 + a1b1 + … + an-1bn-1
Esercizio 12.5 Vero o falso?
3 settembre 2001
69
- Tutti gli elementi di un array sono dello stesso tipo.
- Gli indici degli array devono essere numeri interi.
- Gli array non possono contenere stringhe come elementi.
- Gli array bidimensionali hanno sempre lo stesso numero di righe e colonne.
- In un array bidimensionale gli elementi di colonne diverse possono avere tipi
diversi.
- Una funzione non può restituire un array bidimensionale.
- Una funzione può cambiare la lunghezza di un array fornito come parametro.
Esercizio 12.6 Scrivete una funzione di tipo logico che verifichi se i due array di
interi, passati come parametri, hanno gli stessi elementi, ignorando la
molteplicità e l'ordine. Per esempio, i due array
1 4 9 16 9 7 4 9 11
e
11 11 7 9 16 4 1
hanno gli stessi elementi. Probabilmente avrete bisogno di una o più funzioni di
supporto.
Esercizio 12.7 Scrivete una funzione di tipo logico che riceva come parametri
un array di caratteri e un carattere e restituisca vero se il carattere è presente
nell'array e falso altrimenti.
Esercizio 12.8 Scrivete una funzione che riceva come parametri un array di
caratteri e un carattere. La funzione controlla se il carattere dato è presente
nell'array. In caso affermativo restituisce la posizione dell'elemento dato, in caso
negativo lo inserisce nella I posizione dell'array.
Lezione 13
(classi)
Esercizio 13.1 Scrivere un programma che utilizzando la classe
3 settembre 2001
70
class contobancario
{double saldo;
public:
contobancario()
{saldo=0;}
double estrattoconto()
{return saldo;}
void preleva (double quantita)
{if (saldo>=quantita)
saldo=saldo-quantita;}
void versa (double quantita)
{saldo=saldo+quantita;}
}
crei variabili di tipo contobancario e effettui le operazioni previste dalle
primitive "guidato" da scelte date in input.
Nel programma potete:
- aggiungere nella parte privata dati che si ritengano utili;
- migliorare le primitive con controlli che si ritengano utili;
- realizzare una funzione (non primitiva) per il trasferimento di denaro da un
conto all'altro;
- utilizzare la funzione
double interessi(contobancario x, double tasso)
{return x.estrattoconto()*tasso;}
Esercizio 13.2 Realizzare una classe Product. Ciascun prodotto ha un nome e
un prezzo. Fornite i seguenti metodi:
- printProduct () per stampare il prodotto;
- getPrice () per reperire il prezzo;
- setPrice () per impostare il prezzo.
Scrivete un programma per creare due prodotti e per stamparli, per poi ridurre il
loro prezzo di una certa quantità e stamparli nuovamente.
3 settembre 2001
71