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; }
I/O: lettura e scrittura Non esiste nel C++ nativo. Si usa: 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> #include <iostream> cout << “Hello, world !” << endl; using namespace std; int main() { return 0; } direttiva al preprocessore end of line
Commenti Esistono due tipi di commento in C++ inline: multiline (come in C): I due tipi possono essere usati indifferentemente, ma si raccomanda di usare l’inline (più semplice e meno ambiguo) const int Ntries; // questo e` un commento inline // il resto della linea e’ trattato come un commento 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 */
Tipi predefiniti in C++ Sono definiti una serie di tipi numerici che permettono di rappresentare numeri interi, reali e caratteri char (un solo byte) viene normalmente usato per rappresentare interi inferiori a 256 stringhe e numeri complessi sono implementati come tipi derivati int intero in singola precisione long intero in doppia precisione float reale in singola precisione double reale in doppia precisione long double reale in precisione estesa unsigned int intero senza segno unsigned double reale senza segno in doppia precisione char carattere singolo bool variabili logiche
Tipi predefiniti in C++ (2) ‘\a’ alert ‘\\’ backslash ‘\b’ backspace ‘\r’ carriage return ‘\”’ double quote ‘\f’ form feed ‘\t’ tab ‘\n’ newline ‘\0’ carattere nullo ‘\’’ single quote ‘\v’ vertical tab ‘\101’ 101 ottale, ‘A’ ‘\x041’ esadecimale, ‘A’ Costanti carattere 123 123 0x123 interi costanti, decimale, ottale, esadecimale 123l 123u interi, long, unsigned ‘A’ ‘1’ ‘\t’ caratteri, tab 3.14f 3.1415 3.1415L float, double, long double 300e-2 .03e2 30e-1 double, notazione esponenziale “Nome” stringa costante true false boolean Esempi di costanti “” stringa nulla (‘\0’) “nome” ‘n’ ‘o’ ‘m’ ‘e’ ‘\0’ “una \”stringa\”” stampa: una “stringa” “una stringa \ un \ alla fine della linea su piu` linee” per continuare la stringa Stringhe costanti
Tipi predefiniti in C++ (3) OS 16 bit OS 32 bit OS 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
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 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! const int Ntries; double _attempts; double 2A; // errore!
Keywords Alcuni identificatori sono esplicitamente riservati al sistema (hanno un preciso significato in C++) e non possono essere usati keyword asm else operator throw auto enum private true bool explicit protected try break extern public typedef case false register typeid catch float reinterpret_cast typename char for return union class friend short unsigned const goto signed using const_cast if sizeof virtual continue inline static void default int static_cast volatile delete long struct wchar_t do mutable switch while double namespace template dynamic_cast new this
const La keyword const viene utilizzata per dichiarare un oggetto costante In C le costanti vengono normalmente dichiarate usando il preprocessore 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) const int N=100; N non puo` essere cambiato double w[N]; N usato come per dimensionare un vettore const int vect[5]= le componenti di vect non {10,20,30,40,50}; possono essere cambiate Esempi di const #define N 100
Dichiarazione Le dichiarazioni associano un significato ad un identificatore in C++ ogni cosa deve essere dichiarata per poter essere usata Una dichiarazione è spesso anche una definizione. Per variabili semplici questo consiste nell’associare un valore alla variabile al momento della dichiarazione const int i; // la variabile i double max(double r1,double r2); // la funzione max const double pi=3.1415926; // definizione double max(double r1, double r2) { // dichiarazione return (r1>r2) ? r1: r2; // definizione di max }
typedef L’istruzione typedef viene utilizzata per creare un alias per tipi esistenti typedef NON può essere usato per implementare nuovi tipi, ma solo per definire un alias typedef int INTEGER; // per i nostalgici del fortran typedef int BOOLEAN; // usato prima che bool venisse // implementato typedef void (*ptr_f)(); // ptr_f e` un puntatore ad una // procedura (subroutine) typedef mela frutto; // compila soltanto se mela // e` gia` stata definita
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 };
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; // function scope for (int i=0;i<100;i++) // i e` locale double r; // r e` locale ... } cout<<“n “<< n <<endl; // OK cout<<“i “<< i <<endl; // errore! Ma... cout<<“r “<< r <<endl; // errore!
Scope (2) Attenzione! La stessa variabile può essere ri-dichiarata (con visibilità diversa). Questo è da evitare (se possibile) per non rendere il programma oscuro e a rischio di errore! int i; // file (global) scope int func() { int i=50; // 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
Operatori Espressioni Aritmetiche Commento -i +w piu` e meno unari a*b a/b i%2 moltiplicazione, divisione, modulo a+b a-b addizione e sottrazione binarie a=3; assegnazione Espressioni Aritmetiche Commento k = ++j; j=j+1; k=j; k = j++; k=j; j=j+1; k = --j; j=j-1; k=j; k = j--; k=j; j=j-1; Auto-incremento Espressione e decremento < minore di .LT. > maggiore di .GT. <= minore o uguale .LE. >= maggiore o uguale .GE. == uguale .EQ. != diverso .NE. ! Negazione unaria .NOT. && and logico .AND. || or logico .OR. Operatori relazionali Fortran ~i; Complemento bit a bit i&j; AND bit a bit i|j OR bit a bit i^j XOR bit a bit i<<n shift a sinistra di n pos. i>>n shift a destra di n pos. bit-wise significato
Espressioni di assegnazione Le espressioni di assegnazione sono valutate da destra a sinistra Le assegnazioni multiple sono permesse alcuni operatori di assegnazione combinano assegnazione ed altri operatori Assegnazioni possono essere fatte all’interno di espressioni aritmetiche a = j++; j viene incrementato ed il risultato assegnato ad a a = b = c = d = 100; a *= b; // equivale ad a = a*b; a -= b; // equivale ad a = a-b; a = b + ( c = 3 ); // equivale a c=3; a=b+c;
Statements Statement C++ commenti vuoto ; espressione j=j+k; composto { . . . . } usato in funzioni, if.. Costituisce un blocco goto goto label; da non usarsi if if (p==0) cerr<<“error”; un solo branch if-else if (x==y) cout<<“the same”; else cout<<“different”; due branch for for (j=0;j<n;j++) le dichiarazioni sono a[j]=0; permesse while while (i != j) 0 o piu` iterazioni i++; do-while do y=y-1; 1 o piu` iterazioni while (y>0); break break; esce dal blocco continue continue; prossima iterazione Statement C++ commenti
Statements (2) Statement C++ commenti switch switch (s) { case 1: si deve usare break per ++i; evitare di cadere nei case 2: casi successivi e --i; aggiungere un caso di default: default alla fine della ++j; lista }; dichiarazione int i=7; in un blocco, file o namespace try try {. . . .} usato per trattare le eccezioni label error: cerr<<“Error!”; usato con goto return return x*x*x; valore di ritorno di una funzione Statement C++ commenti
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
if Attenzione all’uso di = e == Nel dubbio, usare sempre un blocco… Attenzione agli else! if (i=1) // questo e` sempre vero!!! {. . . .} if (i != 0) // possibile divisione per 0 a++; // mancano delle {}? a/=i; if (i == 0) // possibile divisione per 0 if (a<0) { cerr<<“a e` negativo!”; } else b=a/i;
while e do-while La forma generale di un while è : 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: Lo statement verrà quindi eseguito almeno una volta while (condizione) statement; do statement; while (condizione);
break e continue break e continue sono utilizzati nei loop per saltare alla fine del loop o fuori dal loop stesso break e continue possono solamente essere utilizzati nel corpo di un for, while o do-while. break e` anche usato negli switch 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
switch Lo switch è uno statement condizionale che generalizza lo if-else lo statement è generalmente composito e consiste di diversi case e, opzionalmente, di un default switch (condizione) (statement); 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; }
switch (2) Non si puo` dichiarare una variabile in uno dei case … ma si puo` creare una variabile locale definendo uno statement composto... switch (k) { case 0: int j=0; // Illegale! Errore! . . . case 1: } switch (k) { case 0: { int j=0; // OK, questo compila . . . } case 1:
L’operatore ? L’operatore ? e` l’unico esempio di operatore ternario in C++ Equivale a: Esempio: expr1 ? expr2 : expr3; if(expr1) expr2; else expr3; double max(double a, double b) { double max = (a>b) ? a : b; return max; }
Funzioni matematiche In C++ non esistono funzioni predefinite #include <cmath> using namespace std; double x = r * sin( theta ) * sin( phi ); double y = r * sin( theta ) * cos( phi ); double z = r * cos( theta ); #include <iostream> cin >> r >> theta >> phi ; { double r, theta, phi; int main() { return 0; } cmath.h definisce sin, cos, ... cout << x << “, “ << y << “, “ << z << endl; Potenze: pow(b,exp) (non si può usare ** )
Array Sono supportati gli array di dimensione fissa Inizializzazione: 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.
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; }
Puntatori Riferimento ad una locazione di memoria indirizzo di memoria #include <iostream> cout << *ptr << endl; int *ptr = &j; using namespace std; int main() { int j = 12; return 0; } j = 24; cout << *ptr << endl; cout << ptr << endl; ptr j 12 24 24 12 0x7b03a928 indirizzo di memoria
Puntatori Puntatore nullo j 12 ptr #include <iostream> using namespace std; int main() { int j = 12; int *ptr = 0; cout << *ptr << endl; // crash ! return 0; } j 12 ptr Segmentation violation (core dumped)
Puntatori e array In C gli array sono trattati come puntatori X[0] 1.5 2.5 0.0 3.5 x X+1 X+3 int main() { float x[5]; int j; for (j = 0; j < 5; j++) x[j] = 0; float *ptr = x; *ptr = 1.5; // x[0] = 1.5 *(ptr+1) = 2.5; // x[1] = 2.5 *(ptr+3) = 3.5; // x[3] = 3.5 }
Puntatori: allocazione dinamica Riferimento ad una locazione di memoria #include <iostream> using namespace std; int main() { int *ptr = new int; *ptr = 12; cout << *ptr << endl; delete ptr; return 0; } 12 ptr 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
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; } 10 ptr 11 12
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 new riserva la quantità necessaria di memoria richiesta e ritorna l’indirizzo di quest’area int *i=new int; alloca un intero, returna il puntatore char *c=new char[100]; alloca un array (stringa) di 100 caratteri int *i=new int(99); alloca un intero e lo inizializza a 99 char *c=new char(‘c’); alloca un carattere inizializzato a c int *j=new int[n][4]; alloca un array di puntatori ad intero operatore new commenti
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 delete ptr; distrugge un puntatore ad un oggetto delete p[i]; distrugge l’oggetto p[i] delete [] p; distrugge ogni oggetto di tipo p operatore delete commenti
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