Il concetto di modulo
Un modulo di un sistema software è un componente che: realizza una astrazione
È dotato di netta separazione tra:
• Interfaccia
• Corpo
L’interfaccia specifica il cosa (ovvero l’astrazione realizzata dal modulo).
Il corpo descrive come è realizzata l’astrazione
I tipi di astrazione realizzati da un modulo possono essere:
ASTRAZIONE SUL CONTROLLO:
Consiste nell'astrarre una data funzionalità dai dettagli della sua implementazione;
È ben supportata dai linguaggi di programmazione tradizionali tramite il concetto di
sottoprogramma.
ASTRAZIONE SUI DATI:
Consiste nell'astrarre le entità (oggetti) costituenti il sistema, descritte in termini di una
struttura dati e delle operazioni possibili su di essa;
Può essere realizzata con un uso opportuno delle tecniche di programmazione modulare nei
linguaggi tradizionali;
È supportata da appositi costrutti nei linguaggi di programmazione ad oggetti.
Per gestire la complessità dei programmi è utile individuare al loro interno dei moduli
funzionali, detti sottoprogrammi:
Ai moduli è passato il flusso di controllo in esecuzione: essi hanno un solo punto in ingresso
e di uscita (secondo i principi della programmazione strutturata).
Ai sottoprogrammi si assegnano determinate responsabilità, che includono la risoluzione di
un sotto-problema:
I sottoprogrammi hanno un nome e possono essere attivati durante l’esecuzione del
programma zero, uno o più volte.
Un sottoprogramma scambia informazioni con l’esterno, in ingresso e/o in uscita.
Se il sottogramma non scambia informazioni in uscita, si chiama anche procedura o routine.
Se il sottoprogramma fornisce informazioni in uscita, allora si chiama anche funzione.
Indicazione di un sottoprogramma
La definizione di un sottoprogramma si compone di titolo e il corpo.
Il titolo comprende:
il nome del sottoprogramma
i parametri di ingresso con il loro tipo
i dati output con il suo tipo
Il corpo è tipicamente un blocco sequenziale che comprende una sequenza di dichiarazioni
e di istruzioni.
C++
returnType functionName(paramType param_1, …, paramType param_n)
{
<function body>
}
Invocazione di un sottoprogramma
Una volta definito, è possibile richiamare il sottoprogramma utilizzando il suo nome e
fornendo i parametri di ingresso, se presenti.
L’invocazione del sottoprogramma è un’istruzione come le altre
Evita di riscrivere più volte lo stesso insieme di istruzioni laddove queste vadano ripetute
Consente di concentrarsi su un problema di piccole dimensioni rispetto al programma
principale
Parametrizzazione del codice
Si definiscono parametri formali i parametri dichiarati nell’interfaccia del sottoprogramma:
essi sono definiti (gli si assegna un valore) all’atto dell’invocazione dal chiamante
Quando il sottoprogramma viene invocato ai parametri formali si assegnano i valori dei
parametri effettivi (ovvero quelli usati per attivare il sottoprogramma).
I parametri effettivi e quelli formali devono essere dello stesso tipo e devono essere
specificati nello stesso ordine!
Esempio: Parametri formali ed effettivi (1/2)
Esempio: Parametri formali ed effettivi (2/2)
L’istruzione return
Per terminare il sottoprogramma e restituire il controllo al chiamante esistono speciali
istruzioni, definite nei linguaggi di programmazione: Il C++ adotta la keyword return
C++
double somma(double x, double y)
{
double z = x + y;
return z;
}
return termina l’esecuzione del sottoprogramma e restituisce al più un valore al chiamante,
del tipo dichiarato nell’interfaccia (returnType)
Esempio: istruzione return
Sottoprogrammi senza output: tipo void
Siccome in C++ è sempre necessario specificare un tipo di ritorno per un sottoprogramma,
nel caso in cui si voglia scrivere una routine (sottoprogramma senza dati in output), si può
usare il tipo void
C++
void stampaNumero(int numero)
{
cout << “Numero = “ << numero;
}
Nel caso di funzioni void è comunque possibile usare l’istruzione return per restituire il
controllo al chiamante.
In ogni caso, il controllo è restituito al chiamante al termine del blocco sequenziale del
sottoprogramma “{…}”
Visibilità
Possiamo dichiarare variabili sia nel programma principale, sia nei sottoprogrammi.
Le regole di visibilità (o di scope) indicano l’ambito in cui una variabile (in generale un
oggetto) è dichiarata in un programma.
Le regole di visibilità sono importanti per evitare interferenze con variabili che non rientrano
nel contesto di una trasformazione!
Un oggetto (una costante, una variabile, …) può avere visibilità:
- locale al sottoprogramma, se definito al suo interno (interfaccia e/o corpo).
- non locale al sottoprogramma, se è dichiarato esternamente.
Lo scope degli oggetti locali è limitato al sottoprogramma. Gli oggetti:
- non sono visibili in nessun altro sottoprogramma chiamato.
- non sono visibili al programma chiamante, dopo la terminazione del sottoprogramma.
Gli oggetti non locali al sottoprogramma:
- risultano visibili all’interno del sottoprogramma, e nei sottoprogrammi chiamati.
- Rimangono visibili al termine del sottoprogramma.
Oggetti non locali ad un sottopr. si indicano anche come globali, oppure esterni.
In C++ la visibilità di un oggetto è limitata al blocco sequenziale più interno nella quale è
definita:
I blocchi sequenziali più interni hanno visibilità degli oggetti di tutti i blocchi nei quali sono
contenuti.
Un blocco esterno non ha visibilità degli oggetti nei blocchi interni.
In C/C++ Le variabili dichiarate in un blocco sono variabili locali al sottoprogramma nel quale
il blocco è inserito:
- Non sono visibili ai sottoprogrammi chiamati.
- Non sono visibili dopo la terminazione del sottoprogramma.
Per creare variabili non locali ad un programma (variabili globali) in C/C++ occorre definirle
esterne ad ogni blocco (es.: in testa al sorgente).
Visibilità C/C++: osservazioni
La visibilità di un oggetto inizia dalla sua dichiarazione, sino alla chiusura del blocco
sequenziale che lo contiene (per le variabili locali), o del file sorgente (per le variabili
esterne). Questo vale, oltre che per i sottoprogrammi, anche per i costrutti di controllo!
Se un blocco dichiara una variabile che ha lo stesso nome di una variabile già visibile nel
suo ambito, si ha un fenomeno di aliasing.
La variabile con lo stesso blocco più interna maschera tutte le variabili con lo stesso nome
dichiarate nei blocchi più esterni.
Riepilogo definizione funzioni in C++
Sostituzione dei parametri formali
I parametri effettivi possono essere sostituiti a quelli formali in C/C++ in due diverse
modalità: per valore, per riferimento .
Passaggio per valore: al parametro formale viene assegnato il valore del parametro effettivo
Il parametro effettivo può essere un'espressione, una costante o una variabile. Se è
un'espressione (es.: “x+4*y)”, se ne calcola il valore e lo si assegna al corrispondente
parametro formale.
Nel caso di variabili, la sostituzione per valore garantisce che la variabile passata come
parametro effettivo non venga alterata (il sottoprogramma lavora sulla sua copia)
All’atto della chiamata viene fatta la copia del parametro effettivo con conseguente consumo
di tempo e spazio
Passaggio per valore: esempio
Passaggio per riferimento: al parametro formale viene assegnato l’indirizzo del parametro
effettivo
- Il parametro effettivo deve essere una variabile
- Il sottoprogramma lavora direttamente sulla variabile in memoria
- Il parametro effettivo occupa solo lo spazio necessario per contenere un indirizzo
Tramite un passaggio per riferimento possiamo creare funzioni C/C++ che restituiscono più
di un valore in uscita:
Sempre e al più un valore più essere restituito al chiamante tramite “return”
Tutti gli altri valori in output possono essere scambiati con passaggio dei parametri per
riferimento.
Il passaggio per riferimento non necessariamente ha l’obiettivo di aggiungere ulteriori dati in
output dalla funzione:
Possiamo scambiare per riferimento anche per variabili in input ai sottoprogrammi, invece
che per valore.
Il passaggio per riferimento risulta più efficiente quando si scambiano dati di grandi
dimensioni (es.: dati strutturati).
Scambio di parametri per riferimento: C++
Per scambiare un parametro per riferimento in C++: Si aggiunge una “&” dopo il tipo del
parametro.
C++
double somma(double& x, double& y);
I parametri passati per rifermento sono a tutti gli effetti di “inputoutput”.
Se si vuole proteggere la modifica di un parametro passato per riferimento (e dunque usarlo
esclusivamente per input) si può usare il qualificatore const
C++
double somma(const double& x, const double& y);
Funzioni a più parametri di output
In generale, se un sottoprogramma restituisce più parametri di output si può adottare la
seguente convenzione:
- Si usa come returnType della funzione il tipo void
- Si indicano i parametri in ingresso alla funzione (passandoli per valore, o per
riferimento “const”)
- Si indicano i parametri in output alla funzione (o input-output) passandoli per
riferimento
Il programma principale
Si chiamerà programma principale quella unità di programma che si interfaccia direttamente
con il sistema operativo.
In molti linguaggi il programma principale si chiama main
La funzione main è il punto da cui comincia l’esecuzione, indipendentemente dalla posizione
nel listato
La funzione main può prendere dei parametri in ingresso (che servono per interagire col
sistema operativo) e restituisce solitamente un valore intero, che indica l’esito
dell’esecuzione (codice di errore)
Per convenzione “0” indica che il programma ha completato la sua esecuzione
correttamente.
Tutti i programmi C/C++ contengono sempre e solo una funzione main
Librerie del linguaggio C/C++
Insiemi di sottoprogrammi sono organizzati in librerie, ovvero moduli di funzionalità che
possono essere facilmente riusati in più applicazioni.
Per includere una libreria nel linguaggio C/C++ si usa l’istruzione (da aggiungere
generalmente in testa al file sorgente):
#include <nomeLibreria>
Oppure (nel caso in cui non sia installata nel path predefinito dal compilatore):
#include “pathLIbreria”
Inclusa una libreria si può far uso degli oggetti che sono esportati da essa.
Le stringhe di caratteri
Una stringa è una sequenza di caratteri dotata di una lunghezza
Le stringhe sono definite come array di char terminati in ’\0’
char nome_stringa[dimensione];
Esempio:
char s[10];
È possibile definire un tipo stringa:
const int N=10;
typedef char Stringa[N];
Stringa s;
La stringa viene conclusa con un carattere speciale detto terminatore di stringa,
corrispondente al carattere ASCII NULL
– Si indica con ‘\0’
La seguente dichiarazione definisce una sequenza formata da massimo 9
caratteri
char s[10];
Inizializzazione di una stringa
Una stringa può essere inizializzata elencando i caratteri che devono farne parte, compreso
il terminatore
char s[10] = {‘c’,‘a’,‘s’,‘a’,‘\0’};
Solo per le stringhe è consentita una forma di inizializzazione più compatta usando le
costanti stringa
Il terminatore viene aggiunto automaticamente in fondo
char s[10] = “casa”;
Assegnazione di valori ad una stringa
Doppia modalità di assegnazione:
nome = “Angelo”;
nome[0] = ‘A’;
nome[1] = ‘n’;
nome[2] = ‘g’;
nome[3] = ‘e’;
nome[4] = ‘l’;
nome[5] = ‘o’;
nome[6] = ‘\0’;
Operazioni di I/O per una stringa
La seguente operazione stampa a video tutti i caratteri fino al terminatore
cout << nome;
La seguente operazione carica nel vettore tutti i caratteri inseriti da tastiera e
aggiunge il terminatore in coda automaticamente
cin >> nome;
Tipiche operazioni su stringhe
Elaborazioni tipiche delle stringhe sono le seguenti:
- Calcolo della loro lunghezza
- Assegnazione di una stringa ad un’altra
- Concatenazione di una stringa ad un’altra
- Confronto tra due stringhe
- Estrazione di una sottostringa da una stringa fissando la posizione da cui iniziare ad
estrarre il numero di caratteri
Calcolo della lunghezza di una stringa
lung = 0;
while (s[lung] != ‘\0’ {
lung++;
Assegnazione di una stringa ad un’altra
i = 0;
while (s1[i] != ‘\0’ {
s2[i] = s1[i]; // con una sola istruzione si potrebbe
i++; // scrivere s2[i++]=s1[i]
}
s2[i] = s1[i];
Confronto tra due stringhe
uguali = (lun1 == lun2); // solo se le lunghezze sono uguali
// la variabile è TRUE, altrimenti è FALSE
i = 0;
while (uguali && (s1[i] != ‘\0’)) { // si noti che il ciclo ha iniziato
// solo se le lunghezze sono uguali
uguali = (s1[i] == s2[i]);
i++;
}
/* il ciclo termina quando due caratteri sono diversi rendendo uguali FALSE o perché si è
raggiunta la posizione della terminazione con la variabile uguali rimasta TRUE
*/
Funzioni di libreria
Funzione Obiettivo
strlen Calcola la lunghezza di una stringa
strcpy Copia una stringa in un’altra
strncpy Copia n caratteri di una stringa in un’altra
strcat Accoda una stringa ad un’altra
strncat Accoda n caratteri di una stringa ad un’altra
strcmp Confronta due stringhe
strncmp Confronta i primi n caratteri di due stringhe
strchr Cerca un carattere in una stringa
strstr Cerca una stringa in un’altra
#include <string.h>
stringa s1 = “abcdef”, s2 = “ghilmn”, s3;
Funzione Argomenti Risultati Esempi di uso Val. Calcolati
strlen 1)Stringa di cui La lunghezza lung = 6
calcolare la della stringa di strlen(s1);
lunghezza tipo int
strcpy 1) Stringa in cui La stringa strcpy(s3,s1); La stringa s3
copiare copia diventa uguale a
2) Stringa da cui nel caso in cui strcpy(s3,”Angel “abcdef”
copiare è o”);
in grado di Assegna a s3 la
contenere stringa costante
l’altra
strncpy 1) Stringa in cui La stringa strncpy(s3,s1,3); La stringa s3
copiare copia diventa uguale a
2) Stringa da cui nel caso in cui “abc
copiare è
3) Numero di in grado di
caratteri da contenere gli n
copiare caratteri
dell’altra
#include <string.h>
stringa s1 = “abcdef”, s2 = “ghilmn”, s3;
Funzione Argomenti Risultati Esempi di uso Val. calcolati
strcat 1) Stringa a cui La stringa a cui strcat(s1,s2); s1 diventa
accodare è stata uguale a
accodata la strcat(s1,”123”); “abcdefghilmn”
2) Stringa da cui seconda nel
accodare caso in cui è in s1 diventa
grado di uguale a
contenerla “abcdef123”
strncat 1) Stringa a cui La stringa a cui strncat(s1,s2,3); s1 diventa
accodare sono stati uguale a
accodati gli n “abcdefghi”
2) Stringa da cui caratteri della
accodare seconda nel
caso in cui è in
3) Numero di grado di
caratteri da contenerli
accodare
#include <string.h>
stringa s1 = “abcdef”, s2 = “ghilmn”, s3;
Funzione Argomenti Risultati Es. di uso Val. Calcolati
strcmp 1) Prima stringa Un valore intero esito=strcmp(s1,s2); minore di zero
con l’esito del
2) Seconda confronto: esito=strcmp(s1,”ab uguale a zero
stringa da cdef”);
confrontare =0 se s1=s2
con la prima <0 se s1<s2
>0 se s1>s2
strncmp 1) Prima stringa Un valore intero esito=strncmp(s1, uguale a zero
con l’esito del ”abczwr”,3);
2) Seconda confronto: maggiore di
stringa da esito=strncmp(s2, zero
confrontare =0 se s1=s2 ”a”,1);
con la prima <0 se s1<s2
>0 se s1>s2
3) I primi n
caratteri da
confrontare
#include <string.h>
stringa s1 = “abcdef”, s2 = “ghilmn”, s3;
Funzione Argomenti Risultati Es. di uso Val.
calcolati
strchr 1) Stringa in cui Un puntatore ptr=strchr(s1,’f’); diverso da
cercare con la posizione NULL
del carattere se ptr=strchr(s1,’1’);
2) Carattere da è presente, o uguale a
cercare con valore NULL
NULL se non è
presente
strstr 1) Stringa in cui Un puntatore ptr=strstr(s1,s2); uguale a
cercare con la posizione NULL
della stringa se ptr=strstr(s1,”bcd”);
2) Stringa da è presente, o diverso da
cercare con valore NULL
NULL se non è
presente
Array multidimensionali
Si possono anche dichiarare array multidimensionali
Sono strutture in cui sono necessarie più posizioni per identificare un elemento
Possono essere visti come array di array
<Tipo> nome_array[dim1][dim2]…[dimn];
Esempio:
Array bidimensionale di interi di dimensioni 2 per 3: 6 elementi in totale
int mat[2][3]; // mat[0][0]: prima posizione
// mat[1][2]: ultima posizione
mat[i][j]; // Accesso alle posizioni i e j
È possibile anche dichiarare il tipo di un array multidimensionale
Un array bidimensionale è anche detto matrice, mentre un array monodimensionale vettore
const int N=2;
const int M=3;
typedef int Matrice[N][M]
Matrice mat;
Allocazione degli array bidimensionali
Anche gli array bidimensionali vengono disposti in locazioni di memoria consecutive
In particolare vengono disposte le righe una dopo l’altra
int mat[2][3] = {1,2,3,4,5,6};
mat[0][0]=1;
mat[0][1]=2;
mat[0][2]=3;
mat[1][0]=4;
mat[1][1]=5;
mat[1][2]=6;
Inizializzazione degli array bidimensionali
È anche possibile raggruppare le righe tra parentesi, sempre indicandole una
dopo l’altra
int mat[2][3] = {{1,2,3},
{4,5,6}};
Se nell’inizializzazione si specificano meno valori di quelli presenti nella matrice, i
valori rimanenti sono inizializzati automaticamente a ZERO
Ad esempio:
int mat[2][3] = {0};
Cardinalità degli array
Le cardinalità fissano il numero massimo di elementi dell’array che il programma
può usare
È possibile anche usarne una parte
In tal caso va gestito il grado di riempimento dell’array con una variabile che indica il numero
di elementi che sono realmente usati dal programma
cin >> riemp;
for (int i = 0; i < riemp; i++) {
cin >> v[i];
}
Anche per gli array multidimensionali è possibile utilizzare una parte degli
elementi
cin >> num_rig;
cin >> num_col;
for (int r = 0; r < num_rig; r++) {
for (int c = 0; c < num_col; c++) {
cin >> mat[r][c];
}
}
Passaggio di matrici
Per le matrici valgono considerazioni analoghe
L’unica differenza è che la funzione deve essere informata del numero massimo di colonne
della matrice
//Matrice m con num di righe imprecisate e 50 colonne
void calcola(float m[ ] [50]);
float matr[10][50];
calcola(matr);
Esempio: parametro di tipo matrice
Funzione che determina il numero più grande in una matrice
float max_mat(float mat[ ] [100], int n, int m) {
float max = mat [0] [0];
for (int i = 0;i < n; i++) {
for (int j = 0; j < m; j++) {
if (max < mat [i] [j]) {
max = mat [i] [j];
}
}
}
return max;
}
Passaggio di array multidimensionali
Il caso degli array a più di due dimensioni è una generalizzazione del caso della
matrice
Nella dichiarazione del parametro formale l’unica cardinalità che può non essere espressa è
la prima
void calcola(float m[ ] [10] [20] [5])
Uso di typedef con array
La corrispondenza tra parametri effettivi e formali può essere agevolata
dall’utilizzo di typedef
Semplifica la scrittura di programmi e limita l’introduzione di errori
I record
Il record è una collezione di elementi anche di diverso tipo, a cui viene dato un
unico nome: Detto anche struttura
Gli elementi sono detti campi: Possono essere di tipi differenti
La dichiarazione di un record prevede la specifica di:
- Nome del record
- Campi del record
Dichiarazione di un record
È possibile effettuare l’inizializzazione dei campi di un record elencando i valori
da assegnare
struct Nome_record record = {valore1,…,valoreN};
Es.:
struct Studente studente1 = {Mario, Bianchi, 450001};
Funzione di accesso ad un record
Una volta definito un record, si può solo operare con i suoi campi accedendo ad
essi mediante la dot notation
nome_record.nome_campo
Es.:
[Link] = ‘‘Mario’’;
Operazioni sui record
Sui record le uniche operazioni ammesse sono:
- quelle definite sui singoli campi
- la copia di un intero record in un altro dello stesso tipo
Esempio
cout << “\nInserisci giorno: “;
cin >> [Link];
cout << “\nInserisci mese: “;
cin >> [Link];
cout << “\nInserisci anno: “;
cin >> [Link];
nascita = data; —-----------> [Link] = [Link];
[Link] = [Link];
[Link] = [Link];
Tipi di record
Strutture definite con definizioni differenti, ma identiche campo per campo, non sono
considerate dello stesso tipo: La compatibilità è nominale
Strutture con nomi differenti possono contemplare al loro interno campi che hanno gli stessi
nomi
Strutture complesse
Array di record: esempio
Allocazione in memoria di un record
Il compilatore alloca memoria sufficiente per accogliere tutti i campi di una struttura
Esempio: calc è un record di 92 byte
struct Calciatore{
char nome[30];
char cognome[40];
char ruolo[20];
int eta;
}calc;
Uso di typedef con record
I puntatori
Il C++ prevede puntatori a funzione e puntatori a dati di qualsiasi natura,
semplici o strutturati.
In particolare il puntatore viene utilizzato :
– per il riferimento a liste di variabili dinamiche;
– per il riferimento a funzioni;
– per gli array, in particolare per l'elaborazione di stringhe;
– per i parametri formali di funzione, in alternativa allo scambio per riferimento;
– per il riferimento a strutture dati restituite da funzioni;
– per il riferimento a locazioni specifiche della memoria, associate a dispositivi hardware (ad
esempio per l’ingresso-uscita).
Il puntatore del C++ è strettamente tipizzato;
Un tipo che può assumere come insieme dei valori tutti gli indirizzi di memoria:
Dunque la sua dimensione è strettamente collegata all’architettura del processore (e.g. 32
bit oppure 64 bit).
Un puntatore ad un variabile di tipo T identifica l’indirizzo di memoria della variabile.
Esempio di Puntatore in C++
• typedef int *pint;
• pint pcount;
• int count=500;
• pcount=&count;
Uso dei puntatori
• p è un variabile di tipo puntatore
• p=&x al puntatore p viene assegnato l’indirizzo di x
• *p denota la variabile puntata da p
(dereferenziazione)
Esempi di Assegnazione
Operazioni con i Puntatori
• char* pc; //pc è un puntatore a variabile di tipo carattere
• int* pi; //pi è un puntatore a variabile di tipo intero
• float* pf;//pf è un puntatore a variabile di tipo float
• double* pd; //pd è un puntatore a variabile di tipo doppia
precisione
Il linguaggio C++ tratta esplicitamente le espressioni su dati di tipo puntatore (aritmetica dei
puntatori).
Sono previsti i seguenti operatori:
- l’operatore di assegnazione tra puntatori che puntano allo stesso tipo T*;
- gli operatori unari di incremento (++) e decremento (--) unitari (in forma
prefissa e postfissa ++p, p++, --p, p--);
- l'operatore di somma o differenza tra un puntatore ed un intero (p+i
punta all'elemento che segue di i posizioni quello puntato da p);
l'incremento a p è tale che p+1 punta all’area di memoria immediatamente successiva a
quella impegnata dall’elemento puntato da p.
Questa tecnica è particolarmente utile per i puntatori ad array.
Puntatore a Dati Strutturati
Un puntatore può puntare a variabili strutturate, in particolare esamineremo:
- puntatori ad array;
- puntatori a record;
Il puntatore ad array può utilmente essere sostituito dalla notazione a[i] tipica degli array
I Puntatori ad Array
Un puntatore ad un array a è una variabile che punta alla prima locazione dell'array (a[0] in
C++); considerando che i successivi elementi dell'array sono allocati in posizioni contigue,
esso consente di puntare anche a tutti gli altri elementi dell'array.
Un puntatore ad array è dunque di per sé un puntatore al tipo T degli elementi dell'array.
Esempio:
const int dim=100;
float a [dim]; /* a è un array di dim
elementi di tipo float*/
float* p; // p è un puntatore a float
/*azzeramento di tutti gli elementi di un array di 100 elementi*/
for (p = a, int i=0; i<dim; i++, p++)
*p= 0;
I Puntatori a Record
Un puntatore ad un record è una variabile che punta all'indirizzo di memoria ove il record è
allocato; Esso è molto utile nella realizzazione dei tipi dinamici.
// Dichiarazioni di un tipo strutturato Ts
struct Ts { // Ts è un tipo strutturato
D1;
.....;
Dn ; } ;
Ts r; // r è variabile di tipo Ts
// Dichiarazione di un tipo puntatore a Ts
typedef Ts* Punt;
// Dichiarazione di variabile di tipo puntatore a Ts
Punt p;
Punt p = &r; // p è una variabile puntatore inizializzata ad r
p può essere ridefinito nel corso del programma
p = &r1; // ora p punta alla variabile r1 di tipo Ts
Accesso alla singola componente:
(*p).Di; ←—-----> p->Di;
Puntatori a Record: esempio
// Definizione di tipo struttura:
struct Abbonati { // definisce la struttura Abbonati
String nominativo;
String indirizzo;
int numTel; };
typedef Abbonati* Pabb;
// Definizione di variabili
Abbonati a; // a una variabile di tipo Abbonati
Pabb p= &a; // p, di tipo Pabb, è inizializzato ad a
// Accesso alla componente nominativo mediante puntatore p->nominativo;
Parametro di tipo puntatore
Il generico argomento p di tipo puntatore è solitamente scambiato per valore, nel senso che
l’indirizzo viene ricopiato nel corrispondente parametro formale.
Tale parametro può essere di ingresso, di uscita, o di ingresso-uscita.
L’uso del puntatore come parametro di scambio costituisce una soluzione alternativa
all’impiego dello scambio per riferimento
Lo scambio per riferimento è però più potente in quanto il compilatore tratta
automaticamente il parametro formale con la tecnica dell’indirizzamento indiretto.
Una funzione può restituire un valore di tipo puntatore (cioè un indirizzo):
T* f(); //la funzione restituisce un
//valore di tipo puntatore a T
ove T è il tipo puntato.
Il puntatore restituito punta a un’area associata a uno dei parametri di ingresso della
funzione.
//ricerca il carattere c nella stringa s e
//restituisce il puntatore alla prima occorrenza
char* strchr(const char s[], const char c)
char s[ ]="abcdef";
char* p=strchr(s,’d’); //p punta a s[3]
Argomento e valore restituito di tipo puntatore
È possibile che una funzione abbia un argomento di tipo T* e un valore restituito del
medesimo tipo:
T* f(…T*…);
In tal caso sono ammesse istruzioni del tipo:
f(…f(…p…)…)
Che producono l’esecuzione concatenata della funzione f.
Esempio:
//concatenazione di stringhe
strcat(strcat(a,b),c);