Premi qui per scaricare il PDF
L’accesso standard alle variabili avviene tramite l’identificatore:
int a,b;
Prendi il valore contenuto nella cella identificata da b e memorizzalo nella cella identificata da a:
a = b;
- Dimensione in memoria. (gli interi vengono rappresentati su 2 byte, i caratteri su 1 byte, i reali su 4 byte).
Sizeof(variabile) → quante celle (ogni cella è un byte) vengono occupate da una variabile
Variabile
Si dicono automatiche, il calcolatore si occupa di dargli una posizione in memoria.
Ogni variabile è caratterizzata da:
- Ingombro → grandezza della locazione di memoria (espressa in numero di celle/byte). E’ immutabile, un int rimane int.
- Indirizzo → è l’indirizzo della locazione di memoria associata alla variabile, cioè il numero d’ordine della posizione della prima cella tra quelle associate alla variabile. E’ immutabile, perchè indica una posizione in un elenco di celle di RAM;
- Valore → è il valore contenuto nella locazione di memoria associata alla variabile. Il valore muta durante l’esecuzione del programma.
Quando una variabile è a sinistra di un assegnamento, la macchina usa il suo indirizzo (cioè la posizione della prima cella dell’ingombro di RAM riservato per la variabile) per andarne a modificare il valore.
Quando è a destra, la macchina usa il valore della variabile.
Gli identificatori servono al programmatore per distinguere le variabili, ma per accedere alla RAM, l’esecutore usa gli indirizzi.
In C, per conoscere l’indirizzo delle locazioni di memoria associate alle variabili, si utilizza l’operatore &. Per inserire un puntatore nella printf, si utilizza il comando %p (formato pointer, equivale a %d, %c, %s).
int main()
{
int x = 3;
printf("Indirizzo di x: %p \\n", &x); //%p indica il puntatore
printf("Il valore di x: %d \\n",x);
}
variabili dinamiche → Possono esistere anche delle variabili senza nome (hanno solo un’occupazione in memoria), alle quali si accede tramite un puntatore.
Variabili puntatori
Sono delle variabili automatiche (il calcolatore assegna la posizione in memoria al momento dell’esecuzione). Le variabili puntatore sono variabili il cui valore è l’indirizzo di un’altra variabile (la posizione di memoria).
int cont = 1275;
int * punt; //Sintassi per dichiarazione puntatori
punt = &cont
La variabile puntatore, prende l’indirizzo in memoria della variabile cont e lo assegna alla variabile puntatore punt. (I puntatori possono puntare a int, char, float, struct ecc…).
typedef int* intPointer; //Definisco un tipo puntatore
intPointer myPtr;
int* Pointer; //Abbreviazione
Puntatori a dati di tipo diverso sono variabili di tipo diverso.
Deferenziazione
*myPtr;
Permette di estrarre il valore della variabile puntata dal puntatore che è argomento dell’operatore. Restituisce il valore e non l’indirizzo della variabile puntata.
int x = 3;
int*p = &x //Inizializzo p
printf("Il valore di x è %d\\n", *p); //restituisce il valore di x, non l'indirizzo
Esempio
typedef int* punt;
punt p;
int i = 5, j = 9;
p = &j; //Assegno a p l'indirizzo di j
*p = i; //Cambio il valore della variabile j con quello di i
++i; //incremento i
i = *p; //Assegno alla variabile i, il valore di *p che è 5
(*p) ++; //Equivale a j=j+1. *p è ora 6
p = &i; //Assegno a p l'indirizzo di i
*p = j; //Assegno a *p, che corrispone a i il valore di j, che in questo caso è 6
Riassunto
La dichiarazione di una variabile p di tipo puntatore a un intero
int *p;
Con &i si denota l’indirizzo della variabile i. & restituisce l’indirizzo di una variabile. (& è chiamato operatore di referenziazione)
p = &i;
L’operatore opposto è *, che restituisce il valore puntato. ( è chiamato operatore di dereferenziazione)
i = *p;
Esempio 2
int *p, *q, x, y;
p = &x;
q = &y;
//ORA p punta a Y
p = q; //Si impone che il puntatore p punti alla stessa variabile a cui punta q
//Equivale a x = y
*p = *q; //Il valore della variabile a cui punta p, prende il valore della variabile a cui punta q
NULL
- Il valore iniziale di un puntatore dovrebbe essere la costante speciale NULL;
- NULL significa che non ci si riferisce ad alcuna cella di memoria;
- Deferenziando NULL si ha un errore di esecuzione.
NULL → costante simbolica che rappresenta un valore speciale che può essere assegnato a un puntatore, NULL rappresenta il valore 0.
Test di nullità di un puntatore
if (p == NULL)
//OPPURE
if (!p)
Test di NON nullità
if (p != NULL)
//OPPURE
if (p)
Esercizio 1
Assegnare a due puntatori l’indirizzo degli elementi con valore minimo e massimo in un array
#include
#define LUNGHEZZA 100
int main()
{
int i, ArrayDiInt[LUNGHEZZA];
int *PuntaAMinore = NULL, *PuntaAMaggiore = NULL;
//Acquisizione array di interi
//CERCA IL VALORE MINIMO DELL'ARRAY
PuntaAMinore = &ArraydiInt[0];
for (i=1; i<LUNGHEZZA; i++)
if(ArrayDIInt[i] < *PuntaAMinore)
PuntaAMinore = &ArraydiInt[i];
//CERCA IL VALORE MASSIMO DELL'ARRAY
PuntaAMaggiore = &ArraydiInt[0];
for (i=1; i<LUNGHEZZA; i++) if(ArrayDIInt[i] > *PuntaAMaggiore)
PuntaAMaggiore = &ArraydiInt[i];
return 0;
}
Esercizio 2
Date le seguenti dichiarazioni di variabile, descrivere cosa comporta nella memoria del calcolatore l’esecuzione delle istruzioni riportate di seguito
int *pPtr = NULL, *qPtr = NULL, *rPtr = NULL;
int x = 1, y = 5;
pPtr = &x; qPtr = &x; rPtr = &y; //Assegna gli indirizzi ai puntatori
*pPtr = *pPtr * 3 + 1; //Equivale a x = x*3+1 -> 4
qPtr = rPtr; //qPtr ora punta a y, come rPtr
printf(" %d, %d, %d ", *pPtr, *qPtr, *rPtr); //Stampa 4, 5, 5
*rPtr = *qPtr; //Equivale a y = y
rPtr = pPtr; //rPtr ora punta a x
pPtr = qPtr; //pPtr ora punta a qPtr che è uguale a y
printf(" %d, %d, %d ", *pPtr, *qPtr, *rPtr); //Stampa 5, 5, 4
Puntatori e tipo delle variabili puntate
Il compilatore segnala l’uso dei puntatori a dati di tipo diverso da quello a cui dovrebbero puntare:
- In forma di warning: sono errori potenziali.
I tipi “puntatore a tipo x” e “puntatore a tipo y” sono diversi tra loro
Il tipo void*, però, è compatibile con i puntatori a tutti i tipi:
- il suo significato è quello di indicare valori di tipo indirizzo di memoria senza alcuna ulteriore specificazione. Punta a qualcosa di cui non conosco il tipo
- (Dobbiamo fare un casting per utilizzare il puntatore void).
int *pPtr; void *superPtr; superPtr = pPtr; //NON SI PUO' FARE (int *)superPtr = pPtr; //Casting di variabile puntatore a int *
Esempio puntatore void
#include int main () { int x = 3, y = 0, *myPtr; void *superPtr; myPtr = &x; //Assegno a una variabile punt a int, l'indirizzo di un int printf("\\n Indirizzo di x: %p", myPtr); printf("\\n Valore di x: %d", x); superPtr = (void*)&y; //spiegazione sotto superPtr = (void*)myPtr; //copia di puntatori con promozione di int* a void* printf("\\n Valore di superPtr: %p\\n", superPtr); y = *((int) superPtr); //Spiegazione sotto printf("\\n Valore di y: %d \\n", y); return 0; }
superPtr = (void)&y;* → una variabile di tipo void* puo’ contenere l’indirizzo di una variabile di tipo qualunque. (E’ buona abitudine evidenziare sempre nel codice le promozioni (come in questo caso) o le coercizioni di valori di tipo diverso) → evidenziare il casting. Se non lo mettiamo, il casting è eseguito automaticamente.
y = ((int) superPtr); → Assegno a una variabile il valore della cella di memoria con indirizzo indicato da superPtr, mettendo in evidenza che il contenuto della variabile superPtr deve essere interpretato come un valore di tipo (int ). E’ una coercizione di tipo da void* a int*
I puntatori const
Le variabili puntatore possono essere dichiarate const
- Possono essere const (NON sovrascrivibili) sia i valori referenziati, sia il valore dell’indirizzo contenuto nella variabile.
int k = 0, a[] = {1,2,3}; //Valori modificabili
const int = 0; //Variabile di valore costante 0
int* const m = &k; //Puntatore costante a int - valore di m è costante
const int* n = &i; //puntatore a un valore costante di tipo int - Il contenuto della cella di memoria referenziata da n (quella di i) è costante
int* const p = a; //puntatore costante a int (array, cella a[0])
const int* const q = b; //Puntatore costante a un valore costante di tipo int
Gli statement che indicano una dichiarazione di variabili, hanno significato leggendoli da destra verso sinistra.
- int const m = &k;* → Puntatore costante a int – valore di m è costante. NON SI PUO’ FARE m++
- const int n = &i;* → puntatore a un valore costante di tipo int – Il contenuto della cella di memoria referenziata da n (quella di i) è costante. NON SI PUO’ FARE →**n = 5, n++. SI PUO’ FARE → n++
- int const p = a;* → puntatore costante a int (punta ad un array, cella a[0]). Equivale a int * const p = &a[0]. Il valore di a non può essere cambiato.
Array e Puntatori
In C esiste una parentela tra array e puntatori. Il nome di un array è una costante (simbolica) di tipo puntatore, di valore “indirizzo della prima cella allocata per l’array”.
int v[3];
definisce v come una costante simbolica il cui tipo è int* const (punto 1 del paragrafo sopra), cioè un puntatore costante a un intero.
Perciò v[i] è equivalente a *(v + i)
- Calcolo dello spiazzamento nel vettore grazie all’aritmetica dei puntatori:
- L’indirizzo della cella i-esima è: v + i, la dereferenziazione di tale valore, permette di accedere al contenuto della cella i-esima.
Esempio

Doppio puntatore → è una variabile puntatore che contiene l’indirizzo di un’altra variabile puntatore
int *pPtr = NULL;
int ** pDblptr = NULL; //doppio puntatore
int * vett[3] = {NULL, NULL, NULL}; //ogni cella del vettore è un puntatore a int
int x = 1, y = 5;
pPtr = &x;//Assegno l'indirizzo di x a pPtr
pDblptr = &pPtr;//Assegno a pDblptr, l'indirizzo di pPtr
vett[0] = vett[1] = vett[2] = pPtr; //Le celle del vattore puntano a pPtr(indirizzo di x), il quale punta a x

*pDblptr = &y; //Sovrascrivo l'indirizzo di y al contenuto di pDblptr che contiene pPtr
vett[1] = pPtr; //La seconda cella dell'array punterà a pPtr che ora punta a y
vett[2] = *pDblptr; //La terza cella dell'array punterà a pDblptr che punta a pPtr e quindi a y

**pDblptr = 6; //Prendi il valore 6 ed inseriscilo in y
**vett = 7; //equivale a *vett[0] = 7. Inserisco il valore 7 in x
*(vett[2]) = *vett[1] + 1 + *vett[0]; //y = 6 + 1 + 7 -> y = 14

Struct e Puntatori
typedef struct
{
int PrimoCampo;
char SecondoCampo;
}TipoDato;
TipoDato t;
TipoDato *p = &t;
int *y;
y = &(t.PrimoCampo); //La posizione di memoria dell'attributo PrimoCampo, della variabile t
(*p).Primocampo = 12; //Assegnamo al campo PrimoCampo il valore 12, equivale a t.PrimoCampo = 12
p -> PrimoCampo = 12; //è equivalente alla scrittura di sopra
Il C assegna un indirizzo ad ogni cella di memoria che contiene 1Byte. L’indirizzo di una variabile rappresenta quindi l’indirizzo del primo byte della sequenza di celle utilizzata per rappresentare il valore.
L’operatore sizeof() dà il numero di byte occupati da un tipo o una variabile.
double d, A[5], *p =&d;
sizeof(double); //restituisce 8
sizeof(A); //8 * 5 = 40
sizeof(*p); //E' grande quanto un double, quindi 8
sizeof(A[2]); //E' un double, quindi restituisce 8
sizeof(p); //Restituisce 4 in un calcolatore a 32 bit Perchè è il numero di byte che ci servono per rappresentare un indirizzo in memoria
Aritmetica con puntatori
Il C permette somme e sottrazioni con i puntatori
p += 5; //p = p + 5
L’effetto è quello di avere il valore p incrementato di un multiplo dell’ingombro in memoria del tipo puntato:
p=p+5 → p = 5 * (sizeof(TipoPuntatoDa_p));
Anche per i vettori, visto che v[3] = 2 equivale a (v+3) = 2; Il calcolatore modifica il valore effettuando la comma come
*v + 3**sizeof(int)
- Se p e q puntatno a due diversi elementi di uno stesso array
int v[5] = {14,16,8,9,12};
int *p = &v[3], *q = &v[1]; //p = 9, q = 16
La differenza p – q dà la distanza espressa in numero di celle dell’array tra gli elementi puntati. p – q = 2
int x[3] = {2,7,4}, *p = x; //Prende l'indirizzo di memoria del primo elemento del vettore p=&x[0]
(*p)++; //Mette il valore 3 nella prima cella dell'array (*p) = (*p)+1
*(p++); //p = p+1; (*p) -> E' uguale a 7, cioè il secondo elemento dell'array
Array multidimensionali
Per calcolare lo spiazzamento, l’esecutore deve conoscere le dimensioni intermedie.
int m[R][C]
*(*(m+i)+j);//equivale a *(m+C*i+j) che equivale a m[i][j]
//Array tridimensionale
int p[X][Y][Z];
*(*(*(p+i)+j)+k);//equivale a *(p+Y*Z*i + Z*j+k) che equivale a p[i][j][k]