Comments
Transcript
Lezione sui tipi di base in C++ - ICAR-CNR
Il main program • Ogni programma in C++, per essere eseguibile, deve contenere una funzione main() da cui l’esecuzione comincerà • main() deve avere un tipo (decidere quale è compito del programmatore). Regola generale è che main() ritorni un intero, a significare il return code dell’applicazione int main() { // il piu` semplice programma in C++ return 0; } 1 I/O: lettura e scrittura • Non esiste nel C++ nativo. Si usa: iostream #include <iostream> – Gli operatori << e >> sono usati per definire la direzione del flusso – cin, cout e cerr rappresentano lo standard input, output e error del programma #include <iostream> using namespace std; int main() { cout << “Hello, world !” << endl; return 0; } direttiva al preprocessore end of line 2 Commenti • Esistono due tipi di commento in C++ – inline: const int Ntries; // questo e` un commento inline // il resto della linea e’ trattato come un commento – multiline (come in C): const int Ntries; /* questo e` un commento multiline: tutto viene trattato come un commento fino a quando il commento stesso non viene chiuso con uno */ – I due tipi possono essere usati indifferentemente, ma si raccomanda di usare l’inline (più semplice e meno ambiguo) 3 Tipi predefiniti in C++ • Sono definiti una serie di tipi numerici che permettono di rappresentare numeri interi, reali e caratteri int long float double long double unsigned int unsigned double char bool intero in singola precisione intero in doppia precisione reale in singola precisione reale in doppia precisione reale in precisione estesa intero senza segno reale senza segno in doppia precisione carattere singolo variabili logiche – char (un solo byte) viene normalmente usato per rappresentare interi inferiori a 256 – stringhe e numeri complessi sono implementati come tipi derivati 4 Tipi predefiniti in C++ (2) Costanti carattere Esempi di costanti 123 123 0x123 123l 123u ‘A’ 3.14f ‘1’ ‘\t’ 3.1415 3.1415L 300e-2 .03e2 interi costanti, decimale, ottale, esadecimale interi, long, unsigned caratteri, tab float, double, long double double, notazione esponenziale stringa costante boolean 30e-1 “Nome” true false ‘\a’ ‘\\’ ‘\b’ ‘\r’ ‘\”’ ‘\f’ ‘\t’ ‘\n’ ‘\0’ ‘\’’ ‘\v’ ‘\101’ ‘\x041’ alert backslash backspace carriage return double quote form feed tab newline carattere nullo single quote vertical tab 101 ottale, ‘A’ esadecimale, ‘A’ Stringhe costanti “” “nome” “una \”stringa\”” “una stringa \ su piu` linee” stringa nulla (‘\0’) ‘n’ ‘o’ ‘m’ ‘e’ ‘\0’ stampa: una “stringa” un \ alla fine della linea per continuare la stringa 5 Tipi predefiniti in C++ (3) OS OS OS 16 bit 32 bit 64 bit char[1] 8 8 8 int[1] 16 32 32 bool 16 32 32 short[1] 16 16 16 long[1] 32 32 64 float 32 32 32 double 64 64 64 long double 64 128 128 [1] Può essere unsigned 6 Identificatori • Un identificatore è composto da uno o più caratteri • Il primo carattere deve essere una lettera o un underscore. Caratteri successivi possono essere lettere, numeri o underscore const int Ntries; double _attempts; double 2A; // errore! • Non c’ è un limite in lunghezza, anche se alcuni sistemi si limitano a considerare i primi 31 caratteri • Gli identificatori che iniziano con un doppio underscore o con un underscore e una lettera maiuscola sono riservati ad usi di sistema • C++ e` case sensitive! 7 Keywords • Alcuni identificatori sono esplicitamente riservati al sistema (hanno un preciso significato in C++) e non possono essere usati asm auto bool break case catch char class const const_cast continue default delete do double dynamic_cast else enum explicit extern false float for friend goto if inline int long mutable namespace new keyword operator private protected public register reinterpret_cast return short signed sizeof static static_cast struct switch template this throw true try typedef typeid typename union unsigned using virtual void volatile wchar_t while 8 const • La keyword const viene utilizzata per dichiarare un oggetto costante Esempi di const const int N=100; double w[N]; const int vect[5]= {10,20,30,40,50}; N non puo` essere cambiato N usato come per dimensionare un vettore le componenti di vect non possono essere cambiate • In C le costanti vengono normalmente dichiarate usando il preprocessore #define N 100 – in questo caso N e` una costante senza tipo ed il preprocessore sostituisce N ovunque lo trovi nel programma, senza rispettare le regole di scope (da evitare) 9 Dichiarazione • Le dichiarazioni associano un significato ad un identificatore • in C++ ogni cosa deve essere dichiarata per poter essere usata const int i; double max(double r1,double r2); // la variabile i // la funzione max • Una dichiarazione è spesso anche una definizione. Per variabili semplici questo consiste nell’associare un valore alla variabile al momento della dichiarazione const double pi=3.1415926; double max(double r1, double r2) { return (r1>r2) ? r1: r2; } // definizione // dichiarazione // definizione di max 10 typedef • L’istruzione typedef viene utilizzata per creare un alias per tipi esistenti typedef int INTEGER; typedef int BOOLEAN; typedef void (*ptr_f)(); // // // // // per i nostalgici del fortran usato prima che bool venisse implementato ptr_f e` un puntatore ad una procedura (subroutine) • typedef NON può essere usato per implementare nuovi tipi, ma solo per definire un alias typedef mela frutto; // compila soltanto se mela // e` gia` stata definita 11 Enumeratori • In C++ sono supportati tipi definiti dall’utente enum Color { red, green, blue }; Color screenColor = blue; Color windorColor = red; int n = blue; // valido Color c = 1; // errore enum Seme { cuori, picche, quadri, fiori }; 12 Scope • Le variabili possono essere dichiarate e definite quasi ovunque in un programma in C++ • la visibilità (scope) di una variabile dipende da dove la variabile è stata dichiarata int func() { … const int n=50; for (int i=0;i<100;i++) { double r; ... } cout<<“n “<< n <<endl; cout<<“i “<< i <<endl; cout<<“r “<< r <<endl; … } // function scope // i e` locale // r e` locale // OK // errore! Ma... // errore! 13 Scope (2) • Attenzione! La stessa variabile può essere ridichiarata (con visibilità diversa). Questo è da evitare (se possibile) per non rendere il programma oscuro e a rischio di errore! int i; int func() { int i=50; // file (global) scope // function scope, nasconde // la i a file scope for (int i=0;i<100;i++) // block scope. Nasconde // la i a function scope { int i; // questo e` un errore... ... } cout<<“i “<< i <<“ “<< ::i <<endl; ... } Scope resolution operator 14 Operatori Espressioni Aritmetiche Auto-incremento e decremento k k k k = = = = ++j; j++; --j; j--; +w a/b a+b a-b i%2 a=3; Espressione j=j+1; k=j; k=j; j=j+1; j=j-1; k=j; k=j; j=j-1; bit-wise ~i; i&j; i|j i^j i<<n i>>n -i a*b significato Commento piu` e meno unari moltiplicazione, divisione, modulo addizione e sottrazione binarie assegnazione Operatori relazionali < > <= >= == != ! && || Complemento bit a bit AND bit a bit OR bit a bit XOR bit a bit shift a sinistra di n pos. shift a destra di n pos. minore di maggiore di minore o uguale maggiore o uguale uguale diverso Negazione unaria and logico or logico Fortran .LT. .GT. .LE. .GE. .EQ. .NE. .NOT. .AND. .OR. 15 Espressioni di assegnazione • Le espressioni di assegnazione sono valutate da destra a sinistra a = j++; j viene incrementato ed il risultato assegnato ad a • Le assegnazioni multiple sono permesse a = b = c = d = 100; • alcuni operatori di assegnazione combinano assegnazione ed altri operatori a *= b; a -= b; // equivale ad // equivale ad a = a*b; a = a-b; • Assegnazioni possono essere fatte all’interno di espressioni aritmetiche a = b + ( c = 3 ); // equivale a c=3; a=b+c; 16 Statements Statement C++ vuoto espressione composto ; j=j+k; { . . . . } goto if goto label; if (p==0) cerr<<“error”; if (x==y) cout<<“the same”; else cout<<“different”; for (j=0;j<n;j++) a[j]=0; while (i != j) i++; do y=y-1; while (y>0); break; continue; if-else for while do-while break continue commenti usato in funzioni, if.. Costituisce un blocco da non usarsi un solo branch due branch le dichiarazioni sono permesse 0 o piu` iterazioni 1 o piu` iterazioni esce dal blocco prossima iterazione 17 Statements (2) Statement switch C++ dichiarazione switch (s) { case 1: ++i; case 2: --i; default: ++j; }; int i=7; try try {. . . .} label error: cerr<<“Error!”; return x*x*x; return commenti si deve usare break per evitare di cadere nei casi successivi e aggiungere un caso di default alla fine della lista in un blocco, file o namespace usato per trattare le eccezioni usato con goto valore di ritorno di una funzione 18 Statement composti • Uno statement composto in è costituito da una serie di statement contenuti fra parentesi graffe • Usato normalmente per raggruppare istruzioni in un blocco (if, for, while, do-while, etc.) • Il corpo di una funzione è sempre uno statement composto • La dichiarazione di una variabile può avvenire ovunque all’interno di un blocco, in questo caso lo scope della variabile sarà il blocco stesso • Ovunque si possa usare uno statement singolo si può definire un blocco 19 if • Attenzione all’uso di = e == if (i=1) {. . . .} // questo e` sempre vero!!! • Nel dubbio, usare sempre un blocco… if (i != 0) a++; a/=i; // possibile divisione per 0 // mancano delle {}? • Attenzione agli else! if (i == 0) if (a<0) { // possibile divisione per 0 cerr<<“a e` negativo!”; } else b=a/i; 20 while e do-while • La forma generale di un while è : while (condizione) statement; • Lo statement verrà eseguito fino a quando la condizione verrà verificata (true). A seconda del volore della condizione, lo statement verrà eseguito zero o più volte • la sintassi di un do-while è invece: do statement; while (condizione); • Lo statement verrà quindi eseguito almeno una volta 21 break e continue • break e continue sono utilizzati nei loop per saltare alla fine del loop o fuori dal loop stesso int i,n=0; int a[100]; cin>>i; // leggo il valore di i while (1) // loop infinito { if (i<0) break; if (n>=100) continue; a[n]=i; n++; // continue salta qui } // break salta qui • break e continue possono solamente essere utilizzati nel corpo di un for, while o dowhile. break e` anche usato negli switch 22 switch • Lo switch è uno statement condizionale che generalizza lo if-else switch (condizione) (statement); • lo statement è generalmente composito e consiste di diversi case e, opzionalmente, di un default switch (n) { case 0: cout<<“ n e` nullo”<<endl; break; case 1: case 3: case 5: case 7: case 9: cout<<“ n e` dispari”<<endl; break; case 2: case 4: case 6: case 8: case 10: cout<<“ n e` pari”<<endl; break; default: cout<<“ n non e` compreso tra 0 e 10”<<endl; } 23 switch (2) • Non si puo` dichiarare una variabile in uno dei case switch (k) case 0: int . . case 1: . . } { j=0; . // Illegale! Errore! . • … ma si puo` creare una variabile locale definendo uno statement composto... switch (k) case 0: { int . . } case 1: . . } { j=0; . // OK, questo compila . 24 L’operatore ? • L’operatore ? e` l’unico esempio di operatore ternario in C++ expr1 ? expr2 : expr3; – Equivale a: if(expr1) expr2; else expr3; – Esempio: double max(double a, double b) { double max = (a>b) ? a : b; return max; } 25 Funzioni matematiche • In C++ non esistono funzioni predefinite #include <iostream> #include <cmath> using namespace std; int main() { double r, theta, phi; cin >> double double double r x y z cmath.h definisce sin, cos, ... >> theta >> phi ; = r * sin( theta ) * sin( phi ); = r * sin( theta ) * cos( phi ); = r * cos( theta ); return 0; } • Potenze: pow(b,exp) **endl; ) cout << x << (non “, “ <<siy può << “, usare “ << z << 26 Array • Sono supportati gli array di dimensione fissa int main() { int x[10]; for ( int i = 0; i < 10, i++ ) x[i] = 0; double m[5][5]; for ( int i = 0; i < 5; i++ ) for ( int j = 0; j < 5; j++ ) m[i][j] = i * j; return 0; } • Inizializzazione: int x[] = { 1, 2, 3, 4 }; char[] t = { ‘C’, ‘i’, ‘a’, ‘o’, ‘\0’ }; char[] s = “Ciao”; int m[2][3] = { {11, 12, 13}, {21, 22, 23} }; • L’indice va da 0 a n-1. Usare un indice maggiore di n-1 può causare un crash. 27 Esempio con gli arrays • Moltiplicazione fra matrici: int main() { const int DIM=3; float m[DIM][DIM], m1[DIM][DIM], m2[DIM][DIM]; // Assumiamo che m1 ed m2 vengano riempiti qui... // Moltiplicazione: for (int i=0; i<DIM; i++) { for (int j=0; j<DIM; j++) { float sum=0; for (int k=0; k<DIM; k++) sum += m1[i][k] * m2[k][j]; m[i][j] = sum; } } return 0; } 28 Puntatori • Riferimento ad una locazione di memoria #include <iostream> using namespace std; ptr j 24 12 int main() { int *ptr = &j; int j = 12; cout << *ptr << endl; j = 24; cout << *ptr << endl; cout << ptr << endl; return 0; 12 24 0x7b03a928 } indirizzo di memoria 29 Puntatori • Puntatore nullo #include <iostream> ptr j 12 using namespace std; int main() { int j = 12; int *ptr = 0; cout << *ptr << endl; // crash ! return 0; } Segmentation violation (core dumped) 30 Puntatori e array • In C gli array sono trattati come puntatori X[0] 1.5 X[1] 2.5 x X+1 int main() { float x[5]; int j; for (j = 0; j < 5; j++) x[j] = 0; float *ptr *ptr = *(ptr+1) = *(ptr+3) = X[2] 0.0 X[3] 3.5 X[4] 0.0 X+3 = x; 1.5; // x[0] = 1.5 2.5; // x[1] = 2.5 3.5; // x[3] = 3.5 } 31 Puntatori: allocazione dinamica • Riferimento ad una locazione di memoria #include <iostream> ptr using namespace std; 12 int main() { int *ptr = new int; *ptr = 12; cout << *ptr << endl; delete ptr; return 0; • Attenzione: – Non usare delete}fa accumulare locazioni di memoria inutilizzate (memory leak) – Utilizzare puntatori prima del new o dopo il delete causa il crash del programma 32 Puntatori: allocazione dinamica • Riferimento a più locazioni di memoria #include <iostream> using namespace std; int main() { int *ptr = new int[3]; ptr[0] = 10; ptr[1] = 11; ptr[2] = 12 delete [] ptr; return 0; } ptr 10 11 12 33 new e delete • Gli operatori new and delete vengono utilizzati per allocazione/deallocazione di memoria dinamica – la memoria dinamica (heap), è un’area di memoria libera provvista dal sistema per quegli oggetti la cui durata di vita è sotto il controllo del programmatore operatore new int *i=new int; char *c=new char[100]; int *i=new int(99); char *c=new char(‘c’); int *j=new int[n][4]; commenti alloca un alloca un caratteri alloca un alloca un alloca un intero, returna il puntatore array (stringa) di 100 intero e lo inizializza a 99 carattere inizializzato a c array di puntatori ad intero • new riserva la quantità necessaria di memoria richiesta e ritorna l’indirizzo di quest’area 34 new e delete (2) • L’operatore delete è usato per restituire una certa area di memoria (allocata con new) allo heap • Ogni oggetto allocato con new deve essere distrutto con delete se non viene piu` utilizzato, altrimenti l’area di memoria che esso occupata non potra` piu` essere ri-allocata (memory leak) • L’argomento di delete è tipicamente un puntatore inizializzato preventivamente con new operatore delete delete ptr; delete p[i]; delete [] p; commenti distrugge un puntatore ad un oggetto distrugge l’oggetto p[i] distrugge ogni oggetto di tipo p 35 new e delete (3) • Attenzione – la dimensione dello heap non e` infinita – l’allocazione con new può fallire, nel qual caso new restituisce un puntatore nullo o suscita un’eccezione. Nel caso di allocazione di memoria importante bisogna verificare che l’operazione abbia avuto successo prima di usare il puntatore – ogni oggetto creato con new deve essere distrutto con delete, ogni oggetto creato con new [] deve essere distrutto con delete [] , queste forme NON sono intercambiabili 36