next up previous contents index
Next: Puntatori e funzioni Up: Puntatori e riferimenti Previous: Puntatori (parte prima)   Indice   Indice analitico

Puntatori (parte seconda)

Abbiamo visto come si dichiarano i puntatori e come si può ottenere l'indirizzo di una variabile; combiniamo le due cose e otterremo la sintassi per l'assegnamento (o per la definizione) dei puntatori. Vediamo il seguente esempio:

// ex5_2_1.cpp
void main() {
  int n = 19;
  double x = 2.71;
  int* p = &n;      // inizializzazione
  double* q;        // dichiarazione
  q = &x;           // assegnamento
  // q = &n;        // ERRORE: n e' una variabile intera,
                    // mentre q punta a variabili reali
}

Abbiamo creato dunque due variabili, le quali contengono certi valori, e due puntatori, i quali anch'essi contentono certi valori, solo che tali valori sono gli indirizzi delle due variabili precedenti. Ovviamente, come già notato, non possiamo assegnare ad un puntatore di un certo tipo, l'indirizzo di una variabile di un tipo diverso da esso. Così come le variabili non inizializzate, prima che fosse loro assegnato un valore, contengono dati del tutto privi di significato, anche per i puntatori vige la stessa regola; tuttavia un puntatore non inizializzato è potenzialmente più pericoloso di una variabile non inizializzata: siccome il programma interpreta il valore dei puntatori come indirizzi di memoria, se effettuiamo una operazione di scrittura, ad esempio, su di un puntatore non inizializzato e non assegnato potremmo causare danni al sistema sul quale il programma viene eseguito. La spiegazione risiede nel fatto che, malauguratamente, il contenuto arbitrario del puntatore potrebbe corrispondere proprio ad una zona di memoria utilizzata da un altro programma, o dal sistema operativo stesso. Si faccia bene attenzione dunque a creare un puntatore immediatamente prima del suo utilizzo in maniera tale da inizializzarlo con l'indirizzo della variabile da puntare; nel caso ciò non fosse possibile, o comunque fosse sconveniente, si inizializzi il puntatore con il valore $0$, il quale indica ``nessun indirizzo''.

Ora che sappiamo come dichiarare, inizializzare, assegnare puntatori, non ci resta che capire il loro utilizzo: i puntatori se preceduti dall'operatore *, costituiscono degli alias5.2 delle variabili puntate. Questo significa che se abbiamo


int n = 5;
int* p = &n;
possiamo indifferentemente utilizzare al medesimo scopo n e *p, il che è spesso utile, ed in certi casi indispensabile. Vediamo il seguente esempio:

// ex5_2_2.cpp
#include <iostream.h>
void main() {
  int a = 5;
  int b = 17;
  cout << "a = " << a << "\tb = " << b << "\n";
  int* p_a = &a;
  int* p_b = &b;
  cout << "p_a = " << p_a << "\np_b = " << p_b << "\n";
  a += 2;
  *p_b += 2;     // *p_b e b sono sinonimi
  // p_b += 2;   // NO: in questa maniera si cambia
                 // l'indirizzo cui punta p_b
  cout << "a = " << a << "\tb = " << b << "\n";
  cout << "p_a = " << p_a << "\np_b = " << p_b << "\n";
}

esempio di output:
a = 5 b = 17
p_a = 0xbffff844
p_b = 0xbffff840
a = 7 b = 19
p_a = 0xbffff844
p_b = 0xbffff840

Come si vede, modificando *p_b il puntatore rimane invariato, infatti continua a puntare sempre lo stesso indirizzo di variabile, mentre cambia il valore contenuto in b. Abbiamo appena visto un modo di cambiare il valore di una variabile senza utilizzare la variabile stessa; vedremo tra non molto come questa possibilità di operare apre nuove strade della programmazione in C++. Vediamo ora come modificare il valore di un puntatore, semplicemente cambiando l'indirizzo in esso contenuto:


// ex5_2_3.cpp
#include <iostream.h>
void main() {
  double x = 3.14, y = 2.71;
  double* p = 0;
  cout << "p = " << p << "\n";
  // cout << "p = " << p << "\t*p = " << *p << "\n";
  // NO: *p NON e' l'alias di nessuna variabile
  // per cui NON ha alcun valore
  p = &x;  // *p e' alias di x
  cout << "p = " << p << "\t*p = " << *p << "\n";
  p = &y;  // *p e' alias di y
  cout << "p = " << p << "\t*p = " << *p << "\n";
}

esempio di output:
 p = (nil)  
 p = 0xbffff840 *p = 3.14
 p = 0xbffff838 *p = 2.71

In fase di stampa ci viene ricordato che p inizialmente non punta alcuna variabile, per cui il suo valore è nil (dal latino nihil = ``niente''); ovviamente non possiamo stampare inizialmente il valore di *p in quanto non vi corrisponde nessuna variabile.

Per ogni variabile, possiamo avere un numero arbitrario di puntatori che fungono da alias:


// ex5_2_4.cpp
#include <iostream.h>
void main() {
  int n = 3;
  int* p = &n;
  int* q = p;    // quindi anche q = &n
  cout << "n = " << n << "\t*p = " <<
    *p << "\t*q = " << *q << "\n";
  cout << "p = " << p << "\n";
  cout << "q = " << q << "\n";
  // int m = n**p**q;
  // Il precedente statement, anche se non si
  // direbbe, e' equivalente al successivo, il
  // quale e' tuttavia di gran lunga piu' chiaro.
  // Si tratta di un esempio di cattiveria pura
  // nei confronti di un possibile utilizzatore
  // del nostro codice (o dell'insegnante che
  // ci corregge l'esercizio)
  int m = n * (*p) * (*q);
  cout << "m = " << m << "\n";
}

esempio di output:
 n = 3 *p = 3 *q = 3
 p = 0xbffff844    
 q = 0xbffff844    
 m = 27    

Si sarà notato che il simbolo * ha significati diversi nel linguaggio: operatore (binario) di moltiplicazione tra interi o reali, operatore (binario) di derivazione tipo per dichiarare puntatori, operatore (unario) che applicato a sinistra di un puntatore lo trasforma nella variabile puntata. Nel caso si debbano utilizzare in una sola espressione delle diverse accezioni del simbolo *, come nell'esempio precedente, si faccia uso dell'operatore parentesi tonde '()' al fine di evitare errori o confusioni in un eventuale utilizzatore del nostro codice.


next up previous contents index
Next: Puntatori e funzioni Up: Puntatori e riferimenti Previous: Puntatori (parte prima)   Indice   Indice analitico
Claudio Cicconetti
2000-09-06