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
© Copyright 2024 Paperzz