Programmazione Avanzata per il Calcolo Scientifico Lezione N. 1 Luca Formaggia MOX Dipartimento di Matematica “F. Brioschi” Politecnico di Milano A.A. 2011/2012 1/1 Aule e orari Il corso consiste di lezioni ex cathedra che si tengono il MERCOLEDI dalle 13.15 alle 15.15 e il GIOVEDI dalle 15.15 alle 18.15 e di laboratori didattici che si tengono il VENERDI dalle 15.15 alle 18.15 nel Laboratorio Didattico del Dipartimento (quarto piano). 2/1 Docenti I Docente: Luca Formaggia ([email protected]) I Esercitatori: Alessio Fumagalli ([email protected]) e Carlo deFalco ([email protected]) Orario di ricevimento: Mercoledi 11.15 13.15 (previo appuntamento) Libri di testo: C++ and Object Oriented Numeric Computing for Scientists and Engineers, Daoqi Yang, Springer-Verlag, 2000 Parallel scientific computing in C++ and MPI, Karniadakis, G. and Kirby, R.M.,Cambridge University Press, 2003 Note del corso (work in progress) 3/1 Altre risorse on-line I Il sito del corso www2.mate.polimi.it:8080//CN/PACS contiene molto materiale addizionale costantemente aggiornato. I www.cplusplus.com fornisce una eccellente on-line reference con esempi. I www.cppreference.com/wiki è una pagina wiki molto completa aggiornata all’ultimo standard C++11. 4/1 Modalità di esame Corso da 8 crediti I Valutazione della attività di laboratorio (3 punti) I Test in laboratorio didattico Corso da 10 crediti I Valutazione della attività di laboratorio (3 punti) I Progetto concordato con il docente (2 crediti) 5/1 Supporto informatico I portali del corso sono http://corsi.metid.polimi.it e www2.mate.polimi.it:8080/CN/PACS L’iscrizione al portale METID è necessaria per accedere ai tutti i servizi. Il sistema operativo di riferimento è Linux, i compilatori di riferimento sono i compilatori gnu versione 4.x. Utilizzeremo il softare eclipse come strumento di sviluppo (IDE) (scaricabile dal sito www.eclipse.org) Un altra IDE utilizzabile è Code::Blocks (www.codeblock.org). Sul portale PACS si trovano informazioni su Linux e su Windows. 6/1 Una nota linguistica Il corso viene dato in lingua Italiana. Ciononostante, spesso la terminologia in lingua Inglese è di uso comune (e talvolta più precisa). Si farà quindi spesso uso di quest’ultima (cercando comunque di fornire anche il corrispondente termine in Italiano). I libri di testo (comprese le note del corso) sono in Inglese. 7/1 8/1 Una possibile definizione Il calcolo scientifico è la disciplina che permette di riprodurre efficacemente su un calcolatore un fenomeno o processo descritto da un opportuno modello matematico, cercando di massimizzare l’efficienza su una particolare architettura e ridurre gli errori introdotti dal calcolo (errori di arrotondamento). 9/1 Dalla realtà al computer Fenomeno Fisico Osservazione Sperimentale Modello Concettuale Modello Matematico Leggi di Conservazione Equazioni Differenziali Modelli Statistici Programmazione 00 11 00 11 000 111 Linguaggio di programmazione Implementazione dell’algoritmo Strutture dati Ottimizzazione Analisi Buona posizione Proprietà della soluzione Soluzione analitica Analisi Numerica Metodi di discretizzazione Stima dell’errore Analisi degli algoritmi PostProcessing Visualizzazione Analisi dei risultati CALCOLO SCIENTIFICO 10/1 Dall’algoritmo al risultato CODIFICA Algoritmo Risultati POST PROCESSING COMPILAZIONE Programma Sorgente Codice Macchina 11/1 Implementazione di un linguaggio I Interpretato Le istruzioni (comandi) vengono processate ’sequenzialmente’ e ’tradotte’in codice macchina. Esempio: BASIC, MATLAB, Python.Semplicità di programmazione e debugging I Compilato L’intero codice sorgente viene tradotto in codice macchina, creando un eseguibile. Esempi: C++, FORTRAN. Prestazioni migliori. Più difficile il debugging. I Semicompilato Il codice sorgente o parte di esso viene tradotto in un codice intermedio (byte-code) indipendende dalla architettura. Quest’ultimo viene poi interpretato “run-time” da una “macchina virtuale” (virtual machine). Esempio: Java. 12/1 Implementazione di un linguaggio I Interpretato Le istruzioni (comandi) vengono processate ’sequenzialmente’ e ’tradotte’in codice macchina. Esempio: BASIC, MATLAB, Python.Semplicità di programmazione e debugging I Compilato L’intero codice sorgente viene tradotto in codice macchina, creando un eseguibile. Esempi: C++, FORTRAN. Prestazioni migliori. Più difficile il debugging. I Semicompilato Il codice sorgente o parte di esso viene tradotto in un codice intermedio (byte-code) indipendende dalla architettura. Quest’ultimo viene poi interpretato “run-time” da una “macchina virtuale” (virtual machine). Esempio: Java. 12/1 Implementazione di un linguaggio I Interpretato Le istruzioni (comandi) vengono processate ’sequenzialmente’ e ’tradotte’in codice macchina. Esempio: BASIC, MATLAB, Python.Semplicità di programmazione e debugging I Compilato L’intero codice sorgente viene tradotto in codice macchina, creando un eseguibile. Esempi: C++, FORTRAN. Prestazioni migliori. Più difficile il debugging. I Semicompilato Il codice sorgente o parte di esso viene tradotto in un codice intermedio (byte-code) indipendende dalla architettura. Quest’ultimo viene poi interpretato “run-time” da una “macchina virtuale” (virtual machine). Esempio: Java. 12/1 Il processo di compilazione in C(++) - semplificato - Compilazione prog.cc g++ −c prog.cc Linking prog.o g++ −o prog prog.o −lblas −lX Preprocessing header (#include) files prog.hpp other headers Esecuzione prog prog check librerie statiche libblas.a Loader librerie dinam. libX.so system libraries Il comando g++ -o prog prog.cc esegue i passi di compilazione e linking in sequenza. 13/1 Il processo di compilazione in C(++) - semplificato - Compilazione prog.cc g++ −c prog.cc Linking prog.o g++ −o prog prog.o −lblas −lX Preprocessing header (#include) files prog.hpp other headers Esecuzione prog prog check librerie statiche libblas.a Loader librerie dinam. libX.so system libraries Il comando g++ -o prog prog.cc esegue i passi di compilazione e linking in sequenza. 13/1 Tipi di programmazione Programmazione Procedurale. I dati vengono elaborati da procedure (comandi o funzioni) che ne modificano lo stato operando (tipicamente) in modo sequenziale. Programmazione ad oggetti. I dati sono encapsulati in strutture apposite (classi). Si opera sui dati tramite metodi della classe. L’accesso diretto ai dati è normalmente proibito. Programmazione generica. Si opera su tipi di dati differenti (ma che soddisfano pre-requisiti opportuni) usando la stessa interfaccia. 14/1 Tipi di programmazione Programmazione Procedurale. I dati vengono elaborati da procedure (comandi o funzioni) che ne modificano lo stato operando (tipicamente) in modo sequenziale. Programmazione ad oggetti. I dati sono encapsulati in strutture apposite (classi). Si opera sui dati tramite metodi della classe. L’accesso diretto ai dati è normalmente proibito. Programmazione generica. Si opera su tipi di dati differenti (ma che soddisfano pre-requisiti opportuni) usando la stessa interfaccia. 14/1 Tipi di programmazione Programmazione Procedurale. I dati vengono elaborati da procedure (comandi o funzioni) che ne modificano lo stato operando (tipicamente) in modo sequenziale. Programmazione ad oggetti. I dati sono encapsulati in strutture apposite (classi). Si opera sui dati tramite metodi della classe. L’accesso diretto ai dati è normalmente proibito. Programmazione generica. Si opera su tipi di dati differenti (ma che soddisfano pre-requisiti opportuni) usando la stessa interfaccia. 14/1 Programmazione procedurale Esempio in MATLAB A=gallery(’poisson’,100); b=ones(100,1); [L,U]=lu(A) y=L\b; x=U\y; 15/1 Programmazione ad oggetti Esempio in C++ class Matrix{public: Matrix(string filename); Vector solve(Vector const & b); private: double * dati;} ... Matrix A(“file.dat”);Vector b; Vector x; x=A.solve(b); .. Non si può accedere direttamente a dati. 16/1 Programmazione ad oggetti Esempio in C++ class Matrix{public: Matrix(string filename); Vector solve(Vector const & b); private: double * dati;} ... Matrix A(“file.dat”);Vector b; Vector x; x=A.solve(b); .. Non si può accedere direttamente a dati. 16/1 Programmazione generica Esempio in C++ template<class T> T sqrt(T& x); ... int main(){ float x; int y; complex z; flot rx; int ry, complex rz; rx=sqrt(x); // usa sqrt<float> ry=sqrt(y); // usa sqrt<int> rz=sqrt(z); // usa sqrt<complex> ...} La funzione sqrt<T>(T &) si applica a ogni tipo T purchè esso soddisfi opportuni prerequisiti stabiliti dal programmatrore (p. es. x > 0 per dati float e int). 17/1 Programmazione generica Esempio in C++ template<class T> T sqrt(T& x); ... int main(){ float x; int y; complex z; flot rx; int ry, complex rz; rx=sqrt(x); // usa sqrt<float> ry=sqrt(y); // usa sqrt<int> rz=sqrt(z); // usa sqrt<complex> ...} La funzione sqrt<T>(T &) si applica a ogni tipo T purchè esso soddisfi opportuni prerequisiti stabiliti dal programmatrore (p. es. x > 0 per dati float e int). 17/1 Programmazione generica Un secondo Esempio template<class T> class Vector {...} class triangle {...} int main(){ Vector<float> a;// Un vettore di float Vector<triangle> b;// Un vettore di triangle ... triangle t=b[2]; ...} 18/1 Il linguaggio C++ I Supporta solo la modalità di compilazione (linguaggio compilato) I È una estensione del linguaggio C, quindi supporta la programmazione procedurale I Supporta inoltre la programmazione ad oggetti e la programmazione generica. I Compilatori facilmente disponibili (e spesso gratuiti). I È il 3o linguaggio più utilizzato nel mondo (statistiche Settembre 2010). I È un linguaggio complesso e l’utilizzo nel calcolo scientifico richiede attenzione per evitare degradazione d’efficienza. I È costantemente aggiornato: è appena uscito il nuovo standard (C++11). 19/1 Utilizzo del C++ () Popolarita‘ dei linguaggi di programmazione langpop.com 2010 Java C C++ PHP Javascript Python C# Perl Pascal Fortran 20/1 Alternative per il calcolo scientifico I I I I I FortranXX. Programmazione procedurale. Fortran90 ha introdotto i moduli e la allocazione dinamica della memoria. Permette di implementare una programmazione “quasi” ad oggetti. Estremamente efficiente per le operazioni matematiche. Java. Lo sviluppo di applicazioni numeriche in Java è limitato principalmente a scopi didattici o divulgativi (web computing). C. La maggior parte dei codici commerciali di calcolo scientifico è scritta in questo linguaggio. python Utilissimo per interfacce. PyNum è un ambiante che rende disponibile numerose funzionalità di calcolo scientifico. Il suo uso nel calcolo scientifico è in forte crescita per la sua flessibilità. Matlab Supporta programmazione ad oggetti e può essere compilato. Il suoi cloni “free software” Octave e Scilab sono oramai valide alternative. 21/1 la struttura del linguaggio User Libraries Core Language o <i str e <vector> h> am > umfpack.h libumfpack.a libblas.so at External Libraries STANDARD LIBRARY m <c fem.h libfem.a tr <s ing > Il linguaggio è estensibile: al nucleo rappresentato dalle istruzioni condamentali (if, while,...) si aggiungono i moduli della standard libray e quelli eventualmenti forniti dall’utente o da altre librerie 22/1 la struttura del linguaggio User Libraries Core Language <i os tre <vector> h> am > umfpack.h libumfpack.a libblas.so at External Libraries STANDARD LIBRARY m <c fem.h libfem.a <s g> trin Nel codice l’estensione è riconoscibile dalla presenza della direttiva #include header_file. La lista completa degli header files delle standard library si trova a pag. 125 del libro di testo (un estratto e‘ reperibile sul sito). 22/1 C e C++ header files Gli header files di sistema dei C++ introducono nomi e funzioni nello spazio di nomi (namespace) std. Quindi per richiamarli occorre o usare il full qualified name (nome qualificato) std::xxx o usare il comando using: #i n c l u d e <cmath> // i n t r o d u c e s q r t ... double a=s t d : : s q r t ( 5 . 0 ) ; // f u l l q u a l i f i e d name ... using std : : s q r t ; ... double c=s q r t ( 2 ∗ a ) ; // non s e r v e s t d : : 23/1 esempio_ch1 #include <iostream> // include il modulo per l’i/o // della libreria standard int main() { using namespace std; \\rende visibili i nomi della STL int n, m; // n e m sono 2 interi cout « ’’Dammi due interi:’’ « endl; cin » n » m; if (n > m) { int temp = n; n = m; m = temp; } double sum = 0.0; for (int i = n; i <= m; i++) sum += i;// sum += i significa sum = sum + i; cout « ’’Somma=’’ « sum « endl; } 24/1 la direttiva “include” #include<iostream> using namespace std; Il linguaggio C++ consiste in un insieme di istruzioni di base (core language) e “estensioni” le cui definizioni possono essere richiamate con in comando #include<XXX> dove XXX è un header file. In particolare la libreria standard (Standard template library o STL) mette a disposizione il modulo iostream per l’i/o, che definisce (tra l’altro) std::cin e std::cout per l’i/o da tastiera/terminale. using namespace std; permette di omettere il qualificatore (scope qualifier) std:: ai nomi di introdotti dal “modulo” iostream. 25/1 Il programma principale int main() { .... for (int i = n; i <= m; i++) a[i]=i; } Per produrre un eseguibile il sorgente del programma deve contenere uno e un solo main() (programma principale o main program). In C++ il main ritorna sempre un intero (int) e può prendere in ingresso due argomenti (si veda l’esercitazione) oppure nessuno, come in questo caso. Le istruzioni (statement) terminano sempre con il punto e virgola (;) che funge da separatore. Non vi è un formato predefinito, a differenza di Python o del Fortran77. 26/1 Le variabili int n, m; float x, y; Triangle t; ... for (int i = n; i <= m; i++) I tipi si dividono in due gruppi: i tipi nativi del linguaggio (in-built) e quelli definiti dal programmatore (user-defined). Tra i tipi nativi, il tipo int identifica una variabile che memorizza un valore intero con segno. A differenza del Matlab le variabili devono sempre essere dichiarate e (a differenza del Fortran) possono essere dichiarate ovunque (ma prima di essere utilizzate). 27/1 Identificatori Un nome di variabile (o di funzione) deve essere un valido identificatore. Un identificatore in C++ è una successione di caratteri alfanumerici in cui il primo carattere è alfabetico. È ammesso il carattere underscore (_), anche come primo carattere. Il linguaggio distingue lettere maiuscole da minuscole: pippo è un identificatore diverso da Pippo. Un identificatore NON può essere uguale a una parola chiave (keyword). La lista delle parole chiavi del C++ (in tutto 74) è contenuta a pag. 20 del libro di testo. 28/1 Oggetti, variabili, espressioni Un oggetto (object) è un area di memoria capace di memorizzare un valore di un certo tipo. Una variabile (variable) è un oggetto con un nome anche se talvolta si indica con unnamed variable un oggetto senza nome. Un operatore è una particolare istruzione che combina uno o due argomenti (in un caso speciale tre) e ritorna un risultato (es. il + in a+b;). Una espressione è una combinazione di oggetti, espressioni ed operatori che producono un valore di un certo tipo. Per esempio a+5 è una espressione che, se a è un int produce un valore di tipo int . Una espressione è composta se è formata dalla combinazione di sottoespressioni, per esempio a + b∗c è formata da a e b∗c. Una espressione è costante se il suo valore non può essere modificato (in generale). 29/1 L’input/output (formatted) #include <iostream> .. std::cout « ’’Dammi due interi:’’ « std::endl; std::cin » n » m; Uno stream può essere immaginato come una sorgente o un “pozzo” di dati (tipicamente caratteri), usualmente associati a lettura/scrittura da file o teminale. La standard template library attraverso il modulo iostream mette a disposizione 4 stream per i/o da/su terminale: std::cin std::cout std::cerr std::clog Standard Standard Standard Standard Input (buffered) Output (buffered) Error (unbuffered) Logging (default è = cout) 30/1 Tipi di variabili I principali tipi nativi forniti dal linguaggio sono float Numero reale in singola precisione (4 bytes) double Numero reale in doppia precisione (8 bytes) long double Numero reale in precisione estesa (8 o 16 bytes) int Intero con segno (tip. 4 bytes) short int Intero corto con segno (tip. 1 byte) long int Intero esteso con segno (tip. 8 bytes) bool Variabile logica (impl. dip.) char Carattere (1 byte) unsigned Prefisso per interi positivi 31/1 Numeri a virgola mobile (floats) Tipicamente le variabili float obbediscono allo standard IEEE. Il C++ permette però di controllare i limiti numerici ed in particolare di trovare l’epsilon macchina per i vari tipi a virgola mobile. Si deve usare il modulo della Standard Template Library <limits>. #include <limits> ... std::numeric_limits<float>::epsilon(); std::numeric_limits<float>::max(); Si veda anche l’esempio in examples/numeric_limits. 32/1 Rappresentazione dei numeri a virgola mobile Un sistema di numerazione floating point (normalizzato) F è definito dai numeri reali rappresentabili nella forma y = ±m × β e−t β m base (tipicamente 2) mantissa, 0 ≤ m ≤ β t − 1 t e precisione esponente, emin ≤ e ≤ emax Analogamente y = 0.d1 d2 ...dt × β e d1 6= 0, 0 ≤ di ≤ β − 1 I numeri rappresentabili in un computer appartengono a F ∗ = F ∪ Fs dove Fs è il set dei numeri subnormali, della forma y = ±m × β emin −t , 0 < m < β t−1 33/1 Arrotondamento Un numero reale x ∈ range(F ∗ ) è rappresentato nel computer da x̂ = fl (x) ∈ F ∗ e |x − x̂|/x è l’errore di arrotondamento (relativo). Se x ∈ range(F ∗ ) allora x̂ = fl (x) ∈ F e x − x̂ = ∆, x 1 con |∆| ≤ u = β 1−t 2 u è detto roundoff unit ed è legato all’epsilon macchina M da 1 u = M . Si ricorda che M è il minimo numero positivo per cui 2 fl (1 + M ) 6= 1. Nota: 0 ∈ F . 34/1 Aritmetica IEEE Lo standard IEEE 754 è stato definito nel 1985 e definisce un sistema aritmetico a virgola mobile oramai comunemente implementato nei normali microprocessori. Definisce due tipi principali di floating point: singola precisione (float) e doppia precisione (double). Type Size t e u Range float double 32 64 23 + 1 52 + 1 8 11 2−24 2−53 10±38 10±308 I numeri rappresentano i bits usati. Soddisfa inoltre il modello standard: se x, y ∈ F allora fl (x op y ) = (x op y )(1 + δ) |δ| ≤ u, op = + − ×, / 35/1 Numeri speciali Lo standard IEEE stabilisce che fl (x) = 0 se|x| < Fmin (UNDERFLOW) Fmin ∈ F ∗ essendo il minimo numero positivo rappresentabile. fl (x) = sign(x)Inf se|x| > Fmax (OVERFLOW) Fmax ∈ F essendo il massimo numero rappresentabile. Inoltre x/0 = ±Inf se x 6= 0, Inf + Inf = Inf e x/Inf = 0 se x 6= 0. NaN indica il risultato di una operazione illecita (0/0, log(0) etc). Inoltre x op NaN = NaN, Inf − Inf = NaN e 0/Inf = NaN. 36/1 Forward and backward error Sia f : R → R e fˆ : F → F l’equivalente espressione operante su numeri a virgola mobile. Sia y = f (x) e ŷ = fˆ(x̂). L’analisi del forward error si propone di trovare un δ tale che |y − ŷ | ≤δ |y | Il backward error (relativo) ∆ e definito da ŷ = f (x(1 + Delta)). Se f ∈ C 2 ha che y − ŷ = c(x)∆ + O((∆x)2 ) y |c(x)| è detto numero di condizionamento. Normalmente ne viene fornita una stima δ ≤ C (x)|∆| 37/1 Un esempio: la cancellazione Consideriamo y = f (a, b) = a − b e ŷ = f (a(1 + ∆), b(1 + ∆)). Dopo semplici calcoli | |a∆ − b∆| |a| + |b| |a| + |b| y − ŷ |= ≤ ∆⇒C = y |a − b| |a − b| |a − b| Abbiamo che C → ∞ quando |a − b| → 0: sottrazione di due quantità molto vicine causa un forte errore di arrotondamento. E‘ quindi una operazione da evitare! 38/1 Calcolo della radice di una quadratica Gli zeri di ax 2 + bx + c possono essere calcolati con la formula classica √ −b ± b2 − 4ac x1,2 = 2a √ Ma cosa succede se b >> 4ac o se b ' 2 ac? Si veda examples/QuadraticRoot 39/1 Ritorno al linguagggio C++: i char char p=’A’; char q=’\n’; Un carattere è individuato da gli apici singoli. Vi sono dei caratteri speciali, quali ’\n’. Quest’ultimo indica andare a capo (carriage return). Una variabile char contiene un singolo carattere. Vedremo in seguito come trattare le stringhe di caratteri. 40/1 bool bool l=true; bool s;int a=5; s= (a==5); //s è pari a true La variabile bool può assumere solo due valori, vero o falso, indicati dalle due parole riservate true e false. Si può usare in alternativa 1 e 0. Le variabili bool vengono usualmente utilizzate per memorizzare il risultato di espressioni logiche. 41/1 Principali operazioni i=g+g; La somma (+), moltiplicazione (*) etc. sono definite per tutti i tipi numerici. a=pow(3.5,4). (3.5)4 . Richiede <cmath>. a*=5. Equivalente a a=a*5. Ma più efficiente. In generale op ha anche l’equivalente op= ++i e i++. Preincremento e post-incremento. Entrambe pari a i = i + 1 ma la seconda ritorna il valore di i prima dell’incremento. --i e i--. Come sopra ma decrementando di 1. Operatori logici: &&, || e !. Test logici: ==, !=, <, <= etc. 42/1 Dichiariazione e definizione di variabile Questi termini sono di suo comune e vengono spesso confusi. I Dichiarazione. Associa un tipo a un nome di variabile. Permette al compilatore di determinare la dimensione di un oggetto di quel tipo. I Definizione. Indica come una variabile viene costruita (e ne definisce le sue funzionalità). Rilevante per tipo definiti dall’utente. I Istanza. Momento in cui una variabile viene costruita in memoria e viene creato un oggetto. I Inizializzazione. Assegnazione di un valore iniziale a una variabile (spesso coincide temporalmente con l’istanza) I Assegnazione Attribuisce a una variabile già istanziata in precedenza un valore, usualmente attraverso l’operatore di assegnazione =. 43/1 Inizializzazione di variabili Per i tipi nativi la dichiarazione implica anche l’istanzazione della variabile (a meno che non si usi la keyword extern) e l’inizializzazione (a meno che la variabile non sia membro di una struct o classe) extern int z; Dichiarazione int a; Dichiarazione (e istanziazione/inizial.) float b=3.14; Inizializzazione float c(3.14); Inizializzazione float e=atan(1)*4; Inizializzazione long int=3L; Inizializzazione a=10; Assegnazione Nota: Non assumere mai che una variabile sia inizializzata automaticamente a un valore: inizializzatela sempre esplicitamente. 44/1 Alcune regole importanti • Una variabile deve sempre essere dichiarata prima di poter essere riferita in altre parti del programma. • Una variabile può essere dichiarata più volte, purchè la dichiarazione sia identica. • Una variabile può essere definita una sola volta. La variabile è accessibile solo dopo la sua definizione. • La definizione è anche una dichiarazione. 45/1 Unitd̀i compilazione Un programma C++ è normalmente contenuto in più file sorgente (source files), con estensione .cpp, o .cxx. Solo nei casi più semplici vi è un solo file. Un file sorgente può includere degli header files, tipicamente con estensione .hpp o .h. Un file sorgente insieme agli header files eventualmente inclusi, forma una compilation uniti (unità di compilazione). Il compilatore compila ciascuna unità di compilazione separatamente. Sarà poi il linker a mettere insieme le informazioni fornite da ciascuna unità di compilazione e produrre l’eseguibile. 46/1 Le istruzioni di controllo e i cicli if (n > m) { ...Blocco di istruzioni }... for (int i = n; i <= m; i++) sum+=i Il linguaggio prevede istruzioni di controllo: if{...}else{...} e di ciclo: for(){...}, do{...} while(). 47/1 Ciclo for for(init;test;update) { corpo (body) ..} 48/1 Ciclo for for(init;test;update) { corpo (body) ..} init è una istruzione che viene eseguita all’inizio del ciclo, tipicamente per inizializzare una variabile locale, per esempio int i=0. 48/1 Ciclo for for(init;test;update) { corpo (body) ..} test. Una espressione logica che viene eseguita all’inizio del corpo del ciclo. Se è vera le istruzioni nel corpo vengono eseguite. Es.: i<10. 48/1 Ciclo for for(init;test;update) { corpo (body) ..} update è una istruzione che viene eseguita alla fine del corpo del ciclo. Es.: ++i. 48/1 Ciclo for for(init;test;update) { corpo (body) ..} Le variabili definite nel ciclo sono locali al ciclo: i=90; for(int i=0;i<=10;++i) ... cout«i; // i qui vale 90 48/1 Conversioni tra variabili di tipo diverso int a=5; float b=3.14;double c,z; c=a+b c=double(a)+double(b) (conv. per costruzione) z=static_cast<double>(a) (conv. per casting) Il C++ prevede una serie di regole per la conversione implicita tra tipi “nativi”. È possibile anche indicare esplicitamente la conversione, come indicato sopra. Nota: È meglio, per la leggibilità del codice, usare sempre conversioni esplicite. 49/1 Conversioni particolari int a=5; char A=’A’ bool c=bool(a); non serve la conver. esplicita float b=3.14; c=b; c è pari a true a=A; a è il codice ASCII di ’A’ Vi è la regola che ogni valore non nullo di variabili intere e a virgola mobile e ogni carattere diverso dal carattere nullo sono convertiti in true. Analogamente, in una espressione logica, il puntatore nullo viene convertito a false, mentre ogni altro puntatore a true. Nota: Questa regola si rivelerà molto utile. 50/1 Conversione per casting statico La conversione per casting statico è più efficiente perchè non crea temporanei: float a=10; double b=static_cast<double>(a); La variabile a è reinterpretata come double e il valore viene usato per inizializzare b float a=10; double b=double(a); Una variabile temporanea di tipo double viene costruita copiando da a a e quindi assegnata a b. 51/1 Due modalità per i commenti i n t a =0; // Commento i n l i n e a b=s t d : : s q r t ( 5 . 0 ) ; /∗ Commento s u p i ù l i n e e È e r e d i t a t o d a l C ∗/ .... Esistono delle tecniche di documentazione (si veda il programma DoxyGen (che useremo negli esercizi)) che permettono di produrre automaticamente il manuale di riferimento processando il codice sorgente usando delle keyword particolari all’interno dei commenti. È importante commentare il codice e i commenti devono essere significativi, cioè aiutare chi utilizza il codice a comprenderne il funzionamento. 52/1 Lo scope (ambito di visibilità) Lo scope (ambito di visibilità) di una variabile (o una funzione) definisce la porzione di programma in cui la variabile (o funzione) è visibile, cioè può essere utilizzata usando il suo nome. Una variabile non in scope può essere tuttavia accessibile usando il suo full qualified name attraverso lo scope resolution operator :: (ne parleremo più in dettaglio più avanti...). .. int x=10;//variabile globale int main(){ int x=20;//variabile locale cout « x« ::x; ... } 53/1 • Ogni parte di programma racchiusa tra parentesi graffe identifica uno scope separato. Eccezione: nella istruzione if {...} else { ...} le due (o più) coppie di parentesi graffe identificano lo stesso scope. • Gli scope sono annidati tra loro, e uno scope “interno” eredita le definizioni delle variabili di quello esterno. • Il nome di una variabile (o funzione) dichiarata in uno scope nasconde una variabile o funzione dichiarata in uno scope più esterno (name hiding o shading). • Lo scope più esterno è detto globale. • L’operatore :: (scope resolution operator) permette di accedere a variabili/funzioni globali. • Le istruzioni di ciclo for, while etc. definiscono uno scope, che include la parte che concerne il test. • Una variabile istanziata in uno scope è locale a tale scope. • Una variabile viene distrutta alla termine dello scope in cui è stata istanziata (a meno che non sia dichiarata static, come si vedrà in seguito). 54/1 Visibilità ed esistenza Il termine scope riferito a un oggetto spesso viene usato anche con il significato di ambito di esistenza. L’ambito di esistenza di un oggetto è la porzione del codice in cui l’oggetto esiste. Inizia al momento della istanza e termina quando viene chiamato il distruttore dell’oggetto. Manterremo l’ambiguità del temine che dovrà essere interpretato correttamente dal contesto. 55/1 Array (monodimensionali) double a[5]={1.6,2.7,3.4,7,8}; b=a[0]+a[3];//b vale 8.6 int c[2]; c[0]=7; c[1]=8; In C++ gli array vengono indirizzati a partire da 0 usando l’operatore []. Un’alternativa: usare i vector<> della Standard Template Library #include <vector> vector<double> a; a.push_back(1.6)\1.6 -> a[0] 56/1 vector<T> I vector della standard library sono un esempio di template di cui parleremo estensivamente in una prossima lezione. Sono dei contenitori generici che implementano l’indirizzamento diretto tramite l’operatore []. #include <vector> using std::vector vector<unsigned int> a(5); a[2]=9; ... a[0]+=a[2]; ... 57/1