Post on 25-Aug-2020
Corso di Laboratorio di Informatica
Ingegneria Clinica – BCLR
Unità 6
Passaggio parametri
puntatore
Domenico Daniele Bloisi
Docente
Ing. Domenico Daniele Bloisi, PhD
Ricercatore
Dipartimento di Ingegneria Informatica, Automatica
e Gestionale “Antonio Ruberti”
Via Ariosto 25
(adiacente Piazza Dante,
Manzoni
Tram 3 fermata via Labicana)
email: bloisi@dis.uniroma1.it
home page:
Laboratorio di Informatica
2014/2015
Passaggio parametri puntatore
Unità 6
Pagina 2
http://www.dis.uniroma1.it/~bloisi
Ricevimento
In aula, subito dopo le lezioni
Su appuntamento (tramite invio di una email)
presso:
Dipartimento di Ingegneria Informatica,
Automatica e Gestionale “Antonio Ruberti”,
via Ariosto 25 - II piano, stanza A209
Si invitano gli studenti a controllare regolarmente la
bacheca degli avvisi
http://www.dis.uniroma1.it/~bloisi/didattica/labinf1415.html#Avvisi
Pagina 3Laboratorio di Informatica
2014/2015
Passaggio parametri puntatore
Unità 6
Orari
Lunedì 12.00 – 13.30
Aula 4, via del Castro Laurenziano 7A
Martedì 14.00 – 17.15
Aula 15, Laboratorio Didattico via Tiburtina 205
Mercoledì 12.00 – 13.30
Aula 4, via via del Castro Laurenziano 7A
Pagina 4Laboratorio di Informatica
2014/2015
Passaggio parametri puntatore
Unità 6
Sommario – Unità 6
• Memoria, indirizzamento e puntatori• Tipo void * e conversioni sui puntatori
• Gestione dinamica della memoria
• Tempo di vita delle variabili allocate
dinamicamente
• Problemi di deallocazione della memoria
• Passaggio dei parametri tramite puntatori
Pagina 5Laboratorio di Informatica
2014/2015
Passaggio parametri puntatore
Unità 6
Tempo di vita delle variabili allocate
dinamicamente (1/2)
Per le variabili allocate dinamicamente il tempo di vita
corrisponde al periodo che va
Dal momento in cui la variabile viene allocata
Al momento in cui la variabile viene deallocata.
Il tempo di vita di una variabile allocata
dinamicamente è definito solo al momento
dell'esecuzione del programma.
Pagina 6Laboratorio di Informatica
2014/2015
Passaggio parametri puntatore
Unità 6
Tempo di vita delle variabili allocate
dinamicamente (2/2)
Se una variabile allocata dinamicamente non viene
deallocata esplicitamente, il suo tempo di vita si
prolunga fino alla terminazione del programma.
Può verificarsi il caso in cui una zona di memoria
allocata non sia più accessibile, in quanto non ci
sono variabili che la “riferiscono”.
Tale zona di memoria rimarrà inutilmente occupata
per tutta la durata del programma.
Pagina 7Laboratorio di Informatica
2014/2015
Passaggio parametri puntatore
Unità 6
Esempio: tempo di vita variabili
dinamiche
int* creaint(int a) {
int* temp = malloc(sizeof(int));
*temp = a;
return temp;
}
int main() {
int* pt2 = creaint(5);
printf("*pt2 = %d\n", *pt2);
}
Pagina 8Laboratorio di Informatica
2014/2015
Passaggio parametri puntatore
Unità 6
Esecuzione: Tempo di vita variabili
dinamiche
Il codice precedente stampa
*pt2 = 5
poiché la variabile allocata dalla funzione creaint
viene restituita alla funzione che l'ha invocata e
rimane accessibile anche al termine dell'esecuzione
della funzione.
Le variabili create dinamicamente non rispettano le
regole di campo di azione.
Pagina 9Laboratorio di Informatica
2014/2015
Passaggio parametri puntatore
Unità 6
Esempio Esaurimento della memoria
// sciupa Memoriaint *temp, k;
for (k = 1; 1 ; k++) {
printf("k = %d\n", k);
temp = malloc(sizeof(int));
}
L'esecuzione comporta una richiesta infinita di
memoria (si noti che la condizione del ciclo è sempre
verificata).
Pagina 10Laboratorio di Informatica
2014/2015
Passaggio parametri puntatore
Unità 6
Esecuzione Esaurimento della memoria
L’esecuzione produrrà una richiesta infinita di memoria che in
C comporta (dopo un numero di cicli piuttosto alto)
l’interruzione dell’esecuzione con un messaggio simile al
seguente:
dynamic(1320) malloc: *** mmap(size=16777216) failed
(error code=12) *** error: can’t allocate region
Nota: Ad ogni ripetizione del ciclo, la memoria allocata al ciclo
precedente risulta inaccessibile.
Pagina 11Laboratorio di Informatica
2014/2015
Passaggio parametri puntatore
Unità 6
Problemi di deallocazione della memoria
int *pt1;
pt1 = malloc(sizeof(int)); // allocazione della variabile
*pt1 = 1;
printf("*pt1 = %d\n", *pt1); // uso della variabile
free(pt1);
// la locazione di memoria rimane accessibile dopo free!!!printf("after free\n");
printf("*pt1 = %d\n", *pt1);
// la stessa locazione di memoria potrebbe essere ri-allocata!!!int *pt2 = malloc(sizeof(int));
printf("after malloc\n");
printf("*pt2 = %d\n", *pt2);
Pagina 12Laboratorio di Informatica
2014/2015
Passaggio parametri puntatore
Unità 6
Esecuzione
Pagina 13Laboratorio di Informatica
2014/2015
Passaggio parametri puntatore
Unità 6
Discussione Problemi di deallocazione
della memoriaNell'esempio precedente può verificarsi che la memoria rimanga accessibile anche dopo il rilascio (free) e addirittura
che il suo contenuto sia ancora immutato (anche se non è
garantito).
In aggiunta, la riallocazione immediata potrebbe portare ad avere la variabile pt2 nella stessa posizione della memoria
che aveva pt1 e quindi con lo stesso valore (anche questo
non è garantito).
In ogni caso, entrambe le situazioni sopra delineate vanno
evitate accuratamente, in quanto potrebbero generare errori e
comportamenti instabili del programma.
Pagina 14Laboratorio di Informatica
2014/2015
Passaggio parametri puntatore
Unità 6
Puntatori appesi
Si consideri il seguente frammento di codice
int *pt1;
pt1 = malloc(sizeof(int));
*pt1 = 1;
printf("*pt1 = %d\n", *pt1);
int *pt2;
pt2 = pt1;
printf("prima di free(pt1)\n");
printf("*pt2 = %d\n", *pt2);
free(pt1);
printf("dopo di free(pt1)\n");
printf("*pt2 = %d\n", *pt2);
Pagina 15Laboratorio di Informatica
2014/2015
Passaggio parametri puntatore
Unità 6
Questa situazione di puntatore appeso (dangling) deve
essere evitata assolutamente, in quanto fonte di errori difficili
da individuare.
Esecuzione Puntatori appesi
*pt1 = 1
prima di free(pt1)
*pt2 = 1
dopo di free(pt1)
*pt2 = 4001536
In questo caso, il rilascio della memoria puntata dalla variabile pt1 lascia la variabile pt2 puntare a una locazione di
memoria rilasciata e quindi suscettibile di modifiche arbitrarie
da parte del sistema operativo.
Pagina 16Laboratorio di Informatica
2014/2015
Passaggio parametri puntatore
Unità 6
Passaggio di parametri (1/2)
L’uso di variabili di tipo puntatore consente di
simulare la modalità di passaggio dei parametri per
riferimento.
Nel passaggio di parametri per valore, in genere
utilizzato per i dati di tipo primitivo, il parametro
formale può essere considerato come una variabile
locale che viene inizializzata al momento della
chiamata della funzione con il valore corrispondente
al parametro attuale.
Pagina 17Laboratorio di Informatica
2014/2015
Passaggio parametri puntatore
Unità 6
Passaggio di parametri (2/2)
Il passaggio di parametro per valore effettuato con
il tipo puntatore consente di aggirare una proprietà
cruciale del passaggio di parametri per valore, cioè
la garanzia che la funzione non abbia effetti sul
programma chiamante (a eccezione della
restituzione del valore calcolato).
Pagina 18Laboratorio di Informatica
2014/2015
Passaggio parametri puntatore
Unità 6
Esempio Passaggio parametri
puntatore
void swap(int *a, int *b) {
int temp;
temp = *a;
*a = *b;
*b = temp;
}
Pagina 19Laboratorio di Informatica
2014/2015
Passaggio parametri puntatore
Unità 6
int main (){
int x, y;
x = 12; y = 27;
printf("x = %d\n", x);
printf("y = %d\n", y);
swap(&x, &y);
printf("after swap\n");
printf("x = %d\n", x);
printf("y = %d\n", y);
}
Pagina 20
Esempio Passaggio parametri
puntatore
Laboratorio di Informatica
2014/2015
Passaggio parametri puntatore
Unità 6
Esecuzione
x = 12
y = 27
after swap
x = 27
y = 12
Pagina 21Laboratorio di Informatica
2014/2015
Passaggio parametri puntatore
Unità 6
Passaggio parametri puntatore
Nonostante il passaggio di parametri sia per valore, il risultato dell’esecuzione della funzione swap consiste proprio nel
modificare il valore di due variabili del programma principale.
Passando alla funzione i puntatori a due variabili è possibile
modificare il valore delle variabili puntate, anche se il valore
delle variabili puntatore rimane inalterato.
L’uso dei puntatori nel passaggio di parametri per valore
consente di effettuare in pratica il passaggio di parametri per
riferimento.
Pagina 22Laboratorio di Informatica
2014/2015
Passaggio parametri puntatore
Unità 6
Passaggio per valore VS passaggio
tramite puntatori
void swap(int *a, int *b) {
int temp;
temp = *a;
*a = *b;
*b = temp;
}
swap(&x, &y);
void swapVal(int a, int b) {
int temp;
temp = a;
a = b;
b = temp;
}
swapVal(x, y);
Cosa succede se si invoca swapVal(&x, &y); ?
definizione invocazione
definizione invocazione
Pagina 23Laboratorio di Informatica
2014/2015
Passaggio parametri puntatore
Unità 6
Esecuzione
Pagina 24Laboratorio di Informatica
2014/2015
Passaggio parametri puntatore
Unità 6
Esempio Intestazione di funzione con
parametri puntatori
void f(int*, double*);
Parametro formale
di tipo puntatore ad int
Parametro formale
di tipo puntatore a double
Pagina 25Laboratorio di Informatica
2014/2015
Passaggio parametri puntatore
Unità 6
Chiamata tramite parametri puntatore
(per indirizzo): funzione chiamante
Quando la funzione viene chiamata, devono essere passati
come parametri gli indirizzi delle variabili
• Il passaggio degli indirizzi potrà essere ottenuto applicando l’operatore & alla variabile che deve essere
modificata
• Sono passate le “celle” fisiche dove i valori sono
memorizzati
int a; double b;
a = 2; b = 3.2;
f(&a, &b);
2
3.2
a
b
&a
&b
Pagina 26Laboratorio di Informatica
2014/2015
Passaggio parametri puntatore
Unità 6
Chiamata per indirizzo
Nell’intestazione della funzione chiamata deve essere usato l’operatore * per le variabili passate
per indirizzo
void f(int *pa, double *pb)
{
*pa = 6;
*pb = 7.4;
}
&a
&b
pa
pb
&pa
&pb
Pagina 27Laboratorio di Informatica
2014/2015
Passaggio parametri puntatore
Unità 6
Chiamata per indirizzo: esecuzione
int a; double b;
a = 2; b = 3.2;
f(&a, &b);
void f(int *pa, double *pb)
{
*pa = 6;
*pb = 7.4;
}
pa
pb
&pa
&pb
6
7.4
a
b
&a
&b
int *pa = &a; double *pb = &b;
Pagina 28Laboratorio di Informatica
2014/2015
Passaggio parametri puntatore
Unità 6
Vantaggi del passaggio per riferimento
Il passaggio di parametri di tipo puntatore è
particolarmente utile quando i dati che devono
essere scambiati tra funzione chiamata e funzione
chiamante sono voluminosi.
Il passaggio per riferimento risulta molto più
efficiente sia in termini di occupazione di memoria
che di tempo di calcolo (infatti, occorre la copia del
solo puntatore ai dati, non di tutto l’insieme dei
dati).
Pagina 29Laboratorio di Informatica
2014/2015
Passaggio parametri puntatore
Unità 6
Valori restituiti di tipo puntatore
double* puntatore(double a) {
double *r = malloc(sizeof(double));
*r = a;
return r;
}
int main () {
double *pd = puntatore(5.4);
printf("pd = %p\n", pd);
printf("*pd = %f\n", *pd);
return EXIT_SUCCESS;
}La funzione puntatore crea un puntatore ad
una variabile di tipo double e la inizializza con
il valore passato come argomento
Pagina 30Laboratorio di Informatica
2014/2015
Passaggio parametri puntatore
Unità 6
Esercizi
Esercizio 6.5
Scrivere un programma che legga tre valori in tre distinte variabili small, medium, large, e ne scambi i valori in
modo da avere il più grande nella variabile large, il più
piccolo nella variabile small ed il restante nella variabile
medium. Definire una funzione per lo scambio dei tre valori
che utilizzi il passaggio di parametri puntatore.
Pagina 31Laboratorio di Informatica
2014/2015
Passaggio parametri puntatore
Unità 6
Esercizi
Esercizio 6.6
Scrivere una funzione che dati due valori interi a e b
restituisca quoziente e resto della divisione a/b.
Scrivere un programma di prova per tale funzione, che
legga l’input da tastiera.
Pagina 32Laboratorio di Informatica
2014/2015
Passaggio parametri puntatore
Unità 6
Appendice: memoria
Memoria
0
n
La memoria del calcolatore
può essere idealmente
descritta come un insieme di
celle ORDINATO
UNIVOCAMENTE
Pagina 33Laboratorio di Informatica
2014/2015
Passaggio parametri puntatore
Unità 6
Appendice: assegnazione
int x = 1;0
n
12359156L’istruzione di assegnazione
permette di “scrivere” un
valore all’interno di una cella
simboleggiata da un nome di
variabile
x
Pagina 34Laboratorio di Informatica
2014/2015
Passaggio parametri puntatore
Unità 6
Appendice: valore di una variabile
int x = 1;
printf("x = %d\n", x);
produce
x = 1
0
n
12359156
Tramite il nome di una variabile è
possibile “leggere” il contenuto della
cella simboleggiata da quella
variabile
x
Pagina 35Laboratorio di Informatica
2014/2015
Passaggio parametri puntatore
Unità 6
Appendice: indirizzo di una variabile
int x = 1;
printf("&x = %d\n", &x);
produce
&x = 2359156
0
n
12359156
Tramite l’operatore & applicato al
nome di una variabile è possibile
“leggere” l’indirizzo della cella
simboleggiata da quella variabile
x
Pagina 36Laboratorio di Informatica
2014/2015
Passaggio parametri puntatore
Unità 6
Appendice: puntatore
int x = 1;
int *x_ptr;
x_ptr = &x;
printf("*x_ptr = %d\n", *x_ptr);
printf("&x_ptr = %d\n", &x_ptr);
printf("x_ptr = %d\n", x_ptr);
produce
*x_ptr = 1
&x_ptr = 2359154
x_ptr = 2359156n
12359156
2359154 2359156
x
x_ptr
Pagina 37Laboratorio di Informatica
2014/2015
Passaggio parametri puntatore
Unità 6
Appendice: valore tramite un puntatore
n
12359156
2359154 2359156
x
x_ptr
Tramite l’operatore * applicato al
nome di un puntatore è possibile
“leggere” il contenuto della cella il
cui indirizzo è contenuto nella cella
simboleggiata da quel puntatore
Pagina 38Laboratorio di Informatica
2014/2015
Passaggio parametri puntatore
Unità 6
Soluzioni
Esercizio 6.2
Scrivere un programma che legga 10 numeri interi e
restituisca il minimo tra essi, usando variabili di tipo puntatore a int anziché variabili di tipo int.
Pagina 39Laboratorio di Informatica
2014/2015
Passaggio parametri puntatore
Unità 6
Soluzioni
#include <stdio.h>
#include <stdlib.h>
int main() {
int i, v, min;
int *min_ptr, *v_ptr;
min_ptr = &min;
v_ptr = &v;
printf("inserisci 10 interi:\n");
scanf("%d", v_ptr);
//printf("ho letto %d\n", *v_ptr);
*min_ptr = *v_ptr;
for(i = 1; i < 10; i++) {
scanf("%d", v_ptr);
//printf("ho letto %d\n", *v_ptr);
if(*v_ptr < *min_ptr)
*min_ptr = *v_ptr;
}
printf("minimo = %d\n", *min_ptr);
return EXIT_SUCCESS;
}Pagina 40Laboratorio di Informatica
2014/2015
Passaggio parametri puntatore
Unità 6
Soluzioni
Esercizio 6.3
Scrivere il programma dell’esercizio precedente tramite
allocazione dinamica della memoria. Deallocare la
memoria utilizzata prima della terminazione del
programma.
Pagina 41Laboratorio di Informatica
2014/2015
Passaggio parametri puntatore
Unità 6
Soluzioni
#include <stdio.h>
#include <stdlib.h>
int main() {
int i, *min, *v;
min = malloc(sizeof(int));
v = malloc(sizeof(int));
printf("inserisci 10 interi:\n");
scanf("%d", v);
//printf("ho letto %d\n", *v);
*min = *v;
for(i = 1; i < 10; i++) {
scanf("%d", v);
//printf("ho letto %d\n", *v);
if(*v < *min)
*min = *v;
}
printf("minimo = %d\n", *min);
free(v); free(min);
return EXIT_SUCCESS;
}Pagina 42Laboratorio di Informatica
2014/2015
Passaggio parametri puntatore
Unità 6
Soluzioni
Esercizio 6.4
Scrivere una funzione stampaPtr che dato in ingresso un
puntatore ne stampi la dimensione in byte, il valore,
l’indirizzo di memoria ed il valore della variabile puntata.
Scrivere un programma che verifichi il comportamento di stampaPtr.
Pagina 43Laboratorio di Informatica
2014/2015
Passaggio parametri puntatore
Unità 6
Soluzioni
void stampaPtr_int(int **ptr) {
printf("dimensione del puntatore: %d\n",
sizeof(*ptr));
printf("valore del puntatore: %p\n", *ptr);
printf("indirizzo del puntatore: %p\n", &(*ptr));
printf("dimensione della variabile puntata: "
"%d\n", sizeof(**ptr));
printf("valore della variabile puntata: %d\n\n",
**ptr);
}
int main() {
int i = 5, *ptr_i;
ptr_i = &i;
infoPtr_int(&ptr_i);
}
Si provi a scrivere una funzionevoid
stampaPtr_double(double **ptr)
Pagina 44Laboratorio di Informatica
2014/2015
Passaggio parametri puntatore
Unità 6