C++ - Lezione II
Conversione di tipo
I puntatori
Arrays
Funzioni
1
Operazioni Miste e Conversioni
di Tipo
Spesso si usano operandi di tipo diverso in una stessa espressione o si
assegna ad una variabile un valore di tipo diverso della variabile stessa
In ogni operazione mista è sempre necessaria una conversione di tipo
che può essere implicita o esplicita
Le conversioni implicite vengono effettuate dal compilatore:


nell'ambito numerico, gli operandi sono convertiti al tipo di quello di
dimensione maggiore
Esempio: 3*2.6 viene calcolata come 3.0*2.6
e non come 3*2
nell’assegnazione un'espressione viene sempre convertita al tipo della
variabile
Esempi:
float x = 3;
//equivale a: x = 3.0
int y = 2*3.6; //equivale a: y = 7
Il programmatore può richiedere una conversione esplicita di un valore
da un tipo ad un altro (casting)

Esistono due notazioni:
 Esempio:
int i = (int) 3.66;  i=3 (troncamento!)
 Esempio:
double f = double(3)
2
Cosa succede quando dichiariamo
una variabile ?
La dichiarazione di una variabile associa 3 attributi alla variabile
stessa


tipo, nome, indirizzo di memoria
Es: int j = 24;
 tipo : int
 nome : j
 locazione di memoria in cui è custodito in valore della variabile
gli indirizzi di memoria vengono rappresentati in esadecimale
indirizzo del
primo byte
0x7b03a928
j
comunemente il tipo int occupa
4 bytes di memoria
24
3
I Puntatori
Un puntatore è un riferimento ad una locazione di memoria
 memorizza l' indirizzo di un altra variabile
#include <iostream.h>
using namespace std;
int main() {
int j = 12;
int * ptr = &j;
ptr
j
cout << *ptr << endl;
24
12
j = 24;
cout << *ptr << endl;
sintassi T * t
cout << ptr << endl;
cout << &j << endl;
cout << &ptr << endl;
return 0;
}
L'operatore di indirizzo & restituisce l'inidirizzo delle variabili
L'operatore di deferenziazione * mostra il contenuto della variabile
puntata
4
Output
#include <iostream.h>
using namespace std;
12
24
int main() {
int j = 12;
int * ptr = &j;
cout << *ptr << endl;
0x22efc4
indirizzi di
memoria
j = 24;
cout << *ptr << endl;
0x22efc4
cout << ptr << endl;
0x22efc0
cout << &j << endl;
cout << &ptr << endl;
return 0;
}
5
Puntatori - Altro Esempio
int i, j;
p
&i
i
3
q
&i
j
3
int *p; int *q;
p=&i; // p=indirizzo di i
*p=3; // equivale a i=3
j=*p; // equivale a j=i
q=p;
// equivale a q=indirizzo di i
6
Esercizio – Scambio di valore
di 2 int
int a = 10, b = 20, temp;
temp = a;
a = b;
b = temp;
Fare lo stesso utilizzando i puntatori ad int ...
int a = 10, b = 20, temp;
int *pa, *pb;
...
7
Gestione Dinamica della
Memoria
In C++ è possibile gestire la memoria anche dinamicamente,
ovvero durante l’esecuzione del programma
La gestione dinamica della memoria consente in un area di
memoria esterna allo stack di esecuzione del programma
L’accesso avviene tramite puntatori
#include <iostream.h>
using namespace std;
int main()
{
int *ptr = new int;
ptr
*ptr = 12;
cout << *ptr << endl;
12
delete ptr;
return 0;
}
operatori di allocazione
e
deallocazione
8
Operatori new e delete
L’operatore new alloca un’area di memoria
atta a contenere un oggetto del tipo
specificato e ritorna un puntatore a tale area
di memoria

Esempio:
int * p = new int;
L’operatore delete dealloca l’area di
memoria dinamica puntata dal puntatore

Esempio:
int * p = new int;
delete p;
9
Deallocazione
Un oggetto creato dinamicamente resta allocato
finchè:
 non viene esplicitamente deallocato con
l'operatore delete

il programma non termina
Non usare delete fa accumulare locazioni di memoria
inutilizzate (memory leak)

La memoria non esplicitamente deallocata con l'operatore
delete può risultare non più disponibile per altri programmi
10
Esempio di Memory Leak
#include <iostream.h>
using namespace std;
si alloca memoria per
un double
int main()
{
while(1){
double x = 20;
ora p_x punta all'area di
memoria contenente x
ma non si è liberata
quella precedemente allocata!!!
double * p_x = new double;
delete p_x;
p_x = &x; // Attenzione!! Memory leak!!
cancelliamo la memoria
precedentemente allocata
per il double
cout<< *p_x << endl;
}
return 0;
}
11
Osserviamo come viene usata
la memoria...
Windows XP –
Task Manager
Linux - top
12
Altro errore ... puntatore a
memoria non allocata
int main() {
int *
new int;
int;
*p;
*pp == new
*p = 3; /* errore! p punta ad un’area non allocata */
delete p;
return0;0;
return
}
Proviamo ad compilare ed eseguire il programma...
Ora è tutto OK ?
13
Puntatore Nullo
#include <iostream.h>
ptr
j
12
using namespace std;
int main()
{
int j = 12;
int * ptr = 0;
cout << *ptr << endl; // crash !
return 0;
}
Segmentation violation
Esercizio : modificare il programma affinché non crashi...
14
Puntatore a Costante e
Puntatori Costanti
#include <iostream.h>
Puntatore a costante: const T * t;

const int * pc;
Puntatore costante : T * const t;

int * const pc;
using namespace std;
int main(){
const int c1 = 3;
int c2 = 5;
Puntatore costante a constante: const T * const t;

const int * const pc;
const int * pc1 = &c1;
int * const pc2 = &c2;
cout << "*pc1 " << *pc1 << endl;
cout << "*pc2 " << *pc2 << endl;
Solo una delle tre righe puo' essere
decommentata: quale?
// pc2=pc1;
// (*pc1)++;
// (*pc2)++;
cout << "*pc1 " << *pc1 << endl;
cout << "*pc2 " << *pc2 << endl;
return 0;
}
15
Arrays
Un array è una sequenza finita di elementi dello stesso tipo
Sintassi:

tipo id[dim];
tipo id[dim]={lista_valori};
tipo id[]={lista_valori};
Esempi:

double a[25]; //array di 25 double
const int c=2;
char b[2*c]={'a','e','i','o'};
char d[]={'a','e','i','o','u'};
Gli elementi di un array di dimensione N sono numerati da 0 a
N-1
Esempio: a[3]=7 assegna il quarto elemento
16
Arrays Multidimensionali
È possibile dichiarare array i cui elementi sono a sua volta degli
array, generando degli array multidimensionali (matrici)
Sintassi:
tipo id[dim1][dim2]…[dimn];
tipo id[dim1][dim2]…[dimn]={lista_valori};
tipo id[][dim2]…[dimn]={lista_valori};
Quindi un array multidimensionale
dim1 x dim2 x … x dimn può essere pensato come un array di
dim1 array multidimensionali dim2 x … x dimn
17
Incremento e Decremento di
Puntatori
char * pc;
char c = `a`;
pc = &c;
pc = pc++; // incrementa pc in modo che punti al char
// immediatamente successivo
//(ossia pc viene incrementato di 1)
pc
a
char
char
il risultato non `e di incrementare di 1 il valore del puntatore,
bensi di un numero di bytes equivalenti all’ampiezza di un char
18
Arrays e Puntatori
#include <iostream>
int main(){
float val[5]={ 3.1, 5.7, 6.4, 1.2, 0.5};
Il nome di un array è una costante
puntatore al primo elemento
dell'array stesso
std::cout<< " val " << val <<std::endl;
std::cout<< " *************************** " <<std::endl;
int i=0;
while(i<5){
std::cout<< "val[" <<i<<"] = " <<val[i] << ", val = "
<< val+i <<std::endl;
++i;
}
std::cout<< " *************************** " <<std::endl;
val
3.1
val+1
5.7
val+2
6.4
val+3
1.2
val+4
0.5
i=0;
while(i<5){
std::cout<< "*val+" <<i<<" = "
<< *val+i <<std::endl;
++i;
}
return 0;
}
19
Arrays Statici & Dinamici
float a[20]; \\ array statico
float * p = new float[20]; \\ array dinamico
Nella prima dichiarazione la memoria è allocata in maniera statica
durante la compilazione del programma e la sua memoria e'
allocata durante tutta l'esecuzione del programma
Nella seconda dichiarazione la memoria è allocata dinamicamente,
la memoria non è allocata fino a quando non e' eseguita la
dichiarazione. La memoria viene liberata non appena si invoca
l'operatore delete
delete [] p;
20
I Riferimenti
Il meccanismo dei riferimenti (reference) consente di dare nomi
multipli a una variabile
Esempio:
int x=1;
int &y=x; // y è di tipo reference
y++
Attenzione : nelle dichiarazioni di variabili di tipo reference,
l’inizializzazione è obbligatoria
Esempio:
int &y; // errore!
Non è possibile ridefinire una variabile di tipo riferimento
precedentemente definita:
double x1,x2;
double &y=x1; // ok
double &y=x2; // errore! gia’ definita!
21
Esercizi...
Scrivere un programma che usa un puntatore
int * p
per cercare un intero, dato dallo standard input, all'interno dell' array
{11, 22, 33, 44, 55, 66, 77, 88, 99}
Se l'intero viene trovato il programma stampa l'indirizzo altrimenti chiede un altro
intero in input.
Scrivere un programma che usa un puntatore
float * p
per stampare il puntatore del maggiore di un array di float passato dallo standard
input
22
Funzioni
Nella realizzazione di un programma



il codice può diventare molto lungo
spesso una stessa sequenza di operazioni viene ripetuta in
più punti
È quindi opportuno e conveniente strutturare il codice
raggruppandone delle sue parti in moduli autonomi, detti
funzioni, che vengono eseguiti in ogni punto in cui è
richiesto
L'organizzazione a funzioni consente di isolare parti di
un programma che possono essere modificate in
maniera indipendente
23
Sintassi per le Funzioni
Dichiarazione:
Tipo func(Tipo1 t1, …, TipoN tN);
Definizione:
Tipo func(Tipo1 t1, …, TipoN tN) {corpo }
t1, …, tN sono i parametri della funzione
Chiamata:
func(exp1, …, expN)
exp1, …, expN sono i parametri della chiamata e devono
essere di tipo compatibile con quelli della dichiarazione
24
Esempio dalla Lezione I
definiamo una funzione
saluti.h
#include <iostream>
implementiamo la funzione
saluti.cc
#include ” s a l u t i . h ”
using namespace std;
void ciao();
g++ -c saluti.cc
g++ -c test.cpp
g++ -o runme saluti.o test.o
./runme
oppure :
g++ -o runme saluti.cc test.cpp
•un altro po' di sintassi
• void non ritorna nessun tipo
• \n fine di linea
void ciao ( ) {
cout << ” ciao ! \ n ” ;
}
usiamo la funzione
test.cpp
#include ” s a l u t i . h ”
int main() {
ciao();
}
return 0;
25
Altro
Esempio di
Funzione
#include <iostream.h>
using namespace std;
/* funzione che calcola il massimo tra due numeri */
float max(float a, float b) {
float max = a>b? a:b;
return max;
}
• Le variabili dichiarate
all'interno di una funzione
sono locali a tale funzione
e appartengono solo alla
funzione in cui sono
dichiarati (sono visibili solo
nella funzione)
/* programma che chiama la funzione max per il calcolo del
massimo */
int main()
{
float f1, f2;
cout << "Inserisci una coppia di numeri " << endl;
cin >> f1 >> f2;
cout << "Il max fra " << f1 << " e " << f2 << " vale "
<< max(f1,f2) << endl;
return 0;
}
26
Passaggio dei Parametri
Esistono due modalità di associazione dei parametri
attuali ai parametri formali:


passaggio parametri per valore. Il parametro ha una sua
propria cella di memoria in cui viene copiato il valore del
parametro attuale corrispondente all’atto della chiamata.
passaggio parametri per riferimento. La cella di memoria
associata al parametro è quella della variabile
corrispondente.
Nell'esempio precedente veniva usato il passaggio
per valore, ovvero veniva fatta una copia dei
parametri
27
Passaggio
per
Riferimento
#include <iostream.h>
using namespace std;
passaggio per riferimento
/*funzione incremento e decremento*/
void incremento(int &ival) {ival=ival+1;}
void decremento(int &ival) {ival=ival-1;}
/* programma che chiama la funzione incremento decremento */
int main(){
supponiamo di voler
implementare
da noi la funzione per
l'incremento
ed il decremento di un intero
int i1;
cout << "Inserisci un numero intero " << endl;
cin >> i1;
cout << "Il numero inserito e` " << i1 << endl;
incremento(i1);
incremento(i1);
cout << "Ora il numero inserito vale " << i1 << endl;
decremento(i1);
cout << "Ora il numero inserito vale " << i1 << endl;
return 0;
}
28
Puntatori e Passaggio per
Riferimento
I puntatori consentono di simulare il
passaggio parametri per riferimento
Esempio:
void scambia(int* px, int* py) {
int t;
t = *px; *px = *py; *py = t;
}
void main() {
int a, b;
cin >> a >> b;
scambia(&a, &b);
cout << a << b << endl;
}
29
Considerazioni sul Passaggio
per Riferimento
Quando


l'oggetto da passare è grande  minore
carico di calcolo
bisogna modificare il parametro passato
alla funzione (da utilizzare esternamente)
 Tuttavia, se non si vuole che il parametro
passato venga modificato conviene usare il
passaggio per riferimento costante :
void f(const Tipo &t)
30
Passaggio di un Array
Quando viene passato un array ad una
funzione, non vengono copiati i suoi elementi
nel corrispondente parametro ma viene
ricopiato solo l’indirizzo del primo elemento
//funzione che somma i primi n elem.
int somma(int v[], int n) {
int i, s=0;
indirizzo dell'array
for (i=0; i<n; i++) s += v[i];
dimensione dell'array
return s;
}
31
Funzioni Ricorsive
Una funzione può invocare non solo un'altra
funzione, ma anche se stessa: si ha in questo
caso una funzione ricorsiva

Si rende agevole la programmazione in tutti i quei
casi in cui risulta naturale formulare il problema da
risolvere in maniera ricorsiva
Esercizio: calcolare n! utilizzando una
funzione ricorsivamente
32
Fattoriale :
Soluzione
#include <iostream.h>
using namespace std;
/*funzione fattoriale*/
int fatt(int n){
if (n==0 || n==1) return 1;
return n*fatt(n-1);
}
ricorsione
/* programma che chiama la funzione fattoriale*/
int main(){
int i1;
cout << "Inserisci un numero intero " << endl;
cin >> i1;
cout << i1 <<"! = " << fatt(i1) << endl;
return 0;
}
33
Esercizi ...
Implementare ed usare la funzione

void computeCircle(float &area, float &perimetro,
float raggio)
per il calcolo dell'area e del perimetro di un
cerchio
Implementare ed usare una funzione che
restituisce la deviazione standard di n numeri
x1...xn
34
#include <iostream.h>
using namespace std;
void computeCircle(float& area, float& perimetro, float r) {
const float PI = 3.1416;
perimetro = 2*PI*r;
area = PI*r*r;
}
int main(){
float r;
cout << "Inserisci il raggio della circonferenza " << endl;
cin >> r;
float area, perimetro;
computeCircle(area, perimetro, r);
cout << "Il perimetro e` " << perimetro << ", l'area e` " << area << endl;
return 0;
}
35
#include <iostream.h>
using namespace std;
double deviazioneStandard(double x[], int dim) {
double media=0;
for(int kk=0;kk<dim;++kk) media+=x[kk];
media/=dim;
double scarti=0;
for(int kk=0;kk<dim;++kk) scarti+=(x[kk]-media)*(x[kk]-media);
return sqrt(scarti/double(dim-1));
}
int main(){
int n;
cout << "Inserisci il numero di dati " << endl;
cin >> n;
double * x = new double[n];
cout << "Inserisci i dati " << endl;
for(int kk=0;kk<n;++kk) cin>>x[kk];
cout << "La deviazione standard e` " << deviazioneStandard(x,n) << endl;
delete [] x;
return 0;
}
36
Backup
37
Sistema Binario ed Esadecimale
Sistema decimale : reppresentazione dei numeri in
base 10

20 = 2x101+0x100
Sistema binario : reppresentazione dei numeri in base
2

20 = 1x24+0x23+1x22+0x21+0x20 = 10100
Esadecimale : sistema di numerazione in base 16


20 = 1x161+4x160 = 14
efficace perchè i numeri sono corti
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
0 1 2 3 4 5 6 7 8 9 A B C D E F
38
Scambio di interi coi puntatori
int a = 10, b = 20, temp;
int *pa, *pb;
pa = &a; /* *pa diventa un alias per a */
pb = &b; /* *pb diventa un alias per b */
temp = *pa;
*pa = *pb;
*pb = temp;
39
Scarica

C++ - Lezione I