63
Informatica 2 modulo C Massimo Callisto De Donato [email protected] www.cs.unicam.it/massimo.callisto LEZIONE 7 PUNTATORI Università degli studi di Camerino Scuola di scienze e tecnologia - Sezione Informatica

Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

  • Upload
    others

  • View
    0

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Informatica 2 – modulo CMassimo Callisto De Donato

[email protected]

www.cs.unicam.it/massimo.callisto

LEZIONE 7 – PUNTATORI

Università degli studi di CamerinoScuola di scienze e tecnologia - Sezione Informatica

Page 2: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Organizzazione intuitiva della memoria• Ad ogni variabile associamo un nome per poterla utilizzare.

• Dal punto di vista del calcolatore, si utilizza un “indirizzo” di memoria (usualmente rappresentati in valore esadecimale). Esempio (schema semplificato) :

int a;

a = 5;

printf(„„%d‟‟, a);

? ? ? ?

0x120 0x121 0x122 0x123

? ? ? ?

0x120 0x121 0x122 0x123

a

? 5 ? ?

0x120 0x121 0x122 0x123

a

Se volessimo manipolare direttamente gli indirizzi?

Page 3: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

I puntatori

• Un indirizzo di memoria può essere assegnato solo ad una categoria speciale di variabili: i puntatori

• Una variabile di tipo puntatore contiene l’indirizzo di memoria di un altra variabile.

• Un puntatore di un certo tipo (e.g. int, float, …) punta ad una variabili delle stesso tipo (e.g. int, float, …). Inoltre si possono a tipi di dati derivati (es: strutture).

• Due aspetti da poter manipolare:

– Il puntatore in sé

– La variabile puntata.

• Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria.

Page 4: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Dichiarare un puntatore

• Sintassi della dichiarazione di un puntatore:tipo* nome_variabile;

tipo *nome_variabile; // equivalente al primo

• Abbiamo definito una variabile(un puntatore) capace di memorizzare indirizzi di memoria di variabili di tipo tipo.

• Parliamo di "indirizzi di memoria di tipo tipo" dato che ogni tipo di dato corrisponde una diversa quantità di memoria (celle) occupata.

• Esempi:

int *ptr; /* si legge: ptr è un puntatore ad intero, ovvero p

contiene l‟indirizzo di un intero */

int *a, *b;

double* dptr;

char *cptr;

Page 5: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Indirizzare un puntatore

• Possiamo inizializzare un variabile puntatore utilizzando l’operatore & che restituisce l’indirizzo di una data variabile. Esempio :

int *p; // p è un puntatore ad intero ma a chi punta?

• Assegniamo a p l'indirizzo di b

int b = 7;

int *p; // p è un puntatore ad intero

p = &b; // p punta a b (p prende l‟indirizzo di b)

• Visivamente (ed in modo intuitivo):

0x121 7 ? ?

0x80 0x121 0x122 0x123

bp

Page 6: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Operatore di indirezione

• L'operatore di indirezione (o deferenziazione) “*”:

permette di ottenere il valore contenuto della locazione di memoria puntata dal puntatore.

• Quindi, anteponendo * al puntatore operiamo sul valore della variabile puntata:

Page 7: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Operatore di indirezione

• Dopo l’assegnamento pi=&a, a e *pi sono degli alias, ossia sono perfettamente equivalenti.

• Che stampiamo con il codice sotto?

Page 8: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Altri esempi/* 1 esempio*/

int *p;

int pass;

p = &pass;

*p = 7;

p = 7; //?? cioè?

/* 2 esempio*/

int c = 1; float d;

int *p;

p = &c;

d = *p; // Ci manca?

/* 3 esempio*/

int c = 1;

int *p ;

p = &c;

(*p)++;

printf(" c = %d",c); // Che stampa?

Page 9: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Altri esempi (unico frammento di codice)

int *p;

int x = 5;

int y;

p = &x;

y = *p;

int *q; //?

q = p;

Page 10: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Inizializzare i puntatori

• Con la dichiarazione di un puntatore, all’inizio a cosa puntiamo?

• Si assume che i puntatori conterranno un indirizzo indefinito (puntano a qualcosa in memoria che non sappiamo).

• Dato che il puntatore è un tipo di dato molto delicato è sempre bene fare una giusta inizializzazione usando:

– Assegnando l’indirizzo di una variabile dichiarata e definita;

– Assegnandogli un altro puntatore (vale solo se sono dello stesso tipo);

– Assegnandogli il valore NULL (costante definita in stddef.h)

Page 11: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

I puntatori e array

• In C, un array è una costante all’indirizzo della prima cella di memoria. Questa costante è il puntatore alla 1° cella.

• Possiamo operare sugli array con i puntatori (fondamentale nell’allocazione dinamica di array).

• Ad esempio:char buffer[5]; char *s;

// Le due istruzioni si equivalgono

s = &buffer[0];

s = buffer; // buffer è implementato come un puntatore

Page 12: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

I puntatori e array

• Dato che gli array sono elementi sequenziali identificati da un indice di posizione, l'incremento dell'indirizzo del puntatore scorrere anche l'array.

• Esempio:

– s[0] corrisponde al puntatore s nella prima posizione;

– (s + j) è corrisponde al puntatore s alla posizione (j+1)-esimo elemento dell'array. s[0] == (s + 0)

// Equivalenti

Page 13: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

I puntatori e arrayint buffer[5];

int s*;

s = buffer;

for(i=0; i<5; i++)

buffer[i] = i;

for(i=0; i<5; i++){

*s = i; // oppure s[i] = i;

s = s + 1;}

• Nota che buffer è una costante implementata come un puntatore.

• Significa che tutte le operazioni di modifica dell’indirizzo puntato non sono valide:

buffer++; // no

s = (buffer + 2); // ok

buffer = (buffer + 2); // no

Page 14: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

I puntatori e array: altro esempioint a[5];

int *p;

// nota che p = a ; equivale a p = &a[0]

Page 15: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

I puntatori e array: altro esempio/* Che stampiamo? (sempre che sia corretto) */

#include <stdio.h>

main() {

int b[] = {1,2,3};

int *s;

s = b;

printf("%d\n", b[0]); // 1

printf("%d\n", *s); // 1

printf("%d\n", *b); // è *(b + 0)==b[0]

printf("%d\n", *(s+1)); // 2

printf("%d\n", s[1]); // 2

printf("%d\n", *(b + 4)); // out of range

printf("%d\n", *(b++)); // nn si può fare

}

Page 16: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Aritmetica dei puntatori• Nota la differenza:

– (*s)++ -> incrementa di uno il valore della variabile puntata da s

– (s++) -> incrementa l'indirizzo contenuto in s

• Sono valide solo operazioni di l’incremento ++,+ e di decremento --, -(altri operatori non sono giusti)

• Ma di quanto si incrementa l'indirizzo contenuto in s dopo s++?

– Dipende dal tipo di dato puntato da s dato che ogni tipo occupa un numero diverso di elementi in memoria.

– se una cella è di 1 byte, il tipo char occupa 1byte ed int 4 byte, allora:

char *p; // p++ incrementa l‟indirizzo di 1 byte

int *p; // p++ incrementa l‟indirizzo di 4 byte

Page 17: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Aritmetica dei puntatori• Ad esempio, l’assegnamento (1) sposta il puntatore pi in avanti di tre posizioni:

int buffer[5], *pi;

pi = buffer;

pi = pi + 3; (1)

• L’assegnamento (2) sposta il puntatore pi indietro di tre posizioni e punta a buffer[1]:pi = pi - 2; (2)

buffer[4] = *p++; // inserisco p[1] in buffer[4] e p

//diventa p[2]

• E se ora scrivessi come sotto che c’è che non va?c = *(pi – 3); // fuori range

Page 18: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Esempio#include<stdio.h>

main(){

int v1[5];

int v2[5];

int i;

int *pi;

for(i=0; (v1[i] = (i+1)) <= 5; i++);

i = &v1[4]-&v1[2];

printf("%d\n", i); // scrive 2 -> (v + 4) – (v + 2)

i = &v2[4] - &v1[2];

printf("%d\n", i); //v1 è diverso da v2

pi = v2 - 2; // non ha senso

}

Page 19: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Puntatori e passaggio dei parametri per indirizzo

• Supponiamo di voler implementare la funzione swap per lo scambio di valori di due interi ma cosi posso solo scambiare i valori dei parametri formali:

Page 20: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Puntatori e passaggio dei parametri per indirizzo

• In C gli argomenti delle funzioni vengono passati per valore: alterare i valori dei parametri attuali non cambia quelli dei parametri attuali [non vale per gli array].

• Il passaggio per indirizzo permette di applicare le modifiche sui parametri formali anche su quelli attuali: una funzione può modificare una variabile della funzione chiamante.

• Per implementare (simulare) il passaggio per indirizzo usiamo i puntatori come parametri formali:

(Simulare perché…) Passiamo sempre un valore ma questo è un indirizzo. Riferendoci all’indirizzo modifichiamo il valore della variabile puntata.

Page 21: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Puntatori e passaggio dei parametri per indirizzo

• Modificare il contenuto delle locazioni puntate dai puntatori a e b significa chiaramente modificare i valori associati alle variabili x e y

Page 22: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Puntatori e passaggio dei parametri per indirizzo

• Se scrivessimo:void swap(int *a, int *q){

int *tmp;

tmp = a;

a = b;

b = tmp;

}

• E' uguale?void swap(int *a, int *q){

int tmp;

tmp = *a;

*a = *b;

*b = *tmp;

}

• La prima implementazione è la migliore dato che manipoliamo solo indirizzi.

Page 23: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

I puntatori e array… e stringhe

• Le stringhe sono implementate in C come vettori di caratteri con in più il terminatore di stringa ‘\0’.

• Dato che gli array sono trattati con i puntatori, le stringhe possono essere manipolate con i puntatori…

Page 24: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Esempio#include<stdio.h>

int my_strlen(char *);

main(){

char str[] = "Una stringa molto lunga";

printf("La lunghezza di %s e' %d\n ", str, my_strlen(str));

}

int my_strlen(char * s){

int i=0;

while (s[i] != '\0') i++; // valeva anche while(s[i++]);

// dato che „\0‟ viene visto come false

return i;

}

/*

Nota che gli array possono essere passati come puntatori. Ovvero:

int my_strlen(char * s); //equivale a

int my_strlen(char s[]);

*/

Page 25: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Esempio: equivalenze (forse)

/* prima versione */

int my_strlen (char *s) {

int n = 0;

while ( *s != '\0') {

n++; s++;

}

return n;

}

/* seconda versione*/

int my_strlen (char *s) {

int n = 0;

while (*s++ != '\0')

n++;

return n;

}

/* terza versione */

int my_strlen (char *s) {

int n = 0;

while (*s++)

n++;

return n;

}

/* quarta versione */

int my_strlen (char *s) {

char *p = s;

while (*s++);

return s - p;

}

Page 26: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Esempio: che stampa?

main() {

char v[6];

int i;

for(i = 0; i < 4; i++)

v[i] = 'B';

v[4] = '\0';

v[5] = 'X';

printf("1: %d\n", my_strlen1(v));

printf("sto a %c\n", *v);

printf("2: %d\n", my_strlen2(v));

printf("sto a %c\n", *v);

printf("3: %d\n", my_strlen3(v));

printf("sto a %c\n", *v);

printf("4: %d\n", my_strlen4(v));

printf("sto a %c\n", *v);

printf("Ufficiale: %d\n", strlen(v));

}

int my_strlen1 (char *s) {

char *p = s;

while (*s++);

printf("1 - sto a %c\n", *s);

return s - p;

}

int my_strlen2 (char *s) {

int n = 0;

while (*s++) n++;

printf("2 - sto a %c\n", *s);

return n;

}

int my_strlen3 (char *s) {

int n = 0;

while (*s++ != '\0„) n++;

printf("3 - sto a %c\n", *s);

return n;

}

int my_strlen4 (char *s) {

int n = 0;

while ( *s != '\0„)

{ n++; s++; }

printf("4 - sto a %c\n", *s);

return n;

}

Page 27: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Esempio/* Che stampa?? */

#include <stdio.h>

#include <limits.h>

int my_strlen (char *s) {

char *p = s;

while (*s++);

return s - p;

}

int my_strlenInt (int *s) {

int *p = s;

while (*++s != INT_MIN);

return s - p;

}

main() {

int b[] = {1,2,3,INT_MIN};

char s[] = "max";

printf("my_strlen ->%d\n", my_strlen(s));

printf("my_strlen2->%d\n", my_strlenInt(b));

}

// Perché abbiamo usato INT_MIN?

Page 28: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Esempio di sostituzione/* Che stampa?? */

#include <stdio.h>

void func(char *, char *);

int main ()

{

char a[] = "e' una prova";

char b[] = "stringa da sostituire";

printf ("'%s'\n'%s'\n\n",a, b);

func(a, b);

printf ("'%s'\n'%s'\n\n",a, b);

return 0;

}

void func(char *p, char *q){

while (*p != '\0'){

*q = *p;

p++; q++;

}

*q='\0'; // dobbiamo immettere un nuovo terminatore in b[]

}

Page 29: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

I puntatori e array… e stringhe• Nota che questo codice genera un errore nel programma:

char *p;

p = "stringa";

*(p + 2) = 't'; // non ammesse modifiche

p = p + 2; // ok. Possiamo spostarci p”ringa”

• La sequenza "stringa" è una costante puntata da p che può essere solo letta e su cui ci possiamo spostare. Cosa diversa (e corretta) è questa:

char s[] = "stringa";

char *p = s;

*(p + 2) = 't';

Page 30: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Altri esempi: che facciamo?/* ?? */

char s[] = "prova";

/* ?? */

char s[] = {‟p‟, ‟r‟, ‟o‟, ‟v‟, ‟a‟, 0};

/* ?? */

char c, *t;

/* ?? */

c = *s;

/* ?? */

t = s + 2;

/* ?? */

s[0] = ‟t‟;

/* ?? */

char *name = "arturo";

/* ?? */

char surname[] = "rossi";

/* ?? */

name++;

/* ?? */

surname++;

Page 31: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Altri esempi: che facciamo?/* un vettore di 6 caratteri, compreso il terminatore */

char s[] = "prova";

/* lo stesso, in notazione diversa ma con un errore */

char s[] = {‟p‟, ‟r‟, ‟o‟, ‟v‟, ‟a‟, \0};

/* un carattere, un puntatore a carattere… però meglio spezzare */

char c, *t;

/* c prende il valore ‟p‟ */

c = *s;

/* t rappresenta la stringa "ova" */

t = s + 2;

/* ora la stringa s `e "trova" */

s[0] = ‟t‟;

/* un puntatore ad un‟area prei-inizializzata di 7 byte */

char *name = "arturo";

/* un‟area di 6 byte, con indirizzo "surname" */

char surname[] = "rossi";

/* ora name indica "rturo" */

name++;

/* errore: surname ha un indirizzo costante che non può essere modificato */

surname++;

Page 32: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

In sintesi• Un puntatore è una variabile che contiene un indirizzo di memoria (della variabile

a cui punta). Sintassi:tipo* nome_ptr; oppure tipo * nome_ptr;

• Assegnamento di un puntatore: possiamo far puntare un puntatore ad una variabile assegnandogli l'indirizzo con &. Sintassi:

– ptr = &x; dove ptr è un puntatore al tipo a cui appartiene x

– vale anche: ptr = pr; dove prt e pt sono puntatori dello stesso tipo

• Accesso alla variabile puntata tramite l’operatore di indirezione *. Sintassi: – *ptr restituisce il valore della variabile puntata da ptr

• I puntatori possono essere incrementati (++, +) e decrementati (--, -); Si usano con gli array…

Page 33: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

In sintesi• Puntatori ed array: il nome di un array contiene una costante che è l’indirizzo alla

1° elemento:char buffer[5], *s;

s = buffer; // equivalente a s = &buffer[0]

buffer++; // no. L‟indirizzo di buffer è costante, s++ è ok

• L’equivalenza è data dal fatto che buffer[i]e *(buffer+i) sono uguali:for (i=0; i < n; i++) buffer[i] = „K‟;

for (i=0; i < n; i++) *(s + i) = „K‟;

• ++ o -- sono ragionevoli solo su array e se la posizione corrente è corretta:s = buffer;

s++; // ok

s = buffer;

s--; // non va bene. Out of range su buffer

Page 34: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

In sintesi• Puntatori e passaggio per riferimento: usiamo i puntatori realizzare il passaggio di

indirizzi.

• Operando tramite indirizzi, l'alterazione dei parametri formali modifica anche i parametri attuali!

• Usando i puntatori nei parametri formali e passando l'indirizzo del parametro attuale (con &) si realizza il passaggio per riferimento.

#include <stdio.h>

void toLowerP(char *);

void toUpperP(char *);

main(){

char c = getchar();

printf(“%c\n”, c);

toLowerP(&c); //

printf(“%c\n”, c);

toUpperP(&c); //

printf(“: %c\n”, c);

}

void toLowerP(char *cptr){

if(*cptr compreso in [A-Z])

*cptr = ?? + 32;

}

void toLowerP(char *cptr){

if(*cptr compreso in [a-z])

*cptr = ?? – 32;

}

Page 35: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

In sintesi

#include <stdio.h>

void inizializza(int *, int);

main(){

int c[10];

inizializza(c, 10);

}

void inizializza(int* pt, int n)

{

int i;

for (i=0; i< n; i++){

printf(„„Inserisci elemento: ‟‟);

scanf(„„%d‟‟, pt[i]);

//in alternativa: scanf(„„%d‟‟, (pt + i));

}

}

Perché qui non c’è ‘&’?

Page 36: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

In sintesi

• Perche scriviamo in questo modo?

int i, n;

float x;

char name[50];

n = scanf("%d%f%s", &i, &x, name);

Page 37: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Qualche errore che si commette:• Mancato indirizzamento del puntatore e versione corretta:

• Operazioni concesse ma sbagliate:

int x;

int *p;

float *t;

x = p; // x contiene un indirizzo di memoria

t = x; // ci manca la & però x è anche un int

t = p; // ma p è un int!

Page 38: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Qualche errore che si commette:

• Assunzioni riguardo posizioni in memoria di array “adiacenti”

• Fare assunzioni riguardo alle posizioni di variabili in memoria. Ad ogni esecuzione la posizione usualmente cambia.

• L’output di sotto non è sempre lo stesso:

Page 39: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Ancora sui puntatori…

• Puntatori e funzioni

• Allocazione dinamica della memoria

• Array di puntatori

• Indirizzamento multiplo

• …

Page 40: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Puntatori e funzioni

• Abbiamo visto i puntatori ed il passaggio per indirizzo simulano il passaggio per riferimento.

• E’ naturale aspettarsi che una funzione ritorni un puntatore.

#include <stdio.h>

char* myfunc(char *str){

int i = 0;

while(str++)

if(*str == 's')

return str;

return NULL;

}

main()

{

char str[] = "massimo";

printf("%s", myfunc(str));

}

Page 41: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Puntatori e funzioni.. ed esercizi• Tornando un puntatore, possiamo far puntare ad una locazione di

memoria che può essere l’inizio di una sequenza (es. un array, strutture)

• Per esercizio, implementare i seguenti prototipi:

/* ritorna la stringa fino al primo carattere maiuscolo, null se non c'è */

char* fromFirstUpper(char *);

/* ritorna la stringa fino al primo numero, null se non c'è */

char* fromFirstDigit(char *);

/* ritorna la stringa di soli numeri, null se non c'è */

char* parseDigits(char *);

/* torna una stringa a partire da s1 senza i caratteri compresi in cs */

char* parseChars(char* s1, char* cs);

/* torna la parte di stringa in s1 che fa match con s2, null se non c'è */

char* mySubString(char* s1, char* s2);

/* torna i valori di vet i cui elementi sono > limit, null se nn ci sono */

int* greatesValues (int* vet, int limit);

/* trova la posizione della 1 occorrenza di c in s1, -1 se non c‟è*/

int firstOccurrenceOf (char* s1, char c);

Page 42: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Allocazione dinamica• In C, la memoria può essere gestita staticamente, automaticamente o

dinamicamente.

– Gestione statica: le variabili sono allocate ed esistono dall'inizio fino alla fine del programma (e.g. variabili globali, static).

– Gestione automatica: sono allocate e distrutte durante l'esecuzione. Ad esempio, l’allocazione automatica per le variabili locali ad una funzione.

• In tutti e due i casi la memoria viene occupata in dipendenza alle variabili ed alle funzioni utilizzate.

• In molte situazioni vogliamo più flessibilità (es. in casi dove l’input è variabile).

• Parliamo quindi di gestione dinamica della memoria, ovvero le tecniche attraverso le quali un programma ottiene memoria durante la sua esecuzione (run-time).

Page 43: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Allocazione dinamica

• Possiamo allocare dinamicamente la memoria (malloc()) e quindi de-allocarla (free()).

#include <stdlib.h>

void *malloc(size_t size);

• L’utilizzo tipico è il seguente:punt = (tipodato *)malloc(sizeof(tipodato));

– tipodato è il tipo di memoria che abbiamo richiesto

– punt è una variabile di tipo tipodato *

– sizeof() serve a calcolare il corretto numero di bytes che occupa tipodato

Page 44: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Allocazione dinamica: void *punt = (tipodato *)malloc(sizeof(tipodato));

– tipodato è il tipo di memoria che abbiamo richiesto

– punt è una variabile di tipo tipodato *

– sizeof() serve a calcolare il corretto numero di bytes che occupa tipodato

• Se la memoria c’è, otteniamo il puntatore al suo inizio (punt); significa che possiamo scorrere punt in quell’area senza far danni.

• Se la memoria non è disponibile otteniamo un puntatore a NULL.

• Perché abbiamo usato un casting (tipodato *)?

– Perché main torna un puntatore generico void * che può puntare a qualsiasi tipo di dato.

– La conversione che facciamo è sicura (senza rischi di perdite) dato che sappiamo già quanta memoria abbiamo richiesto (e ottenuta).

Page 45: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Esempio

• Allochiamo dinamicamente un intero:

#include <stdio.h>

#include <stdlib.h>

main()

{

int *p;

p = (int *)malloc(sizeof(int));

if(p == NULL) exit(-1);

*p = 12;

(*p)++; // *p++ -> ??

printf("*p vale %d\n", *p); // stampa?

}

La funzione exit(val) termina immediatamente il programma.

Al programma chiamante (es. il S.O.) ritorna val per comunicare la condizione di terminazione

Page 46: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Esempio/*Assunzioni sbagliate*/

#include <stdio.h>

main()

{

int *p;

p = (int *)malloc(sizeof(int));

// prima potevamo scrivere anche:

p = (int *)malloc(4);

// ma poteva essere un assunzione sbagliata.

// Es.: il seguente output non è sempre predicibile

printf("Tutto in byte (n byte = (2^(n*8))-1 bits)...\n");

printf("int: %d\n", sizeof(int));

printf("char: %d\n", sizeof(char));

printf("float: %d\n", sizeof(float));

printf("long double: %d\n", sizeof(long double));

printf("short int: %d\n", sizeof(short int));

printf("long int: %d\n", sizeof(long int));

}

Page 47: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Esempio• Array dinamico:

/*soliti includes*/

main()

{

int i, *p;

/* allochiamo 10 elementi. Significa che se ci muoviamo nello spazio di p per 10 posizioni allora non faremo mai danni */

p = (int *)malloc(10*sizeof(int));

if(p == NULL) exit(-1);

for(i = 0; i < 10; i++)

*(p + i) = i;

for(i = 0; i < 10; i++)

printf("*(p + %d) vale %d\n", i, *(p + i)); // stampa?

p = (int *)malloc(20*sizeof(int)); // 20 elementi

if(p == NULL) exit(-1);

for(i = 0; i < 20; i++)

*(p + i) = i*2;

for(i = 0; i < 20; i++)

printf("*(p + %d) vale %d\n", i, *(p + i)); // stampa?

}

Page 48: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Esempio

• Che facciamo?

/*che includes ci vanno??*/

main()

{

int *p;

p = (int *) malloc (INT_MAX * sizeof(int));

// Sempre bene controllare

if (p == NULL){

printf (“Mmmm\n") ;

exit(-1);

}

}

Page 49: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Esempio• Che facciamo?

int* request_mem(void)

{

unsigned num; // Perché?

int *ptr;

printf("Enter the number of type int to allocate: ");

scanf("%u", &num);

ptr = (int *)malloc(num*sizeof(int));

if (ptr != NULL){

printf("Memory allocation was successful.");

return ptr;

}else{

printf("Memory allocation failed.");

return NULL;

}

}

Page 50: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Esempio#include <stdio.h>

#include <stdlib.h>

#include <string.h>

char* my_concat(const char *,const char *),

main (void)

{

char *p;

p = concat("abcd","efghi");

printf ("risultato %s", p); // stampa?

}

char* my_concat(const char *s1, const char *s2){

char * result;

result = (char *) malloc((strlen (s1) + strlen (s2) + 1) * sizeof(char));

if (result == NULL) {

printf (“Out of memory\n");

exit (-1);

}

strcpy (result, s1);

strcat (result, s2);

return result;

}

La keyword const asserisce che nessuna modifica nel valore del parametro è possibile.

Utile nel passaggio per riferimento.

Page 51: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Esempio su const/* otteniamo un errore di compilazione */

#include <stdio.h>

void _stampa(const char * s){

*(s+2) = 'F';

printf("%s\n", s);

}

main() {

char s[] = "stringa";

_stampa(s);

}

Page 52: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Allocazione dinamica: de-allocazione• free: de-alloca memoria precedentemente ottenuta con una malloc:

void* free(void* p);

• Si richiama sempre su puntatori restituiti da una malloc.

• free si rende necessaria dato che la memoria ottenuta con mallocrimane riservata fino alla fine del programma.

• Nota che free torna void*: dopo lo chiamata p punta a qualcosa di indefinito ed è bene farlo puntare manualmente a NULL:

int* p = (int *)malloc(sizeof(int));

int* q;

free(p);

free(q); // scorretto

p = NULL; // stiamo sicuri su p

ora p punta ad una posizione

indefinita ovvero possiamo

fare ancora danni!

Page 53: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Esempio: frammento di codice/* che facciamo in ogni istruzione? */

int *pi;

int *pj;

pi = (int *)malloc(sizeof(int));

*pi = 150;

pj = pi;

free(pj);

pi = (int *)malloc(sizeof(int));

*pi = 4000;

pj = (int *)malloc(sizeof(int));

*pj = *pi;

pj = (int *)malloc(sizeof(int));

/* ALLA FINE ABBIAMO PERSO UNA CELLA DI MEMORIA con l‟ultima malloc() */

Nota che abbiamo ucciso anche pi

e non leggeremmo più 150!

Page 54: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Esempio: frammento di codice/* Anche qui perdiamo qualcosa. Dove e perché? */

/* includes…*/

main(){

int *p, *q ;

int x, y;

x=5;

y=14;

p=(int *)malloc(sizeof(int));

q=(int *)malloc(sizeof(int));

*p = 25;

*q = 30;

*p = x;

y = *q;

p = &x; // ho perso la memoria puntata da p in preedenza

}

Page 55: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Esempio: frammento di codice/* Se copiassimo e compilassimo di sana pianta questo codice … Cosa

c‟è che non va bene? */

main(){

printf(“Inserisci la lunghezza del vettore ”);

scanf(“%f”, &n);

int A[n];

int *a;

a = (int *)malloc (n * sizeof(int));

for (i=0; i<n; i++)

a[i] = 0;

free (a);

a = NULL;

free(A);

}

Page 56: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Array di puntatori

• I puntatori possono essere disposti su un array:int* a[5], x = 10;

a[0] = &x;

printf("%d\n", *a[0]);

printf("%d\n", *a[2]); // ok??

• Altri esempi:char* a[5];

float* b[MAX_INT];

char* colori[3] = {"Blu", "Rosso", "Verde"};

Page 57: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Array di puntatori

• char* colori[3] = {"Blu", "Rosso", "Verde"}; definisce un array multi-dimensionale (2 in questo caso) inizializzato con 3 array di stringhe. E’ uno dei casi più comuni:

void syntax error(int num){

char* err[] = {

"Impossibile aprire il file",

"Errore in lettura",

"Errore in scrittura",

"Guasto al dispositivo"

};

if (num < 4)

printf("%s\n", err[num]);

else

printf("%d non valido", num);

}

Page 58: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Indirizzamento multiplo

• Se un puntatore contiene l’indirizzo di un “tipo di variabile”, quel tipo può essere a sua volta di tipo puntatore e parliamo di indirizzamento multiplo.

int x, *P, **DP;

P = &x;

DP = &P;

**DP=1518;

• Altro esempio:

int x = 10;

int *p = &x; int **q = &p;

printf("%d\n", **q);

• Che significa? int x; int * a[10]; *a[0] = &x; **a=10;

Page 59: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Indirizzamento multiplo

int **ipp;

int i = 5, j = 6, k = 7;

int *ip1 = &i;

int *ip2 = &j;

ipp = &ip1;

*ipp = ip2;

*ipp = &k;

Page 60: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Indirizzamento multiplo/* Che succede? */

main() {

int a;

int *p;

int **pp;

a=9;

p=&a;

pp=&p;

printf("Indirizzo di pp=%d, valore=%d\n", &pp, pp);

printf("Indirizzo di p=%d, valore=%d\n", &p, p);

printf("Indirizzo di a=%d, valore=%d \n", &a, a);

}

Page 61: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Indirizzamento multiplo

• Allocazione dinamica di un array bidimensionale 2 x 3.

int **a;

int i;

a = (int **) malloc (2 * sizeof (int *));

for (i=0; i<2; i++)

a[i] = (int *) malloc (3 * sizeof (int));

a [0][1] = 15;

Page 62: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Indirizzamento multiplo

• Ora sappiamo leggere meglio il prototipo del

main?

int main(int argc, char *argv[]);

int main(int argc, char **argv);

Page 63: Informatica 2 – modulo C - WordPress.com › 2011 › ... · –La variabile puntata. •Vedremo che i puntatori rivestono un ruolo chiave nella gestione dinamica della memoria

Indirizzamento multiplo#include <stdio.h>

// int main (int argc, char *+argv)

int main (int argc, char *argv[])

{

int count;

// argc è sempre > 0 argv[0] è sempre corretto

printf ("Nome del programma '%s'\n",argv[0]);

if (argc > 1)

for (count = 1; count < argc; count++)

printf("argv[%d] = %s\n", count, argv[count]);

else

printf("Nessun parametro passato.\n");

return 0;

}

/* nota che argc e argv sono nomi che si usano per convenzione. Potevamo

anche usare altri nomi. */