CPP - INFN

Transcript

CPP - INFN
Indice
Le istruzioni in C++ (CAP 5)
• le istruzioni in C++
Alberto Garfagnini e Marco Mazzocco
Università degli studi di Padova
• la visibilità nelle istruzioni
• l’ordine di valutazione nelle chiamate a funzioni
• l’istruzione condizionale if
A.A. 2014/2015
• il ciclo while e do while
• i cicli for tradizionale o range for
• le istruzioni break e continue
• la gestione delle eccezioni in C++
Le istruzioni in C++
• Le istruzioni in un programma in C++ sono eseguite sequenzialmente
• Il linguaggio definisce un insieme di istruzioni che permettono di controllare
il flusso del programma
• La maggior parte delle istruzioni termina con un punto-e-virgola
ival + 7;
std::cout << ival;
// inutile, ma valido
L’istruzione più semplice è quella nulla:
;
// a null statement
Può essere utile dove il linguaggio richieda una istruzione, ma la logica del
programma no :
while(std::cin >> s && s != valore_cercato)
; // a null statement
Attenzione però alle istruzioni nulle non volute
while( it != vec.end() ) ; // corpo del while nullo
++iter;
// l’incremento e’ fuori dal loop!!
Le istruzioni composte o blocchi di istruzioni
• Un’istruzione composta viene definita come blocco se è formata una serie
di istruzioni racchiuse da parentesi graffe
while (val <= 10) {
sum += val;
++val;
}
È necessario racchiudere le istruzioni nel while in un blocco per far sì che
vengano eseguite tutte nel ciclo ⇒ il corpo del ciclo while è una istruzione
o più istruzioni racchiuse in un blocco
È possibile definire un blocco vuoto:
while(std::cin >> s && s != valore_cercato)
{ } // empty block
La visibilità nelle istruzioni
• È possibile definire variabili dentro strutture di controllo if, while, for
• la loro visibilità è limitata al ciclo :
while (int i = get_num())
std::cout << i << std::endl;
i = 0; // Errore: i non accessibile fuori dal while
for (int i = 0; i<n; i++)
std::cout << i << std::endl;
i = 0; // Errore: i non accessibile fuori dal for
la stessa cosa si verifica per le variabili definite in un blocco :
while(std::cin >> s && s != valore_cercato) {
int a = s;
}
std::cout << a; // Errore: a non ccessibile fuorid al blocco
Ordine di valutazione
3 la precedenza definisce come sono raggruppati gli operandi di
un’espressione complessa
7 nulla è specificato riguardo all’ordine di valutazione
int i = sqrt(val) * f2();
3 le due funzioni saranno invocate prima della moltiplicazione, ma non c’e’
nessun modo di conoscere l’ordine di chiamata
int i = 0;
cout << i << " " << ++i << endl;
3 non c’e’ nessun modo per conoscere come verrà valutata l’istruzione :
A Il compilatore può valutare prima ++i e poi i producendo come output 1 1
B Il compilatore può valutare prima i e in tal caso avremo come output 0 1
C Il compilatore può decidere un’altra strategia a noi sconosciuta
se è necessario l’accesso, vanno definite fuori dal blocco
auto beg = v.begin();
while (beg != v.end() && *beg >= 0 ) {
++beg;
}
std::cout << a; // Errore: a non ccessibile fuorid al blocco
Lo statement if
• Ne esistono due forme
if (espressione) statement
if (espressione) statement_1 else statement_2
• A multiway conditional statement
if (espressione_1)
statement_1
else if (espressione_2)
statement_2
...
else
statement_n
// calcolatore.cxx - Esegue semplici calcoli
#include <iostream>
int main( )
{
using namespace std;
cout << "Introduci una espressione (numero op numero) :";
double n1, n2;
char op;
cin >> n1 >> op >> n2;
double val;
if (op == ’+’)
val = n1 + n2;
else if (op == ’-’) val = n1 - n2;
else if (op == ’/’) val = n1 / n2;
else if (op == ’*’) val = n1 * n2;
else {
cout << "Operatore \"" << op << "\" sconosciuto \n";
return 1;
}
cout << n1 << op << n2 << " = " << val << endl;
return 0;
}
$ ./a.out
Introduci una espressione (numero op numero) : 1 + 1
1+1=2
// calcolatore.cxx - Esegue semplici calcoli
#include <iostream>
int main( )
{
using namespace std;
cout << "Introduci una espressione (numero op numero) :";
double n1, n2;
char op;
cin >> n1 >> op >> n2;
double val;
if (op == ’+’)
val = n1 + n2;
else if (op == ’-’) val = n1 - n2;
else if (op == ’/’) val = n1 / n2;
else if (op == ’*’) val = n1 * n2;
else {
cout << "Operatore \"" << op << "\" sconosciuto \n";
return 1;
}
cout << n1 << op « n2 << " = " << val << endl;
return 0;
}
$ ./a.out
The "dangling else" problem
• Il codice seguente da luogo ad una difficoltà semantica
if ( a > 0 )
if ( b == 1 )
cout << "***" << endl;
else
cout << "###" << endl;
• A quale if appartiene lo statement else ?
• Al secondo ?
Introduci una espressione (numero op numero) : 4 % 2
Operatore "%" sconosciuto
The "dangling else" problem (2)
The "dangling else" problem (3)
• Per risolvere ogni ambiguità, racchiudere i blocchi tra parentesi ({ })
• Potevamo riscriverlo in questo modo
if ( a > 0 )
if ( b == 1 )
cout << "***" << endl;
else
cout << "###" << endl;
• Adesso sembra che lo statement else appartenga al
primo if
Regola
Ogni else statement si concatena allo statement if più vicino
if ( a > 0 ) {
if ( b == 1 ) {
cout << "***" << endl;
} else {
cout << "###" << endl;
}
}
if ( a > 0 ) {
if ( b == 1 ) {
cout << "***" << endl;
}
} else {
cout << "###" << endl;
}
Appartiene al if interno
default, senza { }
Appartiene al if esterno
sovrascrive la regola
Esempio : menu con l’istruzione switch
L’istruzione switch
• L’istruzione switch funziona come un dispositivo di routing, indicando al
computer quale riga di codice eseguire:
Sintassi:
switch ( espressione_intera )
{
case label_1 : statement(s)
case label_2 : statement(s)
#include <iostream>
using namespace std;
int main()
{
char scelta;
cout << "\nOpzione: ";
cin >> scelta;
$ ./a.out
Opzione: a
-> Opzione A
Opzione: b
-> Opzione B
Opzione: c
-> Opzione c non disponibile
A. stampa A
B. stampa B
Q. esce dal programma
->Opzione: q
while (scelta != ’Q’ && scelta != ’q’)
{
switch (scelta)
$
{
case ’a’:
case ’A’: cout << "\nOpzione A"; break;
case ’b’:
case ’B’: cout << "\nOpzione B"; break;
default : cout << "\nOpzione " << scelta << " non disponibile";
cout << "\nA. stampa A";
cout << "\nB. stampa B";
cout << "\nQ. esce dal programma";
}
cout << "\nOpzione: ";
cin >> scelta;
}
return 0;
...
default : statements(s)
}
• la scelta viene fatta tramite una
espressione_intera
• anche le etichette devono essere costanti intere (es const int, const char)
• una volta che il programma entra in una specifica linea ’case’, continua
l’esecuzione sequenzialmente, a meno che non venga diretto fuori dallo
switch con una istruzione break
}
Esempio : conto le vocali e consonanti in un testo
Il ciclo while
#include <cctype>
• Sintassi:
int vocali = 0, consonanti = 0;
char ch;
while (cin >>ch)
{
if ( isalpha(ch) ) {
switch (ch)
{
case ’a’ :
case ’e’ :
case ’i’ :
case ’o’ :
case ’u’ :
++vocali;
break;
default:
++consonanti;
}
while (test)
body
Prima del ciclo
$ ./a.out
testo di prova
Vocali: 5
Consonanti: 7
}
}
cout << "Vocali: " << vocali << endl;
cout << "Consonanti: " << consonanti << endl;
test
false
esce dal ciclo
true
body
Esempio :
int i = 0;
while (i<5) {
cout << i << endl;
i++;
}
Esempio : uso del ciclo while
Il ciclo for tradizionale
#include <iostream>
int main( )
{
using namespace std;
int c, digits=0, letters=0;
2 Fornisce una ricetta per eseguire azioni ripetute
• Consta di una
• Inizializzazione eseguita una volta sola per tutto il ciclo
• Verifica di una condizione necessaria per ripetere il ciclo (eseguita ogni
while ( (c = cin.get() ) != EOF) {
if (c>=’0’ && c <= ’9’)
digits++;
volta)
$ ./a.out
prova due 1243
Letters: 8
• Esecuzione delle istruzioni del corpo del ciclo (se sono più di una,
vanno racchiuse tra parentesi graffe)
Digits : 4
else if ( (c>=’a’ && c <= ’z’) ||
(c>=’A’ && c <= ’Z’) )
letters++;
• Aggiornamento del/dei valore/i utilizzati nel test
• Sintassi
}
cout << "Letters :" << letters << endl;
cout << "Digits :" << digits << endl;
for (init; test_expr; update_expr)
body
return 0;
}
Struttura del ciclo for
• Sintassi:
for (inizializzazione; test; update_expr)
body
// forloop.cxx - Uso del ciclo for per contare
#include <iostream>
int main( )
{
using namespace std;
cout << "Fino a che numero vuoi contare ?";
int max;
cin >> max;
int i; // Creo un contatore
for (i=1; i <= max; i++) {
cout << "Numero" << i << endl;
}
inizializzazione
test
false
esce dal ciclo
cout << "Alla fine del ciclo, i=" << i << endl;
true
body
update_expr
Esempio
for (int i=1; i<=5; i++) {
cout << "Numero: " << i << endl;
}
return 0;
}
$ ./a.out
Fino a che numero voi contare ? 3
Numero 1
Numero 2
Numero 3
Alla fine del ciclo i = 41
Fase di inizializzazione del ciclo for
• È possibile dichiarare una variabile all’interno dello statement di
inizializzazione del ciclo for
int numero =
for (int i =
numero *=
}
cout << "i =
Cicli e precisione finita dei calcolatori
• Nei cicli for, è una buona regola utilizzare espressioni relazionali,
quando possibile, invece di espressioni di eguaglianza.
12;
numero -1; i>0; --i) {
i;
• Nel caso di variabili di tipo reale (float o double), un test di
eguaglianza può andare al di là della precisione della macchina:
" << i;
• Attenzione, la variabile i non esiste più al di fuori del ciclo
• Un compilatore conforme allo standard ISO del C++ produce il
seguente messaggio di errore:
$ g++ -std=c++11 if_scope.cxx
if_scope.cxx: In function ’int main()’:
if_scope.cxx:11:21: error: ’i’ was not declared in this scope
double sum = 0.0;
for (double x=0.0; x!=9.9; x+=0.1) {
sum += x;
cout << sum << x << endl;
}
• Il ciclo entra in un loop infinito!
Il range for
C++11
Il ciclo do while
• Le stringhe, gli array e tutti i contenitori della libreria standard del C++
supportano il nuovo costrutto range for
• Sintassi:
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v = 0, 1, 2, 3, 4, 5;
for (auto i : v) // access by value
std::cout << i << ’ ’;
std::cout << std::endl;
for (auto & i : v) // access by reference
std::cout << i << ’ ’;
std::cout << std::endl;
for(int n : {0,1,2,3,4,5}) // a braced-init-list
std::cout << n << ’ ’;
std::cout << std::endl;
return 0;
}
do
body
while ( test );
Prima del ciclo
$ ./a.out
0 1 2 3 4 5
0 1 2 3 4 5
body
0 1 2 3 4 5
test
Il body viene sempre eseguito
almeno una volta
return, goto e break escono
dal corpo del do - while
false
esce dal ciclo
true
continue modifica l’esecuzione
(salta alla valutazione della condizione di uscita)
Esempio : uso del ciclo do while
#include <iostream>
int main( )
{
using namespace std;
break and continue
• interrompono e modificano il normale flusso del programma :
2 break
int i, error;
do {
cout << "Inserisci numero positivo: " << endl;
cin >> i;
if ( error = (i<=0) )
cout << "Numero non valido :" << i << endl;
} while (error);
cout << "Numero valido: " << i << endl;
return 0;
}
$ ./a.out
Inserisci numero positivo: -2
Numero non valido : -2
Inserisci numero positivo: 7
1 termina l’esecuzione del ciclo while, do, for e switch
2 trasferisce il controllo allo statement successivo alla fine del ciclo
2 continue
1 termina l’esecuzione del corpo del ciclo while, do o for
2 trasferisce il controllo alla fine del corpo del ciclo e l’esecuzione
continua con la rivalutazione della condizione di test (o con
l’espressione di incremento nel caso del for).
Numero valido : 7
L’istruzione continue e i cicli
L’istruzione break e i cicli
2 permette di uscire dal ciclo più interno
2 permette di saltare alcune parti di codice.
while ( cin.get( ) )
{
statement1
if ( ch == ’\n’ )
continue;
statement2
}
while ( cin.get( ) )
{
statement1
if ( ch == ’\n’ )
break;
statement2
}
statement3
statement3
2 break esce dal ciclo e continua alla prima istruzione successiva
2 continue salta le istruzioni che seguono e fa ripartire un nuovo ciclo
La gestione delle eccezioni in C++
2 Le eccezioni sono anomalie run-time che si possono presentare durante
l’esecuzione di un programma
esempio: perdita della connessione con un database, oppure gestione
inaspettata di input non formattato correttamente o fuori range (es numero
negativo al posto di un positivo)
2 La loro gestione può essere una delle parti più difficili nella progettazione di
un programma
2 il programma deve segnalare che qualcosa non ha funzionato e che non è
in grado di proseguire
3 la parte del programma interessato segnala l’errore (raise an exception);
un’altra parte del programma può essere dedicata alla gestione degli errori
(exception handling)
2 Il C++ fornisce :
3 espressioni throw
3 blocchi try - catch
3 un insieme di classi di eccezioni
Lo statement throw per generare una eccezione
2 Programma che calcola la radice
quadrata di un numero
cin >> x;
if ( x < 0.0 ) {
cerr << "Error, Input negativo";
return -1; // Uscita con errore
} else {
double res = sqrt(x);
cout << "sqrt = " << res << endl;
}
2 generiamo un’eccezione :
#include <stdexcept>
cin >> x;
if ( x < 0.0 ) {
throw runtime_error("Input negativo");
} else {
double res = sqrt(x);
cout << "Risultato: " << res << endl;
}
2 inizia con la parola chiave try ed è seguito da un blocco con una o più
istruzioni
a seguire una o più clausole catch, secondo la sintassi
try{
statement(s);
exception-handler;
Esempio : try - catch
#include <iostream>
#include <stdexcept>
$ ./ecc_02
#include <cmath>
Inserire un numero positivo: -2.0
Error: Input negativo
using namespace std;
Inserire nuovo numero: 2.0
int main()
Risultato: 1.41421
{
double x;
cout << "Inserire un numero positivo: ";
Il catch è fatto di tre parti:
la parola chiave catch,
la dichiarazione dell’eccezione
il blocco delle istruzioni
while (cin >> x) {
try {
if (x < 0.0) {
throw runtime_error("Input negativo");
} else {
cout << "Risultato: " << sqrt(x) << endl;
break;
}
} catch (runtime_error err) {
cout << "Error: " << err.what() << endl;
cout << "Inserire nuovo numero: ";
}
}
return 0;
} catch (exception-declaration) {
exception-handler;
...
}
$ ./a.out
1.5
Risultato: 1.22474
$ ./a.out
-1.5
terminate called after throwing an
instance of ’std::runtime_error’
what(): Input negativo
Aborted
3 std::runtime_error è una classe di eccezioni della libreria standard del
C++ definita nel file header stdexcept
Il blocco try - catch per gestire una eccezione
} catch (exception-declaration) {
$ g++ no_ecc.cxx
$ ./a.out
2.3
sqrt = 1.51658
$ ./a.out
-2.3
Error, input negativo
Una volta terminata l’esecuzione del
blocco del catch, il programma continua
l’esecuzione fuori dal blocco try-catch
}
Le eccezioni standard nelle librerie del C++
• Le librerie standard definisco svariate classi per riportare le eccezioni che si
possono presentare nelle funzioni della libreria standard :
exception
la classe base per le eccezioni
runtime_error
riporta problemi che possono essere diagnosticati
soltanto run-time
range_error
Run-time error : risultati generati al di fuori
di valori permessi
overflow_error
Run-time error : il calcolo produce un valore overflow
underflow_error
Run-time error : il calcolo conduce un underflow
logic_error
Errore nella logica del programma
domain_error
Logic error : argomenti per i quali non esiste risultato
invalid_argument
Logic error : argomento non appropriato
length_error
Logic error : tentativo di creare un oggetto più grande
della dimensione massima per quel tipo
out_of_range
Logic error : valore usato fuori dal range valido