Lezione 3 - 11/03/2026: puntatori
I puntatori sono variabili che vanno dichiarate in C/C++ perché al loro interno vengano memorizzati dei valori, i quali sono i valori degli indirizzi di altre variabili.
Quindi a prescindere le variabili vanno dichiarate.
Esempio: supponiamo di aver dichiarato una variabile intera a, e che voglia memorizzare il suo indirizzo in un’altra variabile.
int a;
int * b; //dichiarazione della variabile b ad essere una variabile di tipo puntatore ad intero, la quale può quindi contenere l’indirizzo di un intero
B allora può contenere un indirizzo ad intero
b=&a;
Il tutto va fatto nel main, dichiarate due variabili intere a e b esse occupano: a 4 byte, mentre b (che è la variabile puntatore, non *b!) occupa sempre solo 4 byte, qualunque sia l’indirizzo della variabile che deve contenere. Bisogna solo stare attenti alla macchina su cui stiamo lavorando: su una macchina di 64 bit, le variabili puntatore occupano 64 bit (punto di vista più hardware che software).
Le variabili puntatore occupano lo stesso spazio di memoria, indipendentemente dalla variabile che devono contenere: lo verifico sempre con sizeof().
Affinché questo codice possa essere correttamente compilato, è necessario che io ad a dia un valore? No, perché il fatto di avere un indirizzo è indipendente dal fatto che io gli abbia dato un valore, quindi può funzionare lo stesso. L’unica cosa necessaria è mettere qualcosa su b, e ciò non dipende da noi, ma dal fatto che il tutto abbia avuto effetto su int a;. Se avessi scritto in a = 0;, ai fini di b non cambia nulla. Questa assegnazione può significare che ha trovato memoria sul byte 4, l’indirizzo di a vale 4 e può essere scritto nell’indirizzo di b.
Se il codice fosse:
int a;
int * b;
b=&a;
*b=5;
Quando il simbolo asterisco segue uno specificatore di tipo (int *b), ha valore dichiarativo: faccio capire che tipo di variabile è b.
Quando l’asterisco precede il nome di una variabile ma non segue lo specificatore di tipo, esso si chiama operatore di dereferenziazione (dereferenziare=indirizzare=risolvere un problema di indirizzo).
Funzione scambia
Come andrebbe scritto:
# include <stdio.h>
void scambia(int*, int*);
int main(){
int a, b;
a=3; //Essendo una void il condominio non è nulla infatti
b=5; la void non mi restituisce nulla
//I valori di a e b non sono valori di indirizzi, ecco
scambia(&a, &b); perché per passare agli indirizzi dobbiamo mettere le &
return 0; davanti ad a e b
}
void scambia(int *A, int*B){
int temp;
temp=*A;
*A=*B; //Sinonimo di a all’interno di scambia è *A è come se scrivessi *&a
*B=temp; // non possiamo avere return perché c’è la void.
}
Tra parentesi tonde ci sono sempre valori che devono essere attualizzati (&a=4, &b=8).
A è il valore dell’indirizzo di a, B è il valore dell’indirizzo di b (su A viene scritto 4, su B viene scritto 8). La funzione scambia, nella seconda parte, potrà lavorare solo sulle variabili che le sono note.
Inserisco la variabile di appoggio temp, facendo sì che la funzione possa lavorare su 3 variabili: temp, A e B. L’effetto di temp sarà quello di scambiare 4 e 8? No, perché vogliamo scambiare 3 e 5 che stanno nel main; prima dello scambio possiamo mettere una printf() che ci dice quanto valgono a e b prima dello scambio.
Stampo quindi 3 e 5, ma dopo lo scambio voglio stampare 5 e 3.
Quindi, alla fine, non abbiamo scambiato 4 e 8, ma a e b.
Se avessi dovuto fare queste righe di codice con temp dichiarate nel main (PER ASSURDO):
#include <stdio.h>
void scambia(int*, int*);
int main () {
int a, b, temp;
a = 3;
b = 5;
temp = a;
a = b;
b = temp; // (b = 3)
}
Dobbiamo vedere la stessa cosa nella funzione scambia.
Per accedere ad a nella funzione scambia lo faccio tramite *A.
#include <stdio.h>
void scambia(int*, int*);
int main () {
int a, b;
a = 3;
b = 5;
scambia(&a, &b);
return = 0;
}
// scambio dei due valori (ho bisogno di una variabile di appoggio, temp)
void scambia(int *A, int *B) {
int temp;
temp = *A;
*A = *B;
*B = temp;
}
Per fare un ulteriore test sulla correttezza sintattica devo osservare queste tre righe di codice, affinché il valore della variabile che sta a sinistra corrisponda al valore della variabile che sta a destra (temp=*A;, *A=*B;, *B=temp;); esse sono corrette quindi solo se il tipo di valore in temp corrisponde al tipo di valore in *A (devono essere entrambi due interi).
Nella versione sintatticamente corretta ma logicamente sbagliata
Immagino di lavorare su due valori interi, e non due puntatori ad interi (vanno tolti tutti gli asterischi). La funzione scambia si aspetta di ricevere due numeri interi; nel suo funzionamento, non cambiano i valori di a e b, bensì cambiano i valori di A e B.
Quindi, se io volessi far funzionare questa cosa per assurdo, la printf del prima la dovrei mettere nel main e la printf del dopo la dovrei mettere in scambia -> Perché il main può vedere solo a e b, che non vengono scambiate. Tanto la printf anche se la metto dopo della scambia stampa la stessa cosa.
Quindi se volessi far scrivere delle cose, visto che il testo lo decido io, le devo far scrivere nella scambia.
Generalmente questa cosa non la si vuole fare perché vogliamo che il main (gestisca sempre input, output) lasci il controllo alla funzione scambia che deve solo scambiare, non deve stampare i valori scambiati.
# include <stdio.h> Il main dichiara le variabili importanti e fa le azioni di interfaccia con
void scambia(int, int); il programmatore (gli fa scrivere prima dello scambio e dopo lo scambio a quanto è uguale).
int main() Il resto lo demanda a funzioni servitrici.
{
int a, b; Più in là vedremo che il main carica un immagine e poi chiama la
a=3; funzione zoom e processing … e il main carica e stampa l’immagine
b=5; originaria, chiama la funzione trasformazione dell’immagine e stampa
scambia(a, b); l’immagine trasformata dalla funzione.
return 0; Quindi il controllo è sempre del main!
}
void scambia(int A, int B){
int temp; In C non è mai possibile cambiare il valore delle variabili locali al main, se questi
temp=A; vengono passati come valori. Non posso cambiare i valori di a e b nel main se li
A=B; passo per valore! Per poterli cambiare li devo passare attraverso i valori dei loro
B=temp; indirizzi.
} Questo è il meccanismo con il quale una funzione riesce ad agire su un’altra area.
Esercizio
Progettare da 0 una funzione (nome, cosa deve restituire…) su una struttura vettoriale di tipo vettore, affinché noi possiamo fare dell’enancement, cioè un aumento, con operazioni di manipolazione delle immagini, facendo conoscere all’algoritmo il valore di grigio più alto e quello più basso.
Abbiamo 3 pixel, che sono gli elementi di memoria dell’immagine, in un range che va da 0 a 255.
Se voglio esaltare i colori ed allontanare i livelli di grigio, faccio diventare 0 quello più grande (quindi 255); in questo modo, il nostro occhio riesce a vedere la differenza tra i 3 livelli di grigio, ma servono ovviamente degli algoritmi per conoscere i valori di minimo e di
-
Appunti lezione 1 Sistemi per la riabilitazione e la terapia assistita
-
Appunti lezione 2 Sistemi per la riabilitazione e la terapia assistita
-
Lezione 4 Sistemi per la riabilitazione e la terapia assistita
-
Lezione 6 Sistemi per la riabilitazione e la terapia assistita