Vai alla Home Page About me Courseware Federica Living Library Federica Federica Podstudio Virtual Campus 3D Le Miniguide all'orientamento Gli eBook di Federica La Corte in Rete
 
 
Il Corso Le lezioni del Corso La Cattedra
 
Materiali di approfondimento Risorse Web Il Podcast di questa lezione

Valeria Vittorini » 5.Puntatori: aspetti avanzati


Riferimenti (in C++)

  • In alcuni linguaggi “riferimento” è un sinonimo di puntatore.
  • In C++ “riferimento” è un identificatore che individua l’indirizzo di una variabile.
  • E’ un modo per dare più nomi ad una stessa variabile.

Riferimenti

  • Come un puntatore così anche un riferimento deve essere definito in relazione ad un tipo.
  • Definizione di un riferimento:

T & r;

  • Un riferimento deve essere inizializzato e non può essere modificato.
  • Attraverso di esso può però essere modificato il valore dell’oggetto riferito.
Mostra codice

Scambio per riferimento

  • IN C++ esiste lo scambio di parametri per riferimento.
  • Un parametro riferimento è dunque un alias della variabile argomento.
  • Il passaggio per riferimento è efficiente in quanto evita la copia del valore:
    • Il parametro formale in realtà è un puntatore che contiene l’indirizzo del parametro effettivo;
    • Il passaggio dell’indirizzo (dereferencing del riferimento) è a cura del compilatore.

    void incr(int & v) {
    v += 1;
    }


    int a = 4;
    incr(a);
    // ora "a" vale 5

Puntatore a costante

L’oggetto puntato da p non può essere modificato attraverso p.

L'oggetto puntato da p non può essere modificato attraverso p.


Puntatore costante

Il puntatore p deve essere inizializzato all’atto della sua definizione e non può essere modificato.

Il puntatore p deve essere inizializzato all'atto della sua definizione e non può essere modificato.


Puntatori come parametri di scambio

  • Ogni volta che viene effettuato uno scambio per indirizzo, ciò che in effetti viene passato alla funzione è un puntatore.
  • Questo puntatore nella maggior parte dei casi viene passato per valore.
  • Ma se necessario come qualunque altro tipo di variabile un puntatore può essere passato per indirizzo o per riferimento!

Puntatori scambiati per valore, per riferimento o per indirizzo

Come qualunque altra variabile un puntatore può essere scambiato per valore. Ad esempio:

void f(T*,...);
void f(const T*,...);

Come qualunque altra variabile un puntatore può essere scambiato per riferimento o per indirizzo. Ad esempio:

void f(T* &,...);
void f(const T* &);
void f(T**,...);
void f(const T**);

Puntatori come tipo di ritorno di una funzione

Una funzione può ritornare un puntatore a tipo T:

T* f(…);

Attraverso un puntatore può essere ritornato sia un dato atomico che strutturato.
Bisogna però fare attenzione alla classe di memorizzazione dell’entità cui il puntatore si riferisce, è un errore logico grave tentare di restituire attraverso un puntatore una variabile automatica (quindi locale ad un blocco e memorizzata nell’area stack).

Infatti…

  • Il valore ritornato viene reso disponibile al programma chiamante, cioè ne viene fatta una copia nel programma chiamante.
  • Pertanto il puntatore “sopravvive” alla funzione.
  • Invece l’oggetto puntato dal puntatore potrebbe venire deallocato se è locale alla funzione (caso di dangling references).
  • Lo stesso problema può presentarsi con funzioni che ritornano un riferimento (T& f(…);)

Esempio 2


Un caso particolare di funzione che ritorna puntatore

Funzioni che ritornano puntatori sono spesso funzioni che operano su stringhe.
Esempio di funzione di libreria:

char* strcat(char* str1, const char* str2);

concatena la stringa str2 alla stringa str1 e restituisce il risultato sia nella stessa str1 che come valore di ritorno.

Notare che in questo caso non c’è pericolo di errore, purchè lo spazio di memoria per str1 sia stato adeguatamente allocato nel programma chiamante.

Una domanda…

Perché strcat restituisce il risultato della
concatenazione anche,
come valore di ritorno?

Uso concatenato di una funzione

  • Esempio: A+B+C viene valutata A+(B+C).
  • Esempio: somma(a,somma(b,c)).
  • L’uso concatenato richiede che il valore di ritorno della funzione sia utilizzato come valore di uno dei parametri effettivi della successiva invocazione.
  • Pertanto il tipo del valore di ritorno e il tipo del parametro effettivo che deve accoglierlo devono corrispondersi.

Uso concatenato di strcat

strcat è un esempio di funzione del tipo:

T* f(...,T*,...);

  • Cioè è una funzione che restituisce come valore un puntatore fornito in ingresso.
  • Questo meccanismo consente l’esecuzione concatenata della funzione f:

strcat(strcat(a,b),c);

  • Inoltre in questo modo può essere chiamata sia come una funzione che come una procedura
  • Lo stesso meccanismo può essere utilizzato con parametri scambiati per riferimento:

T& f(…, T&, …);

Esempio 3

Mostra codice

Esempio 4 – mystrlen

Mostra codice

Esempio 5 – mystrcpy: Puntatore come valore di ritorno

Mostra codice

Esempio 6 – mystrcat


Esempio 7 – concatenazione


Puntatori per accesso alla linea di comando

In C++ e’ possibile fornire argomenti ad un programma principale:

int main(int argc, char *argv[])
{
.....
system("PAUSE");
return 0;
}

Questo meccanismo consente di attivare un programma fornendogli dei parametri a linea di comando (S.O. Unix, MS-DOS/Windows)
La variabile argc contiene il numero di parametri che compongono la linea di comando con la quale il programma è stato lanciato (compreso il nome del programma).
L’array argv è un array di puntatori a variabili di tipo char che può anche essere visto come un array di stringhe, ognuna corrispondente ad una delle parole che compongono la linea di comando con cui viene invocato il programma (compreso il nome del programma).

Puntatori per accesso alla linea di comando (segue)

  • argv[0] contiene sempre il nome col quale è stato invocato il programma, argc vale sempre almeno 1
  • argc e argv[] vengono assegnati dal sistema operativo

Per convenzione gli argomenti passati a linea di comoando devono essere separati da spazi bianchi
Se ad esempio si vuole specificare da linea di comando il nome di due file su cui opera il programma eseguibile “copia”, il programma può essere lanciato digitando:

copia nomefile1 nomefile2

In questo caso:

  • il valore di argc viene assegnato dal sistema e varrà 3;
  • argv[0] contiene il puntatore ad una stringa che memorizza il nome del programma eseguibile (copia);
  • argv[1] contiene un puntatore ad una stringa che contiene il nome del primo parametro (nomefile1);
  • argv[2] contiene un puntatore ad una stringa che contiene il nome del secondo parametro (nomefile2).

Esempio 8

Mostra codice

Esecuzione


Esempio 9

Mostra codice

Esecuzione


Puntatori a tipo void

Un puntatore è “legato” al tipo del dato a cui punta.

Non è possibile assegnare ad un puntatore l’indirizzo di un dato di tipo diverso.

Questa è una limitazione se il tipo del dato non è noto o può variare in alcune circostanze.

Per abilitare un puntatore a contenere solo un indirizzo, in modo che il tipo di dato non venga (momentaneamente) preso in considerazione è possibile definire un puntatore a tipo void.

Un puntatore a void è detto anche puntatore generico proprio per la sua capacità di puntare a dati di qualsiasi tipo.

void *

Definizione di un puntatore a void:

void * ptr;

  • ptr può contenere ora l’indirizzo di un dato indipendentemente dal suo tipo (NON è possibile assegnarli un puntatore a funzione);
  • void * specifica quindi che la variabile è di tipo puntatore ma che il tipo del dato a quell’indirizzo non è conosciuto.

Utilizzo di un puntatore a void

Non si può operare sul dato puntato dal un puntatore a void!

Si può solo memorizzarne l’indirizzo e confrontarlo con un altro indirizzo.

Un puntatore a void quindi non può essere de-referenziato direttamente (non si hanno indicazioni su come interpretare il contenuto di quella locazione di memoria!).

Per poter operare sull’oggetto puntato in C++ è necessario esplicitamente convertire il puntatore a void in un puntatore ad un tipo specifico.

Esercizio

Scrivere una funzione che effettui la divisione intera o la divisione reale sulla base del tipo dei dati puntati dai puntatori forniti in ingresso.

Scrivere un programma che definisce dinamicamente due variabili di tipo intero o di tipo reale a seconda della scelta effettuata da un utente e passi tali parametri alla funzione che effettua la divisione.

I materiali di supporto della lezione

Da C++ a UML

Capitolo 9, da par. 9.1 a 9.5

puntatori per accesso alla linea di comando: esempio 9.3

Funzioni che ritornano puntatori: par. 9.4, 9.5

Puntatori a void: par. 9.6

Puntatori a funzioni: par.9.7, 9.8, 9.9

  • Contenuti protetti da Creative Commons
  • Feed RSS
  • Condividi su FriendFeed
  • Condividi su Facebook
  • Segnala su Twitter
  • Condividi su LinkedIn
Progetto "Campus Virtuale" dell'Università degli Studi di Napoli Federico II, realizzato con il cofinanziamento dell'Unione europea. Asse V - Società dell'informazione - Obiettivo Operativo 5.1 e-Government ed e-Inclusion