Guida al C/C++
autore: Orebla
xx/xx/xx
I puntatori:
La
memoria RAM del calcolatore non è altro che un insieme di locazioni di memoria;
per poter localizzare ciascuna locazione, ognuna di esse è identificata da un
numero, ovvero il suo indirizzo. Questo significa che:
(1) per scrivere qualcosa nella memoria dobbiamo conoscere l'indirizzo del punto
esatto in cui scrivere
(2) se conosciamo l'indirizzo di una data locazione possiamo leggere ciò che ci
sta scritto
Il C ci permette di accedere a tale indirizzo, ovvero di localizzare dove la
variabile sia stata effettivamente memorizzata.
Fino ad oggi abbiamo gestito le variabili all'interno dei blocchi di codice in
cui tali variabili erano visibili, quindi non c'è stata la reale necessità di
utilizzare l'indirizzo della locazione di memoria in cui i valori di tali
variabili erano stati memorizzati.
Ripasso: la sintassi da usare con i puntatori.
int main()
{char a='r'; //
allocazione di memoria
per le variabili
int numero = 45 ;
double var = 34.533;
char *pchar; //
dichiarazione dei puntatori
int *pint;
double *pdouble;
pchar = & a;
//
inizializzazione dei puntatori
pint = & numero;
pdouble = & var;
}
IMPORTANTE:
così come la dichiarazione di una variabile implica che venga riservata
(allocata) della memoria per la variabile stessa, così dichiarare
un puntatore implica che venga riservata della memoria per la
memorizzazione del puntatore, ma NON IMPLICA
che venga automaticamente riservata della memoria per una variabile
avente il tipo identico a quello cui fa riferimento il puntatore.
Ad esempio:
int num;
//
viene riservata memoria per la variabile num
int *pnum;
//
viene riservata memoria per un puntatore
int *altropunt; //
viene riservata ulteriore memoria per un altro puntatore
pnum = & num ; // assegno al
puntatore un valore pari all'indirizzo di num nella memoria
*pnum = 4 ;
/* con l'istruzione precedente ho inserito nella locazione di memoria cui fa
riferimento la variabile pnum, puntatore ad intero, il valore 4; nella medesima
locazione di memoria risiede la variabile num, quindi ho assegnato tale valore
alla variabile num */
*altropunt = 5;
/* Il puntatore altropunt non era stato
inizializzato e quindi il valore in esso contenuto, interpretabile come uno
degli indirizzi della memoria, è del tutto arbitrario. Non siamo perciò
autorizzati ad accedere alla corrispondente locazione di memoria; essa potrebbe
essere utilizzata, ad esempio, da un altro programma. Così facendo si esegue un
accesso illegale alla memoria */
L'unico metodo che conosciamo,
al momento, per riservare della memoria per il
programma, ovvero di effettuare degli accessi legali alla memoria stessa, è
quello di dichiarare una variabile. Per ora, quindi,
ricordiamoci di >usare un puntatore SOLO se questo fa
riferimento ad una variabile già dichiarata all'interno del codice.
// inizio esempio
#include <stdio.h>
#include <stdlib.h>
int main()
{int num1,num2;
double var;
int *punt;
double *pdouble;
printf("inserisci un numero maggiore di 3000");
// l'struzione che segue è errata !
scanf("%d",punt);
// ora correggiamo il codice
punt = & num1 ; // punt ora
contiene l'indirizzo di un'area di memoria cui il programma può accedere
printf("\n causa accesso illegale, inserisci un altro valore");
// ora l'istruzione è corretta !
scanf("%d",punt);
// il valore della variabile num1 è stato inizializzato grazie al puntatore
// ora posso quindi passare num1 alla funzione che inizializza il generatore di
numeri casuali
srand(num1);
// ora uso punt per visualizzare il valore della
variabile num2
punt = & num2;
num2= rand() % 30 ;
printf("Il valore di num2 e' pari a : %d",*punt);
// posso usare punt (puntatore a int)
per inizializzare var (di tipo double) ?
punt = & var ; // e' lecito ? ->
pdouble = & var ;
printf("inserisci un valore decimale");
scanf("%lf", & var );
*pdouble = (*pdouble) * num2 ; // modifico il
valore di var usando pdouble
printf("il valore della variabile double e' %f", var );
printf("\n ed il suo indirizzo e' %p",pdouble);
/* NOTA: non ho avuto bisogno di indicare a printf()
quale sia il tipo di variabile cui fa riferimento il puntatore ... come mai ? */
return 0; }
// fine esempio
Regole che vanno scritte su un foglio da tenere vicino al letto e leggere tutte
le sere prima di addormentarsi:
è molto più
pericoloso usare un puntatore non inizializzato di quanto non sia usare
una variabile normale non inizializzata
|
|
mai assegnare ad un
puntatore l'indirizzo di una variabile di tipo diverso da quello
specificato nella dichiarazione del puntatore
|
Spostiamoci ora verso un impiego un po' più interessante dei puntatori:
Ripensiamo a quali operazioni vengono compiute quando eseguiamo una chiamata ad
una funzione.
Quando, viceversa, la funzione riceve solo il valore di una variabile (valore
che viene copiato in una variabile locale, visibile solo alla funzione stessa)
essa non è in grado di modificare il valore della variabile.
Se una funzione riceve come parametro l'indirizzo di una data variabile, la
funzione stessa è in grado di localizzare la variabile nella memoria, di
leggerne il valore e di alterarlo.
// inizio del codice
#include "myfun.h"
#define MAX 30
{int main()
int temp,vettore[MAX],i, scambi;
char c1,c2;
printf("\n inserire due caratteri per inizializzare il
generatore di numeri casuali\n");
printf("\nprimo carattere :");
c1=mygetchar();
printf("\nsecondo carattere :");
c2=mygetchar();
srand(c1*c2*c2*c1);
for(i = 0; i< MAX; i++)
{vettore[i] = int_rand(0,500);
printf("\nil valore nella posizione %d
del vettore e\' %d",i,vettore[i]);}
scambi = 1;
while(scambi)
{scambi = 0;
for( i = 0; i< MAX -1; i++)
{if( vettore[i] <= vettore[i+1] )
{;}
else
{scambi = 1;
temp = vettore[i];
vettore[i]
= vettore[i+1];
vettore[i+1]
= temp;}
}
}
// ora stampo il risultato
for(i = 0; i< MAX ; i++)
{printf("\nil valore nella posizione %d del vettore
e\' %d",i,vettore[i]);}
return 0;}
// fine del codice
Per
migliorare la leggibilità del codice, implementiamo una funzione che scambia
tra loro i valori di due variabili e poi inseriamola nell'algoritmo di
ordinamento. La funzione deve ricevere come parametri gli indirizzi delle
variabili, altrimenti non riesce a modificare i loro valori. Per rendere
noto
a main() il risultato dello scambio non possiamo sfruttare l'istruzione
return,
perché con essa possiamo restituire al massimo un valore per volta.
// esempio 1: NON FUNZIONA !
void scambia(int,int);
void scambia(int a,int b)
{int temp;
temp = a;
a = b ;
b = temp;}
// esempio 2: FUNZIONA ! (da inserire in myfun.h)
void scambia(int *,int *);
void scambia( int *pa, int *pb)
{int temp;
temp = (*pa) ;
(*pa) = (*pb)
;
(*pb) = temp ;
}
// inizio codice
#include "myfun.h"
#define MAX 30
int main()
{int a,b;
printf("inserisci due numeri interi:\n primo numero ->
");
scanf("%d",&a );
printf("\n secondo numero -> ");
scanf("%d",&b );
printf("i numeri inseriti sono : %d e %d",a,b);
// effettuiamo lo scambio
scambia( &a ,
&b );
printf("\n dopo lo scambio\n");
printf("i numeri inseriti sono : %d e %d",a,b);
return 0;}
// fine codice
// inizio del codice
#include "myfun.h"
#define MAX 30
int main()
{int temp,vettore[MAX],i, scambi;
char c1,c2;
printf("\n inserire due caratteri per inizializzare il
generatore di numeri casuali\n");
printf("\nprimo carattere :");
c1=mygetchar();
printf("\nsecondo carattere :");
c2=mygetchar();
srand(c1*c2*c2*c1);
for(i = 0; i< MAX; i++)
{vettore[i] = int_rand(0,500);
printf("\nil valore nella posizione %d
del vettore e\' %d",i,vettore[i]);}
scambi = 1;
while(scambi)
{scambi = 0;
for( i = 0; i< MAX -1; i++) // mi fermo prima altrimenti
"esco" dal vettore
{if( vettore[i] <= vettore[i+1] )
{;}
else
{scambi = 1;
scambia(&
(vettore[i]) ,& (vettore
[i+1]) );
}
}
}
// ora stampo il risultato
for(i = 0; i< MAX ; i++)
{printf("\nil valore nella posizione %d del vettore
e\' %d",i,vettore[i]);}
return 0;}
// fine del codice
Possiamo
però spingerci un po' più in là ...
void if_scambia(int *,int
*,int * );
void if_scambia( int *pa,
int *pb,int *pscambia ) //
supponiamo di disporle in ordine crescente
{int temp;
if( *pa < *pb )
{temp = (*pa) ;
(*pa) = (*pb)
;
(*pb) = temp ;
*pscambia = 1; }
else
{*pscambia = 0;}
}
// inizio del codice
#include "myfun.h"
#define MAX 30
int main()
{int temp,vettore[MAX],i, scambi;
char c1,c2;
printf("\n inserire due caratteri per inizializzare il
generatore di numeri casuali\n");
printf("\nprimo carattere :");
c1=mygetchar();
printf("\nsecondo carattere :");
c2=mygetchar();
srand(c1*c2*c2*c1);
for(i = 0; i< MAX; i++)
{vettore[i] = int_rand(0,500);
printf("\nil valore nella posizione %d
del vettore e\' %d",i,vettore[i]);}
scambi = 1;
while(scambi)
{scambi = 0;
for( i = 0; i< MAX -1; i++)
{ if_scambia(
& (vettore[i]), & (vettore[i+1]),
& scambi); }
}
// ora stampo il risultato
for(i = 0; i< MAX ; i++)
{printf("\nil valore nella posizione %d del vettore
e\' %d",i,vettore[i]);}
return 0;}
// fine del codice
Regoletta
per ricordarsi in che modo si indichi nel codice il valore di una variabile,
noto il puntatore alla variabile. Pensiamo alla dichiarazione di un puntatore:
double *puntatore;
inseriamo delle parentesi per raggruppare i 3 elementi
double( * puntatore ) ;
(double *) puntatore
;