Vai alla Home Page About me Courseware Federica Living Library Federica Federica Podstudio Virtual Campus 3D Le Miniguide all'orientamento Gli eBook di Federica La Corte in Rete
 
I corsi di Scienze Matematiche Fisiche e Naturali
 
Il Corso Le lezioni del Corso La Cattedra
 
Materiali di approfondimento Risorse Web Il Podcast di questa lezione

Ernesto Burattini » 12.Puntatori - Introduzione


Variabili statiche e dinamiche

Un programma è un processo in esecuzione a cui il sistema operativo assegna una certa zona di memoria.

Tale zona può essere suddivisa in quattro grandi blocchi: vedi figura.


Variabili statiche e dinamiche (segue)

Lo spazio occupato dal codice e dai dati è fisso.

Il blocco stack è utilizzato principalmente dalle procedure e funzioni richiamate dal programma; in particolare lo stack conserva le variabili locali, i valori dei parametri formali passati per valori e gli indirizzi di quelli passati per riferimento, l’indirizzo di ritorno della procedura.

Il blocco heap è riservato alle variabili dinamiche.

Allocazione statica

La memoria viene allocata automaticamente in un processo mediante una dichiarazione esplicita di un identificatore che rappresenta una variabile o una costante che appare nella lista dei parametri di una function.

Tale memoria viene automaticamente deallocata quando il processo termina.

Allocazione dinamica

La memoria è allocata e/o deallocata in un processo mediante delle istruzioni particolari definite in quel processo. Di norma, tale memoria non è disponibile prima che l’opportuna istruzione sia posta in essere e potrebbe continuare o meno ad esistere dopo che il processo è terminato.

Allocazione dinamica (segue)

Una variabile allocata dinamicamente è detta variabile dinamica: per introdurre una tale variabile è necessario servirsi di un puntatore.

Puntatori

Come è noto in un computer la memoria è organizzata in celle consecutive, ciascuna della grandezza di un byte. Queste celle possono essere gestite singolarmente (ad esempio, per oggetti di tipo char che occupano un 1 byte, o a gruppi (ad esempio, per oggetti di tipo float che occupano 4 byte, …).

Ad ogni oggetto viene associato un indirizzo di memoria che rappresenta il primo byte della zona ad esso riservata.

Puntatori (segue)

Definiamo puntatore una variabile che contiene l’indirizzo di un’altra variabile: la variabile p conservata, ad esempio a partire dall’indirizzo 1000, contiene l’indirizzo 3424 del primo byte della variabile float pi che è un numero reale.

Questa situazione viene descritta scrivendo: vedi figura.


Puntatori (segue)

Per identificare una variabile di tipo puntatore si usa un * come suffisso al tipo che la variabile rappresenta, come mostrato in figura.

L’istruzione float* p associa alla variabile puntatore di nome p un indirizzo a partire dal quale si prevede un’occupazione di memoria necessaria per il tipo float.

Per associare alla variabile p ad esempio il valore contenuto in pi si pone p=&pi. Ovviamente pi deve essere dello stesso tipo di p.


Puntatori (segue)

Abbiamo che anche la variabile q punta allo stesso indirizzo purchè sia dello stesso tipo di p: l’assegnazione sta ad indicare che l’indirizzo contenuto in p viene copiato in q (che vale ancora 3424) e che punta, quindi, allo stesso contenuto.


Puntatori (segue)

Ad esempio se alla variabile float n=44.5 è stata assegnata la cella 0×0064fd5 (in esadecimale) in memoria si avrà la situazione qui mostrata.

Ad esempio se alla variabile float n=44.5 è stata assegnata la cella 0x0064fd5 (in esadecimale) in memoria si avrà la situazione qui mostrata.


Puntatori (segue)

Se ora dichiariamo la variabile puntatore float* pn avremo che in memoria verrà allocato un byte per pn.

Se ora dichiariamo la variabile puntatore float* pn avremo che in memoria verrà allocato un byte per pn.


Puntatori (segue)

Ricapitolando definiamo puntatore una variabile che contiene l’indirizzo di un’altra variabile: la variabile p conservata a partire dall’indirizzo 1000, contiene l’indirizzo 3424 del primo byte della variabile float pi che è un numero reale.


Puntatori (segue)

Definite due variabili puntatore p e q ad un float;

float* p, float* q;

Supponiamo che esse abbiano come indirizzo (ad es. 1000 e 2000).
Inizialmente il valore di p è nullo.

Posto p = &pi

alla variabile puntatore p viene associato l’indirizzo della variabile pi ottenendo la situazione vista prima (l’operatore & è l’operatore di indirizzo );

Posto q = p;

Nell’indirizzo della variabile puntatore q, che deve essere dello stesso tipo di p, viene scritto lo stesso indirizzo contenuto in p (che vale ancora 3424 ) e che punta allo stesso contenuto.

Puntatori (segue)

Il simbolo * posto vicino al tipo della variabile è l’operatore di dichiarazione di puntatore;

la scrittura Tipo* p indica che p è una variabile di tipo puntatore che punta a una variabile di tipo Tipo;

poiché il puntatore fornisce l’indirizzo soltanto del primo byte, il sistema conosce esattamente la sua lunghezza soltanto attraverso il tipo.

È assolutamente vietato far puntare un puntatore ad un oggetto che non sia dello stesso tipo di quello proposto nella dichiarazione;

per esempio, tenendo presente le dichiarazioni float* p; int* q il compilatore segnala errore ad una assegnazione del tipo q=p

ATTENZIONE alla istruzione float* p, q;
p è un puntatore ad un float mentre q è una variabile float.

Puntatori (segue)

Assegnato un puntatore, come si fa ad ottenere l’oggetto da esso puntato?

Se ap punta ad a, possiamo ottenere il valore di a direttamente da ap: l’espressione *ap calcola il valore di a.

Tale operatore viene detto operatore di direzione o operatore di derenfereziazione.

Puntatori (segue)

L’istruzione cout<<*p stamperà il contenuto dell’oggetto puntato da p, cioè 3.1415,

Mentre cout<<p
stamperà il valore del puntatore p in esadecimale, cioè l’indirizzo a cui punta p (es. 3424 in esadecimale ).
Es. il codice che segue produce l’output di figura.

int main() {

int a;
int *ap;
cout<<"a="; cin>>a;
ap=&a;
cout<<"indirizzo di a = "<<&a<<" valore di a = "<<
cout<<"indirizzo di ap = "<<&ap<<" valore di ap = "<<
cout<<" valore a cui punta ap = "<<*ap<
system("pause")

}


Puntatori (segue)

Le operazioni che avvengono in memoria sono illustrate in figura.

int a; int *ap;
cin>>a;

ap=&a;
cout<<"indirizzo di a  = "<<&a<<"  valore di a   = ""<<a<<endl;
cout<<"indirizzo di ap = "<<&ap<<"  valore di ap = "<<ap<<endl;
cout<<"  valore a cui punta ap = "<<*ap<<endl;


Puntatori (segue)

E’ possibile assegnare ad un puntatore il valore 0 (o la costante simbolica NULL) in tal caso il puntatore non punta a nulla.

L’indirizzo è di norma composto da tutti zeri e non viene allocata memoria.

int* p=0; // p è un puntatore nullo che non punta a nessun oggetto.

Il puntatore nullo non può essere dereferenziato: i risultati sono imprevedibili ed il programma potrebbe andare in crash.

Puntatori (segue)

È possibile definire puntatori a vari livelli come mostrato di seguito:

{   int n=44;
cout<<"    n= "<<n<<endl;
cout<<"    &n= "<<&n<<endl;
int* pn=&n;     // pn contiene l'indirizzo di n
cout<<"    pn= "<<pn<<endl;
cout<<"    &pn= "<<&pn<<endl;
cout<<"    *pn= "<<*pn<<endl;
int** ppn=&pn ;         //ppn contiene l'indirizzo di pn
cout<<"    ppn= "<<ppn<<endl;
cout<<"    &ppn= "<<&ppn<<endl;
cout<<"    *ppn= "<<*ppn<<endl;
cout<<"    **ppn= "<<**ppn<<endl;
}


Puntatori (segue)

Se si vuole che la cella puntata non possa essere modificata, allora è utile introdurre la clausola const davanti alla definizione ottenendo il puntatore a costante.

…………….
int n=3;
const int* p1; // p1 punta ad un intero costante
p1=&n; // viene segnalato un errore perché la cella n non può essere modificata

Dall’esempio si trae inoltre che un oggetto dichiarato costante può essere puntato soltanto da un puntatore costante.

Allegato puntatori.

Puntatori (segue)

Un puntatore si dice costante se l’indirizzo non può essere modificato.

int n,m,n1=10;
int * const p1= &n1 ;

p1 = &m ; // l’indirizzo di p1 non può essere modificato: ERRATO
*p1=m; //ma il suo contenuto può essere modificato: CORRETTO

const int* const p2=&n1; // non è possibile modificare né l’indirizzo di p2 né il suo contenuto

Puntatori ed array

Quando il compilatore incontra un’espressione tipo a[i] , dove a è un array di n elementi ed i il suo indice, genera il codice per calcolare l’indirizzo di a[i] .
Ad esempio, per controllare l’espressione

a[i] < a[i+1]

il compilatore deve prima calcolare gli indirizzi di a[i] e di a[i+1] e poi eseguire il confronto tra i due valori.

Puntatori ed array (segue)

Vediamo il calcolatore come opera.

Innanzitutto l’array deve essere di un Tipo dichiarato (per es. int o double).

Poiché l’array è rappresentato in memoria come una sequenza di byte, il nome a dell’array rappresenta il suo indirizzo-base, cioé l’indirizzo del suo primo elemento, a[0].

Inoltre, tutte le componenti dell’array hanno la stessa lunghezza in byte (che possiamo determinare con sizeof(Tipo)), per cui gli indirizzi dei vari elementi dell’array possono essere calcolati come

a + i x sizeof(Tipo) per i=0, 1, 2 ………., n

Puntatori ed array (segue)

Il compilatore sostituisce ogni riferimento ad a[i] con il calcolo precedente eseguendo le operazioni di addizione e moltiplicazione.

Una maniera più efficiente di operare è quello di inizializzare una variabile puntatore all’indirizzo-base dell’array e poi aggiungere la quantità sizeof(Tipo) ad ogni passo del ciclo.

Ciò può essere ottenuto in maniera semplice scrivendo

*(a + i)

il compilatore:

  1. determina l’indirizzo del primo elemento dell’array a;
  2. aggiunge i volte la grandezza del Tipo dell’elemento;
  3. restituisce l’indirizzo totale.

Puntatori ed array (segue)

Il codice Mostra codice e l'output in figura mostrano gli indirizzi e il contenuto dell'array sommato passo passo.

Allegato scorrArrayPuntatori


Puntatori ed array (segue)

A titolo esemplificativo riscriviamo le function inserite nel file InsertArray.h.

Ricordiamo che la chiamata del vettore deve essere fatta con un puntatore.

Puntatori ed array (segue)


Puntatori ed array (segue)

Il codice riscritto è illustrato in figura.

Allegato InsertArrayPunt.h


  • Contenuti protetti da Creative Commons
  • Feed RSS
  • Condividi su FriendFeed
  • Condividi su Facebook
  • Segnala su Twitter
  • Condividi su LinkedIn
Progetto "Campus Virtuale" dell'Università degli Studi di Napoli Federico II, realizzato con il cofinanziamento dell'Unione europea. Asse V - Società dell'informazione - Obiettivo Operativo 5.1 e-Government ed e-Inclusion