1
laboratorio di calcolo IIAA 2003/04
seconda settimana
a cura di
Domizia Orestano
Dipartimento di FisicaStanza 159 - tel. (06 5517) 7281
www.fis.uniroma3.it/[email protected]
UNIVERSITA’ DEGLI STUDI ROMA TRE
DIPARTIMENTO DI FISICA “E. AMALDI”
2
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”
nellaprossimalezione
3
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 puntareint i=3; // l’oggetto salvato in memoriaint *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!)int *p: i: int 3
4
Esempioint numero = 82485;
// un oggetto intero a 32 bit
// che in binario si scrive
// 00000000 00000001
// 01000010 00110101
int * puntatore;
// puntatore ad un oggetto intero
puntatore = №
// puntatore a numero
// (posto uguale all'indirizzo di numero)
puntatorepuntatore
numeronumero
0x09053
0x09050
.
.
.
.
00110101010000100000000100000000
0101000010010000
5
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
float *
x:
x[4]x[0] x[1] x[2]
floatfloat float float float
x[3]
6
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)
7
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++;}
8
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;
9
vettori di caratteri e stringheProgramma 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';
10
// 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];
// };
11
// 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;
12
// 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; }
13
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; }
14
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)
15
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] ;
……………};
16
// 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 !
17
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 !
18
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
19
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!
20
#include "CorpoCeleste.h"#include <string.h>#include <iostream.h>#include <iomanip.h>
CorpoCeleste::CorpoCeleste() { }
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;}
CorpoCeleste.cc
Prima parte
21
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 ;}
void CorpoCeleste::stampaVelocita() { cout.setf(ios::fixed); cout.setf(ios::showpos); cout << " " << vx << " " << vy ;}
CorpoCeleste::~CorpoCeleste() {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; }
CorpoCeleste.cc
Seconda parte
22
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!
23
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 distruttore3. Provare i seguenti programmi che usano
CorpoCeleste:
24
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)
25
#include "CorpoCeleste.h"#include <string.h>#include <iostream.h>#include <iomanip.h>
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;}
CorpoCeleste.cc
Prima parte
26
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 ;}
void CorpoCeleste::stampaVelocita() { cout.setf(ios::fixed); cout.setf(ios::showpos); cout << " " << vx << " " << vy ;}
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; }
CorpoCeleste.cc
Seconda parte
27
#include <iostream.h>#include "CorpoCeleste.h”
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; }
Es1_a.cc
28
#include <iostream.h>#include "CorpoCeleste.h" 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 Posizione iniziale " << endl
<< " x y " << endl << endl; cout << " " << "1" << " " << Psole->M() << " " << Psole->X() << " " << Psole->Y() << endl; cout << " " << "2" << " " << Ppietra->M() << " " << Ppietra->X() << " " << Ppietra->Y() << endl; cout << " " << "3" << " " << Psasso->M() << " " << Psasso->X() << " " << Psasso->Y() << endl; cout << endl;
Es1_b.cc
Prima parte
29
// 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; }
Es1_b.cc
Seconda parte
30
#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 Posizione iniziale " << endl
<< " x y " << endl << endl; for ( int i=0; i<np; i++) { cout << " " << i+1 << " " << Pianeti[i].M() << " " << Pianeti[i].X() << " " << Pianeti[i].Y() << endl; } cout << endl; return 0;}
Es1_c.cc
31
#include <iostream.h>#include "CorpoCeleste.h" int main () {
// 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. Massa Posizione iniziale " << endl
<< " x y " << endl << endl; for ( int i=0; i<npp; i++) { cout << " " << i+1 << " " << MieiPianeti[i]->M() << " " << MieiPianeti[i]->X() << " " << MieiPianeti[i]->Y() << endl; } cout << endl;
Es1_cbis.cc
Prima parte
32
// devo ricordarmi di cancellare gli oggetti allocati dinamicamente for ( int i=0; i<npp; i++) { cout << i+1; delete MieiPianeti[i]; } return 0; }
Es1_cbis.cc
Seconda parte
33
#include <iostream.h>#include "CorpoCeleste.h" int main () { // 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].M() << " " << Pianeti[i].X() << " "
<< Pianeti[i].Y() << endl; }
Es1_d.cc
Prima parte
34
// 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; }
Es1_d.cc
Seconda parte
35
#include <iostream.h>#include "CorpoCeleste.h" int main () { // 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. Massa Posizione iniziale " << endl
<< " x y " << endl << endl;
Es1_dbis.cc
Prima parte
36
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; }
Es1_dbis.cc
Seconda parte