UNIVERSITA’ DEGLI STUDI ROMA TRE
DIPARTIMENTO DI FISICA “E. AMALDI”
laboratorio di calcolo II
AA 2003/04
seconda settimana
a cura di
Domizia Orestano
Dipartimento di Fisica
Stanza 159 - tel. (06 5517) 7281
www.fis.uniroma3.it/~orestano
[email protected]
1
Indice
•
•
•
•
Puntatori
Puntatori e vettori
Vettori di caratteri e stringhe
Allocazione dinamica della memoria
• Strutture dati
• Reference
• Passaggio dei dati ad una funzione per
“referenza” e per “valore”
nella
prossima
lezione
2
Puntatori
• Un puntatore è un oggetto che fa riferimento ad un altro oggetto
• Lo si dichiara in uno dei due modi seguenti
int* p;
int *p;
Dove p è il puntatore
• Non gli si può assegnare un valore direttamente, ma solo un
oggetto cui puntare
int i=3;
// l’oggetto salvato in memoria
int *p=&i; // dove &i è l’indirizzo di memoria di i
(questa istruzione equivale a int *p; p=&i;)
• L’oggetto puntato si ottiene “dereferenziando” il puntatore: *p
• Se p vale 0 (puntatore nullo) *p non è definito (il programma si
ferma!)
p:
int *
i:
int 3
3
Esempio
.
.
int numero = 82485;
// un oggetto intero a 32 bit
// che in binario si scrive
// 00000000 00000001
0x09053 00110101
01000010
00000001
0x09050 00000000
numero
// 01000010 00110101
int * puntatore;
// puntatore ad un oggetto intero
puntatore = №
// puntatore a numero
// (posto uguale all'indirizzo di numero)
01010000
10010000
puntatore
.
.
4
Puntatori e Vettori (Arrays)
• float x[5]; // dichiarazione di un vettore di numeri reali con 5 elementi
• x e &x[0] sono la stessa cosa:
il puntatore al primo elemento dell’array
• *x e x[0] sono la stessa cosa:
il primo elemento dell’array
x:
x[0]
x[1]
x[2]
x[3]
float
float
float
float
x[4]
float
float *
5
Aritmetica dei puntatori
float x[5];
float *y=&x[0]; // y è un puntatore a x[0]
float *z=x;
// z è un puntatore a x[0]
(y+1) punta a x[1]
*(y+1) , y[1] e x[1] accedono lo stesso oggetto
sono consentite operazioni di somma e sottrazione ed
è possibile confrontare tra loro due puntatori (si
confrontano gli indirizzi di memoria, che sono numeri
interi)
6
Esempi di uso dei puntatori
somma degli elementi di un vettore
float x[5];
// qui gli elementi vengono inizializzati…
double sum=0;
for(int i=0;i<5;i++) {
sum+=x[i];
}
oppure
float x[5];
// qui gli elementi vengono inizializzati…
float *y=x;
double sum=0;
for(int i=0;i<5;i++) {
sum+=*y++;
}
7
Esempi
inversione dell’ordine degli elementi di un vettore
float x[10];
// qui gli elementi vengono inizializzati…
float *left = &x[0];
float *right = &x[9];
while(left < right) {
float temp = *left;
*left++ = *right;
*right-- = temp;
}
azzeramento degli elementi di un vettore
float x[10];
// qui gli elementi vengono inizializzati…
float *p = &x[10]; //attenzione a non usare *p !!!
while ( p != x ) *--p=0;
8
vettori di caratteri e stringhe
Programma che usa char e stringhe C
#include <iostream.h>
#include <string.h>
int main() {
// Voglio scrivere il mio nome
// Introduco un vettore di caratteri nomechar,
// lungo 7 caratteri, ed introduco il mio
// nome, 1 lettera alla volta.
// L'output utilizza un loop, con o senza il
// puntatore (piu' o meno raffinato)
// input dei dati
char nomechar[7];
nomechar[0] = ‘D';
nomechar[3] = nomechar[5] = ‘i';
nomechar[1] = ‘o';
nomechar[2] = ‘m';
nomechar[4] = ‘z';
nomechar[6] = ‘a';
9
//
//
//
//
//
//
In realta' sarebbe anche possibile
istanziare e inizializzare nomechar con
una sola istruzione. La dimensione viene
assegnata automaticamente dal compilatore.
La linea di istruzione equivalente alle
precedenti e':
// char nomechar[]
//
= {‘D’,’o’,’m’,’i’,’z’,’i’,’a’ };
// Oppure l'input potrebbe essere gestito
// tramite lettura da tastiera
// cout << "Assegnare il nome( 7 caratteri): "
//
<< endl;
//
for (int i=0; i<7; i++) {
//
cin >> nomechar[i];
//
};
10
// Passiamo all'output
// Senza l'uso del puntatore
cout << endl
<< " Output senza l'uso del puntatore: ";
for(int m=0;m<=7;m++) {
cout << nomechar[m];
};
//Ora passiamo all'uso delle stringhe di tipo C
// Istanzio un puntatore alla stringa C che
// contiene il nome
char *nomeC;
nomeC = “Domizia";
// Notare l'uso e la funzione del doppio apice
cout << endl << " Stringa di tipo C:
"
<< nomeC << endl;
11
// Da notare che si puo' effettuare l'output
// anche pensando alla stringa C come una serie
// di caratteri
cout << "Output carattere per carattere: ";
for(int j=0;j<7;j++) {
cout << ' ' << nomeC[j] ;
};
cout << endl;
// oppure ...
cout << "
";
char *q=nomeC;
// q e’ un puntatore ad un carattere
for(int j=0;j<7;j++) {
cout << ' ' << *q++;
};
cout << endl << endl;
return 0;
}
12
Programma che usa le stringhe C++
#include <iostream.h>
#include <string>
int main() {
string name;
name = “Domizia";
cout << name << endl;
string cognome=“Orestano";
string tutto=name+" "+cognome;
cout << endl << tutto << endl;
return 0;
}
13
Nota al Programma che usa i caratteri e le stringhe C
• Sarebbe meglio introdurre una variabile cmax
const int cmax=7;
• E poi sostituire tutte le ricorrenze di 7 con la variabile cmax. In
questo modo il programma diventa più elegante ed eventuali
variazioni di lunghezza non richiederebbero sostituzioni se non
nel valore di cmax.
• Il problema non si pone nell'uso di stringhe C++
(programmazione OO ed allocazione dinamica della
memoria)
14
Dimensionamento dei vettori (1)
// Un esempio da CorpoCeleste
class CorpoCeleste {
protected:
char *Nome;
…………….};
// Variante a dimensione fissa,
// memoria allocata a compilation time
class CorpoCeleste {
protected:
char Nome[20] ;
……………};
15
// Tentativo di variante a dimensione variabile,
// ma non compila!
class CorpoCeleste {
protected:
int n;
char Nome[n];
…………….};
vettori di dimensione variabile, fissata in fase di esecuzione,
non possono essere inseriti tra gli attributi di una classe !
16
Puntatori a variabili locali (1)
===File prova_fp.h
class prova_fp {
private:
// .......
public :
prova_fp() { } ;
~prova_fp() { }
int * int_fp() {
int a = 5 ;
return &a ;
};
} ;
===Main:
#include <iostream.h>
#include <string.h>
#include "prova_fp.h"
int main() {
int * p;
prova_fp myprova;
p = myprova.int_fp();
cout << endl << endl;
cout << " Puntatore : "
<< p << endl ;
cout << " Valore
: "
<< *p << endl ;
return 0;
};
Warning in compilazione e stampa di un Valore privo di senso !
17
Allocazione dinamica della memoria
• L’allocazione statica degli oggetti in memoria consente
maggiore velocità di esecuzione perchè lo spazio usato e il
tempo di utilizzo (“lifetime” dell’oggetto) sono determinati già
in fase di compilazione.
• due problemi:
– vogliamo poter allocare di vettori di dimensione definita
durante l’esecuzione del programma
– vogliamo avere accesso agli oggetti definiti localmente
(all’interno di uno { “scope”})
• una soluzione: allocazione dinamica della memoria. Gli
oggetti vengono creati in un’area di memoria chiamata heap.
Si utilizzano gli operatori
– new
– delete
18
Dimensionamento dei vettori (2)
class CorpoCeleste {
protected:
char * Nome;
…………….};
E poi nel costruttore inizializzato
int n = strlen(nomeCorpo);
Nome=new char[n];
Ma tutti gli oggetti creati con new devono essere distrutti esplicitamente con
l’operatore delete, questi oggetti infatti non vengono cancellati automaticamente
all’uscita dallo “scope”.
In caso di vettori la sintassi è
delete [ ] Nome;
Dobbiamo modificare CorpoCeleste::~CorpoCeleste inserendovi questa istruzione!
19
#include
#include
#include
#include
CorpoCeleste.cc
"CorpoCeleste.h"
<string.h>
<iostream.h>
<iomanip.h>
CorpoCeleste::CorpoCeleste() {
Prima parte
}
CorpoCeleste::CorpoCeleste
(const char *nomeCorpo, float mass,
float xpos, float ypos, float vxi, float vyi) {
Nome = new char[strlen(nomeCorpo)];
strcpy(Nome, nomeCorpo);
m = mass;
x = xpos;
y = ypos;
vx = vxi;
vy = vyi;
}
void CorpoCeleste::calcolaPosizione(
float fx, float fy, float t) {
double ax = fx/m;
double ay = fy/m;
vx += ax*t;
vy += ay*t;
x += vx*t;
y += vy*t;
}
20
void CorpoCeleste::stampaPosizione() {
cout.setf(ios::fixed);
cout.setf(ios::showpos);
cout << " " << setprecision(4) << setw(9)
<< x*1.e-11 << " " << setprecision(4)
<< setw(9) << y*1e-11 ;
}
CorpoCeleste.cc
void CorpoCeleste::stampaVelocita() {
cout.setf(ios::fixed);
cout.setf(ios::showpos);
cout << " " << vx << " " << vy ;
}
CorpoCeleste::~CorpoCeleste()
Seconda parte
{delete [] Nome;}
const char* CorpoCeleste::nome() {return Nome; }
double CorpoCeleste::M()
{ return m; }
double CorpoCeleste::X()
{ return x; }
double CorpoCeleste::Y()
{return y;
double CorpoCeleste::Vx()
{return vx; }
double CorpoCeleste::Vy()
{return vy; }
}
21
Puntatori a variabili locali (2)
class prova_fp_n {
private:
// .......
public :
prova_fp_n() { } ;
~prova_fp_n() { } ;
int * int_fp() {
int * p = new int(5);
return p ;
};
};
===Main:
#include <iostream.h>
#include <string.h>
#include "prova_fp_n.h"
int main() {
int * p;
prova_fp_n myprova;
p = myprova.int_fp();
cout << endl << endl;
cout << " Puntatore : " <<
p << endl ;
cout << " Valore
: " <<
*p << endl ;
delete p;
return 0;
};
Nessun warning in compilazione e stampa del Valore corretto: 5!
22
Esercitazione
•
Obiettivi:
– Utilizzare la classe CorpoCeleste
– Studiare il comportamento dei costruttori e del
distruttore
– Utilizzare puntatori, vettori, vettori di puntatori
1. Modificare il distruttore di CorpoCeleste includendo
l’operazione di delete [ ] Nome
2. Aggiungere stampe nei costruttori e nel distruttore
3. Provare i seguenti programmi che usano
CorpoCeleste:
23
Programmi
• Es1_a.cc (costruttore, uso dei metodi “Get”,
distruttore)
• Es1_b.cc (allocazione dinamica, puntatori)
• Es1_c.cc (vettore di oggetti, costruttore di
default)
• Es1_cbis.cc (vettore di puntatori ad oggetti)
• Es1_d.cc (vettore con allocazione dinamica)
• Es1_dbis.cc (vettore di puntatori con
allocazione dinamica)
24
#include
#include
#include
#include
"CorpoCeleste.h"
<string.h>
<iostream.h>
<iomanip.h>
CorpoCeleste.cc
Prima parte
CorpoCeleste::CorpoCeleste() {
cout << “invocato costruttore di default di CorpoCeleste”<<endl;
}
CorpoCeleste::CorpoCeleste
(const char *nomeCorpo, float mass,
float xpos, float ypos, float vxi, float vyi) {
Nome = new char[strlen(nomeCorpo)];
strcpy(Nome, nomeCorpo);
m = mass;
x = xpos;
y = ypos;
vx = vxi;
vy = vyi;
cout << “invocato costruttore di CorpoCeleste ”<<Nome<<endl;
}
void CorpoCeleste::calcolaPosizione(
float fx, float fy, float t) {
double ax = fx/m;
double ay = fy/m;
vx += ax*t;
vy += ay*t;
x += vx*t;
y += vy*t;
}
25
void CorpoCeleste::stampaPosizione() {
cout.setf(ios::fixed);
cout.setf(ios::showpos);
cout << " " << setprecision(4) << setw(9)
<< x*1.e-11 << " " << setprecision(4)
<< setw(9) << y*1e-11 ;
}
CorpoCeleste.cc
void CorpoCeleste::stampaVelocita() {
cout.setf(ios::fixed);
cout.setf(ios::showpos);
cout << " " << vx << " " << vy ;
}
Seconda parte
CorpoCeleste::~CorpoCeleste()
{
cout << “invocato distruttore di CorpoCeleste ”<<Nome<<endl;
delete [] Nome;
}
const char* CorpoCeleste::nome() {return Nome; }
double CorpoCeleste::M()
{ return m; }
double CorpoCeleste::X()
{ return x; }
double CorpoCeleste::Y()
{return y; }
double CorpoCeleste::Vx()
{return vx; }
double CorpoCeleste::Vy()
{return vy; }
26
#include <iostream.h>
#include "CorpoCeleste.h”
Es1_a.cc
int main () {
// istanzio una serie di oggetti di tipo CorpoCeleste
CorpoCeleste sole("Sole", 2.e30,0.,0.,0.,0.);
CorpoCeleste pietra("dolomite",1.,0.,0.,1.,1.);
CorpoCeleste sasso("quarzo",2.,1.,0.,0.,1.);
// Scrivo alcune caratteristiche degli oggetti che ho istanziato:
// massa e posizione
cout << endl << "Pianeta n.
Massa
Posizione iniziale " << endl
<<
"
x
y "
<< endl
<< endl;
cout << "
" << "1"
<< "
" << sole.M()
<< "
" << sole.X()
<< "
" << sole.Y() << endl;
cout << "
" << "2"
<< "
" << pietra.M()
<< "
" << pietra.X() << "
" << pietra.Y() << endl;
cout << "
" << "3"
<< "
" << sasso.M()
<< "
" << sasso.X() << "
" << sasso.Y() << endl;
cout << endl;
// Il programma e' finito.
// Il compilatore provvede a distruggere gli oggetti di tipo CorpoCeleste
return 0;
}
27
Es1_b.cc
#include <iostream.h>
#include "CorpoCeleste.h"
Prima parte
int main () {
// istanzio una serie di oggetti di tipo CorpoCeleste
// Uso l'allocazione dinamica della memoria, tramite new
CorpoCeleste * Psole
= new CorpoCeleste("Sole", 2.e30,0.,0.,0.,0.);
CorpoCeleste * Ppietra = new CorpoCeleste("dolomite",1.,0.,0.,1.,1.);
CorpoCeleste * Psasso = new CorpoCeleste("quarzo",2.,1.,0.,0.,1.);
// Scrivo alcune caratteristiche degli oggetti che ho istanziato;
// Ad esempio la massa e la posizione
cout << endl << "Pianeta n.
Massa
<<
"
<< endl << endl;
cout << "
" << "1"
<< "
<< "
" << Psole->X()
<< "
cout << "
" << "2"
<< "
<< "
" << Ppietra->X() << "
cout << "
" << "3"
<< "
<< "
" << Psasso->X() << "
cout << endl;
Posizione iniziale " << endl
x
y "
"
"
" << Psole->M()
" << Psole->Y() << endl;
<< Ppietra->M()
" << Ppietra->Y() << endl;
<< Psasso->M()
" << Psasso->Y() << endl;
28
Es1_b.cc
Seconda parte
// Ora devo cancellare gli oggetti allocati dinamicamente tramite new
cout << endl << "Prima delle chiamate a delete " << endl;
delete Psole;
delete Ppietra;
delete Psasso;
cout << endl << "Dopo le chiamate a delete " << endl;
//
//
//
//
Il programma e' finito
Notate (tramite il distruttore intelligente) che il compilatore
ha provveduto a distruggere gli oggetti di tipo CorpoCeleste
ai quali puntavano Psole, Ppietra, Psasso.
return 0;
}
29
Es1_c.cc
#include <iostream.h>
#include "CorpoCeleste.h"
int main () {
// uso un vettore di oggetti di tipo Corpoceleste
const int np = 6;
CorpoCeleste Pianeti[np];
// Con un loop scrivo gli elementi del vettore
for (int i=0; i<np; i++) {
CorpoCeleste Ausiliario(" ",i,i,i,i,i);
Pianeti[i] = Ausiliario;
}
// Con un loop posso estrarre le informazioni
cout << endl << "Pianeta n.
Massa
<<
"
<< endl << endl;
for ( int i=0; i<np; i++) {
cout << "
" << i+1
<<
"
"
}
cout << endl;
return 0;
}
Posizione iniziale " << endl
x
y "
<< "
" << Pianeti[i].M()
<< Pianeti[i].X() << "
"
<< Pianeti[i].Y() << endl;
30
#include <iostream.h>
#include "CorpoCeleste.h"
Es1_cbis.cc
int main () {
Prima parte
// Ora uso un vettore di puntatori a oggetti di
// tipo CorpoCeleste
const int npp = 8;
CorpoCeleste* MieiPianeti[npp];
// Con un loop scrivo gli elementi del vettore
for ( int i=0; i<npp; i++) {
MieiPianeti[i] = new CorpoCeleste(" ",i,i,i,i,i);
}
// Con un loop posso estrarre le informazioni. Ad esempio la massa
cout << endl << "Pianeta n.
<<
"
<< endl << endl;
Massa
Posizione iniziale " << endl
x
y "
for ( int i=0; i<npp; i++) {
cout << "
" << i+1
<< "
" << MieiPianeti[i]->M()
<<
"
" << MieiPianeti[i]->X() << "
"
<< MieiPianeti[i]->Y() << endl;
}
cout << endl;
31
Es1_cbis.cc
Seconda parte
// devo ricordarmi di cancellare gli oggetti allocati dinamicamente
for ( int i=0; i<npp; i++) {
cout << i+1;
delete MieiPianeti[i];
}
return 0;
}
32
#include <iostream.h>
#include "CorpoCeleste.h"
Es1_d.cc
int main () {
Prima parte
// uso un vettore di oggetti di tipo Corpoceleste
// Il vettore di oggetti e' allocato dinamicamente
// tramite new, che fornisce il puntatore all'array
const int np = 6;
CorpoCeleste * Pianeti= new CorpoCeleste[np];
// Con un loop scrivo gli elementi del vettore
for (int i=0; i<np; i++) {
CorpoCeleste Ausiliario("",i,i,i,i,i);
Pianeti[i] = Ausiliario;
}
// Con un loop posso estrarre le informazioni. Ad esempio la massa
cout << endl << "Pianeta n.
Massa
Posizione iniziale " << endl
<<
"
x
y "
<< endl << endl;
for ( int i=0; i<np; i++) {
cout << "
" << (i+1)
<< "
" <<
<<
"
" << Pianeti[i].X() << "
"
<< Pianeti[i].Y() << endl;
}
Pianeti[i].M()
33
Es1_d.cc
Seconda parte
// Ora devo cancellare il vettore allocato dinamicamente
cout << endl << endl << " Prima di chiamare delete [] Pianeti " << endl;
delete [] Pianeti;
cout << endl << " Dopo aver chiamato delete [] Pianeti " << endl;
return 0;
}
34
#include <iostream.h>
#include "CorpoCeleste.h"
Es1_dbis.cc
int main () {
Prima parte
//
//
//
//
//
Ora uso un vettore di puntatori a oggetti di tipo CorpoCeleste
Il vettore di puntatori e' a sua volta allocato dinamicamente,
tramite new, che fornisce il puntatore all'array di puntatori.
Per questo motivo la prima chiamata di new istanzia un oggetto di
tipo CorpoCeleste ** , cioe' un puntatore ad un puntatore.
const int npp = 8;
CorpoCeleste** MieiPianeti = new CorpoCeleste*[npp];
// Con un loop scrivo gli elementi del vettore
for (int i=0; i<npp; i++) {
MieiPianeti[i] = new CorpoCeleste(" ",i,i,i,i,i);
}
// Con un loop posso estrarre le informazioni. Ad esempio la massa
cout << endl << "Pianeta n.
<<
"
<< endl << endl;
Massa
Posizione iniziale " << endl
x
y "
35
Es1_dbis.cc
Seconda parte
for (int i=0; i<npp; i++) {
cout << "
" << i+1
<< "
" << MieiPianeti[i]->M()
<<
"
" << MieiPianeti[i]->X() << "
"
<< MieiPianeti[i]->Y() << endl;
}
cout << endl;
// devo ricordarmi di cancellare gli oggetti allocati dinamicamente
// e poiche' ho usato new a due livelli, chiamero' delete 2 volte
// 1 - per I puntatori agli oggetti di tipo CorpoCeleste
//
(cioe' gli oggetti contenuti nell'array di puntatori
cout << endl << " Prima del loop con delete MieiPianeti[i] " << endl;
for (int i=0; i<npp; i++) {
cout << i+1;
delete MieiPianeti[i];
}
cout << endl << " Dopo il loop con delete MieiPianeti[i] " << endl;
// 2 - per l'array di puntatori
delete [] MieiPianeti;
return 0;
}
36
Scarica

ppt - Università degli Studi Roma Tre