SlideShare a Scribd company logo
1 of 88
Fondamenti di informatica 1
Sottoprogrammi (Funzioni)

1
Problema
• Abbiamo visto sequenze di istruzioni che risolvono
particolari “sotto-problemi”: controllo della primalità
di un numero, confronto di stringhe, etc.
• È possibile riutilizzare queste sequenze?
• È possibile consentire ad altri di riutilizzare queste
sequenze?
• È possibile dare a queste sequenze un nome che
ne indichi la funzionalità?
• È possibile “svincolare” lo sviluppo di soluzioni a
sotto-problemi di questo tipo dallo sviluppo di una
soluzione “complessa”?
Se fosse possibile…
• …fare queste cose, i vantaggi sarebbero
molti:
• Potendo riutilizzare facilmente le sequenze,
aumenterebbe la produttività (potremmo
scrivere più codice in meno tempo)
• Potremmo consentire ad altri di aumentare la
loro produttività
• Potremmo rendere il codice più leggibile
• Potremmo “spezzare” la risoluzione di un
problema in sotto-problemi
Le funzioni
• I principali linguaggi di programmazione offrono, in
risposta a queste esigenze, lo strumento delle
procedure o funzioni
• In C e C++, una funzione è una sequenza di comandi
che:
– ha un nome
– può essere invocata (cioè può esserne richiesta l’esecuzione)
– può ricevere dei valori di parametri che ne influenzano
l’esecuzione
– può produrre un valore risultato

4
Motivazioni
•

Modularità nello sviluppo del codice
– Affrontare il problema per raffinamenti successivi

•

Riusabilità
– Scrivere una sola volta il codice e usarlo più volte
– Esempi:
• un algoritmo di ordinamento
• Le operazioni del calcolo matriciale
• La ricerca di una parola in un testo

•

Astrazione
– Esprimere in modo sintetico operazioni complesse
– Definire operazioni specifiche dei tipi di dato definiti dal programmatore
– Esempi:
• Calcolo “totale + iva” di un ordine
• Lettura scrittura transazioni di vendita dei libri
• Operazioni con entità quali punti, segmenti, poligoni, numeri complessi
Esempio: gestione del dialogo con
l'utente
int main() {
char scelta;
int valore, risultato;
while (true) { // menu, loop infinito
cout << "Premere A per inserire un num tra 0 e 10 e calcolarne il cubo" << endl;
cout << "Premere B per inserire un num tra 11 e 20 e calcolarne il quadrato" << endl;
cout << "Premere C per inserire un num tra 21 e 30 e calcolarne il doppio" << endl;
cout << "Premere Q per uscire" << endl;
cin >> scelta;
switch (scelta) {
case 'a': case 'A':
cout << "Inserisci valore" << endl;
cin >> valore;
risultato = valore * valore * valore;
cout << "risultato = " << risultato << endl;
break;

6
… continua
case 'b': case 'B':
cout << "Inserisci valore" << endl;
cin >> valore;
risultato = valore * valore;
cout << "risultato = " << risultato << endl;
break;
case 'c': case 'C':
cout << "Inserisci valore" << endl;
cin >> valore;
risultato = valore * 2;
cout << "risultato = " << risultato << endl;
break;
case 'q': case 'Q':
cout << "GRAZIE E ARRIVEDERCI";
return 0; // termina il ciclo infinito
}
}
}

7
Fattorizzazione del dialogo
char menu() {
char ch;
cout << "Premere
cout << "Premere
cout << "Premere
cout << "Premere
cin >> ch;
return ch;
}

A
B
C
Q

per
per
per
per

inserire un num tra 0 e 10 e calcolarne il cubo" << endl;
inserire un num tra 11 e 20 e calcolarne il quadrato" << endl;
inserire un num tra 21 e 30 e calcolarne il doppio" << endl;
uscire" << endl;

int leggi() {
int v;
cout << "Inserisci valore" << endl;
cin >> v;
return v;
}

8
Programma principale
int main() {
char scelta;
int valore, risultato;
while (true) { // menu, loop infinito
scelta = menu();
switch (scelta) {
case 'a': case 'A':
valore = leggi();
risultato = valore * valore * valore;
cout << "risultato = " << risultato << endl;
break;
case 'b': case 'B':
valore = leggi();
risultato = valore * valore;
cout << "risultato = " << risultato << endl;
break;
case 'c': case 'C':
valore = leggi();
risultato = valore * 2;
cout << "risultato = " << risultato << endl;
break;
case 'q': case 'Q':
cout << "GRAZIE E ARRIVEDERCI";
return 0;
}
}
}

9
Osservazioni
• La funzione main() invoca le funzioni menu() e
leggi()
– main(): funzione chiamante
– menu() e leggi(): funzioni chiamate

• La comunicazione chiamante-chiamato nel caso
visto avviene tramite il valore di ritorno
char menu() {.. return ch; }
int main () {…
..
scelta = menu();

• Il chiamante copia in una propria variabile locale il
valore prodotto dall'invocazione della funzione
10
Definizione delle funzioni
<tipo restituito> <nome della funzione>
(<lista parametri>) {
}

•Tipo restituito: il tipo del valore di ritorno della
funzione
– Non può essere un tipo array

•Nome: un identificatore
•Lista parametri: una lista di coppie
<tipo-nomeParametro> separate da virgole
– Si possono usare 0 o più parametri

•{ . . . }: corpo della funzione
Definizione di una funzione
int leggi(int min, int max) {
int v;
bool ok = false;
while (!ok) {
cout << "Inserisci valore" << endl;
cin >> v;
if (v >= min && v <= max)
ok = true;
}
return v;
}

12
Chiamata di una funzione
int main() {
char scelta;
int valore, risultato;
while (true) { // menu, loop infinito
scelta = menu();
switch (scelta) {
case 'a':
case 'A':
valore = leggi(0,10);
risultato = valore * valore * valore;
cout << "risultato = " << risultato << endl;
break;

•Leggi(0,10) è un'espressione di chiamata a
funzione
•0 e 10 sono gli argomenti della chiamata
•Valore dell'espressione è il valore restituito dalla
funzione
•Il tipo dell'espressione è il tipo di ritorno delle funzione
13
Esempio
• Definiamo il fattoriale come una funzione
int fatt(int val)
{
int ris = 1;
while (val > 1)
ris *= val--; //assegna ris * val a ris e decrementa val
return ris;
}
int main() {
int numero;
cout << "Inserire un numero positivo" << endl;
cin >> numero;
cout << "Il fattoriale di " << numero << " vale "
<< fatt(numero) << endl;
return 0;
}
Sintassi dell’invocazione
• L'invocazione di una funzione è un’espressione
– es: possiamo immaginare una funzione somma(a,b) equivalente
ad a+b

• Quando, nell’esecuzione del codice, è necessario ‘valutare’
un’espressione-funzione, la funzione viene invocata
• La sintassi per la formulazione di una espressione-funzione è
semplicemente:
<nome funzione>(<lista argomenti>)
– dove <lista argomenti> è una lista di espressioni di tipo
corrispondente ai parametri della definizione della funzione

• Corrispondenza posizionale
– al primo parametro corrisponde il primo argomento, al secondo il
secondo, e così via ...
Semantica dell’invocazione
• Quando una funzione viene invocata:
– Le espressioni corrispondenti agli argomenti vengono
valutate, e il valore reso disponibile ("passato") alla
funzione
– I valori passati alla funzione sono utilizzati per inizializzare
i parametri della funzione
– Il controllo passa da chiamante alla funzione chiamata,
– Questa viene eseguita, fino a quando non viene incontrato
un comando return (o il termine del blocco principale })
– Il “valore” dell’espressione-funzione corrispondente alla
chiamata è il valore dell'espressione che appare
nell'istruzione return che ha causato la terminazione
• NB: oppure nessun valore se la funzione ha void come tipo di
ritorno
Comunicazione chiamantechiamato
• L'uso del valore di ritorno non è sufficiente
• Può essere necessario:
– Che il chiamante comunichi dei valori al chiamato
– Che il chiamato restituisca più di un valore al
chiamante
– Che il chiamato modifichi direttamente un valore
del chiamante

• Ad esempio, la funzione leggi() deve ricevere
dal chiamante i limiti inferiore e superiore del
valore da chiedere all'utente
17
Modalità di passaggio degli
argomenti
• Chiamante e chiamato devono comunicare
• Ci sono diverse esigenze a cui corrispondono diversi
meccanismi di comunicazione
– Il chiamante passa informazione al chiamato
• Il chiamante passa argomenti al chiamato
• Il chiamato opera su una variabile globale visibile al chiamante

– Il chiamante riceve informazione dal chiamato
• Il chiamato restituisce un valore
• Il chiamato modifica una variabile del chiamante
• Il chiamato opera su una variabile globale visibile al chiamante

• Ulteriore esigenza:
– Evitare di copiare argomenti di grandi dimensioni nei
parametri del chiamato, se non necessario
18
Argomenti e parametri
• Gli argomenti fungono da inizializzatori dei
parametri di una funzione
• Notazione posizionale:
– Argomento1  parametro1, argomento2
parametro2
– Tutti gli argomenti richiesti devono essere forniti
– Il tipo di un argomento deve corrispondere a quello
del parametro, secondo le stesse regole di
inizializzazione delle variabili

• Nomenclatura alternativa:
– Parametri  Parametri formali
– Argomenti Parametri attuali
19
Passaggio per valore
• Call by value
• Funziona come nell'inizializzazione di una
variabile
– Il valore dell'argomento (inizializer) viene
copiato nel parametro
– Modifiche del parametro NON cambiano
l'argomento
void callByValue(int i) {
i = i * 2; // NON modifica l'argomento passato
}
20
Passaggio per riferimento
• Call by reference
• I parametri sono di ripo reference (&)
– Il valore dell'argomento (inizializer) è un sinonimo
del parametro
– Modifiche del parametro CAMBIANO l'argomento
del chiamante
– L'argomento deve essere una variabile, non una
costante
void callByRef(int &i) {
i = i * 2; // modifica l'argomento passato del chiamante
}
21
Passaggio per riferimento costante
• Call by const reference
• I parametri sono di ripo const reference (const &)
– Il valore dell'argomento (inizializer) è un sinonimo del
parametro
– Il costruttore const IMPEDISCE modifiche del
parametro, come nel passaggio per copia
– Come nel passaggio per riferimento si evita di copiare
il valore del parametro
– L'argomento deve essere una variabile, non una
costante
void callByConstRef(const int &i) {
i = i * 2; // errore: non si può modifica l'argomento
}

22
Esempio
void callByValue(int i) {
i = i * 2;
}
void callByRef(int &i) {
i = i * 2;
}
int main() {
int j=1;
callByValue(j);
cout << "After call by value j's value is " << j << endl;
callByRef(j);
cout << "After call by reference j's value is " << j << endl;
return 0;
}

23
Altro esempio
void swapByValue(int p, int q) {
int temp;
temp = p;
p = q;
q = temp;
}
void swapByRef(int &p, int &q) {
int temp;
temp = p;
p = q;
q = temp;
}
int main() {
int a = 1, b = 2;
cout << "Valori iniziali: " << a << ' ' << b << endl;
swapByValue(a, b);
cout << "Valori dopo swapByValue: " << a << ' ' << b << endl;
swapByRef(a, b);
cout << "Valori dopo swapByRef: " << a << ' ' << b << endl;
return 0;
}

24
Funzioni vs procedure
• Esistono sottoprogrammi che restituiscono valori
(funzioni) e sottoprogrammi che non lo fanno
(procedure)
• In C++ e C è possibile scrivere un sottoprogramma
che non restituisce un valore, ma produce solo un
effetto sullo stato del programma
• Tale fatto è segnalato dal tipo di ritorno void
void error_line (int line) {
cout << "Errore alla linea" << line << endl;
}

• Il sottoprogramma viene chiamato come segue:
error_line (line_number);

• La terminazione della funzione e il ritorno del
controllo al chiamante avvengono a fine blocco }
25
Intercambiabilità di procedure e
funzioni
• Funzione:

• Procedura:

int f (int par1) {
/* calcola valore
di variabile “risultato”
*/
return (risultato);
}

void f (int par1, int & par2)
{
/* calcola valore di
variabile “risultato” */
par2 = risultato;
}

• chiamata:
y = f(x);

• chiamata:
f (x, y);

• y del chiamante riceve il
valore di par2 del
chiamato
Raccomandazioni di stile
• Per le funzioni:
– passare i parametri per copia
– usare passaggio per riferimento per parametri di grandi
dimensione, per evitare la copia
– evitare l'abuso di variabili non locali, che rendono difficile
la comprensione dei rapporti tra chiamante e chiamato

• Una funzione che abbia parametri passati
per reference e ne modifichi il valore, si dice avere
un effetto collaterale (side effect)
• Benché ammissibili, gli effetti collaterali si devono
usare con prudenza, e in generale sono
sconsigliabili quando non strettamente necessari
27
Parametri: pro e contro
Modi di
passaggio

per valore
("per copia")

per riferimento

per riferimento
costante

Caratteristiche
Tempo e spazio
necessari a trasferire
(copiare) i dati

grandi
(per parametri di
grandi dimensioni)

piccoli
(si copia un indirizzo,
la cui dimensione non
dipende dai dati)

piccoli
(si evita la copia, si
definisce un sinonimo
dell'argomento)

C'è rischio di
effetti collaterali
indesiderati?

No
(i parametri attuale e
formale sono distinti)

Sì
(i parametri attuale e
formale "di fatto"
coincidono)

Sì
(i parametri attuale e
formale sono
sinonimi)

Permette la
restituzione di valori
al chiamante?

No

Sì

Sì

28
Esempio
• Funzione per ricercare un carattere in una
stringa e restituire la prima posizione in cui
appare e il numero di occorrenze
• Progettazione della comunicazione
– Input:
• La stringa: per riferimento costante
• Il carattere da cercare: per copia

– Output :
• La posizione: valore restituito (o parametro per
riferimento)
• Numero di occorrenze: parametro per riferimento
29
Funzione
string::size_type find_char(const string &s, char c, string::size_type &occurs)
{
auto ret = s.size(); // position of the first occurrence, if any
occurs = 0; // set the occurrence count parameter
for (decltype(ret) i = 0; i != s.size(); ++i) {
if (s[i] == c) {
if (ret == s.size())
ret = i; // remember first occurrence of c
++occurs; // increment the occurrence count
}
}
return ret; // count is returned implicitly in occurs
}

30
Main
#include <iostream>
#include <string>
using namespace std;
int main() {
string someString;
string::size_type count;
cout << "Insert the string to search" << endl;
getline(cin, someString);
char c;
cout << "Insert the char to search" << endl;
cin >> c;
auto index = find_char(someString, c, count);
if (index < someString.size()) {
cout << "Character " << c << " first occurs at pos: " << index << endl;
cout << "Number of occurrences is: " << count << endl;
} else
cout << "Character not found" << endl;
return 0;
31
}
Utilità di void e terminazione senza
valore

• http://stackoverflow.com/questions/11560068/
• http://stackoverflow.com/questions/1610030/w

32
Visibilità e durata
• Tutte le variabili di un programma hanno una visibilità (scope)
e una durata (lifetime)
– Scope: porzione del programma in cui il nome è visibile
– Lifetime: tempo durante l'esecuzione in cui la variabile esiste

• Il corpo della funzione definisce un ambito di visibilità (scope)
per cui
– Le variabili dichiarate nella funzione e i parametri sono locali alla
funzione
– Variabili locali e parametri della stessa funzione devono avere
nomi diversi
– Variabili locali e parametri di funzioni diverse possono avere
nomi coincidenti
– I nomi locali mascherano eventuali nomi uguali definiti in scope
più esterni (nomi di variabili globali)

33
Il blocco
• E' un delimitatore esplicito sia di scope sia di
lifetime
• Può comparire ovunque la sintassi preveda
un'istruzione
• Si compone di due parti racchiuse tra { }
– una sequenza di istruzioni
– può anche contenere dichiarazioni

• Due blocchi possono essere:
– annidati (uno è interno all’altro)
– paralleli (entrambi interni a un terzo blocco, ma
non annidati tra loro)
int
g1, g2;
char
g3;
int f1(int par1, int
int f2(int par3, int

par2); // Prototipo di f1
par1); // Prototipo di f2

main () {
int a, b;
{ char a, c;
...
{float a;
...
}
// Fine blocco2
}
// Fine blocco1
}
// Fine main
int f1(int par1,
intd;
{ int e;
...
}
// Fine
{ int d;
...
}
// Fine
}
// Fine
int
}

int

modello “a contorni”

par2) {

blocco3

par1, par2,

blocco4
f1

f2(int par3, int
int f;
...
// Fine f2

par4) {

par3, par4,
Mascheramento della visibilità
• La dichiarazione di un elemento in una
funzione o in un blocco maschera le
eventuali entità omonime più “esterne”
g1,g2,g3

livello globale

a,b

main

a,c

blocco1

a,d

blocco2

d

blocco3

f1

È possibile (ma
sconsigliato)
riusare nomi di
variabili
Denominare le variabili locali alle
funzioni
#include <iostream>
using namespace std;

double potenza (double base, int esp) {
double ris = 1.0;
for (int i = 0; i < esp; ++i)
ris = ris * base;
return ris;
}

Le variabili locali e i
parametri di funzioni diverse
appartengono a "scope"
distinti.
Per cui possono anche
avere lo stesso nome, senza
pericolo di confusione.
Meglio però evitare tale
pratica

int main() {
float base;
cout << "Inserire un numero reale come base" << endl;
cin >> base;
int esp;
cout << "Inserire un numero reale come base" << endl;
cin >> esp;
cout << endl << base << " elevato a " << esp << " vale " << potenza(base, esp) << endl;
return 0;
}
Durata delle variabili (lifetime)
• Va dalla creazione (allocazione della memoria) alla
distruzione (rilascio della memoria allocata)
• Due classi di variabili
– Statiche:
• Allocate una volta per tutte
• Distrutte solo al termine dell’esecuzione del programma
• Sono tutte le variabili globali e quelle locali al main()
– Anche le variabili di funzione o blocco dichiarabili static

• Possono fungere da ulteriore canale di comunicazione tra funzioni

– Automatiche:
•
•
•
•
•

Sono quelle dichiarate nelle funzioni (inclusi i parametri) e nei blocchi
Sono create quando il flusso di esecuzione "entra" nel loro ambito di visibilità
Sono distrutte all’uscita da tale ambito
Sono allocate ad ogni esecusione in celle di memoria differenti
Non conservano i valori prodotti da precedenti esecuzioni della funzione o
del blocco
Oggetti locali statici
•
•

Esigenza: memorizzare informazione locale a una funzione ( o a un
blocco) che permane per tutta l'esecuzione del programma (e non
solo per una singola chiamata)
Meccanismo: variabili locali static

int conta_chiamate()
{
static int ctr = 0; // persiste dopo la chiamata
return ++ctr;
}
int main()
{
for (int i = 0; i != 10; ++i)
cout << conta_chiamate() << endl;
return 0;
}

•

Stampa numeri da 1 a 10 incluso
39
Modello di esecuzione
• In seguito a una chiamata a sottoprogramma,
il programma in corso viene sospeso e il controllo
passa al sottoprogramma
• Al livello dell'ambiente di esecuzione:
– Salvataggio dell'indirizzo della prossima istruzione da
eseguire (program counter, PC) e del contesto
(valori di variabili) del programma chiamante
– Assegnazione al PC dell’indirizzo del sottoprogramma
– Esecuzione del sottoprogramma
– Ritorno al programma chiamante con ripristino
del suo contesto
40
Record di attivazione
• Ogni sottoprogramma (incluso il main) ha
associato un record di attivazione. Contiene:
– tutti i dati relativi all’ambiente locale del
sottoprogramma
– l’indirizzo di ritorno nel programma chiamante (serve
per poter eseguire l'istruzione del chiamante
immediatamente successiva alla invocazione del
sotto-programma)

• Per ogni attivazione di sottoprogramma si
crea un nuovo record di attivazione
41
Perché è necessario?
• In generale, una funzione può essere chiamata un
numero imprecisato di volte
• Ogni chiamata a procedura richiede allocazione di
spazio di memoria per le sue variabili locali
– Il compilatore potrebbe “preparare” un ambiente per ogni
funzione definita? In generale no...

• Vi sono procedure che richiamano se stesse
(vedremo: ricorsione)
– Possono esistere più “istanze” di una funzione,
“addormentate” in attesa della terminazione di una
“gemella” per riprendere l’esecuzione

• In questo ultimo caso il compilatore non può sapere
quanto spazio allocare per le variabili del programma
(nei vari ambienti)
42
La pila (stack) di sistema
• Una porzione della memoria di lavoro, chiamata stack (pila):
modalità LIFO (Last in First Out) permette al sistema operativo
di gestire i processi e di eseguire le chiamate a sottoprogramma
• Lo Stack Pointer (puntat. alla pila) è un registro che contiene
l’indirizzo della parola di memoria da leggere nello stack
312
311
310
• Operazione di inserimento:
- incremento SP
...
- scrittura nella parola indirizzata da SP
• Operazione di estrazione:
- lettura da parola indirizzata da SP
303
- decremento SP
SP

43

312

Stack pointer = 312
Esempio
main()

ambiente
globale

Chiama f1

crescita
verso
l'alto

...

r.d.a. di f1(3°)
r.d.a. di f2(2°)

f2()

f1()

r.d.a. di f1(2°)
r.d.a. di f2(1°)

Chiama f2

Chiama f1

r.d.a. di f1(1°)
r.d.a. di main()
variabili globali

44
Esempio di esecuzione
int potenza(int a,int b);
int moltiplica(int x,int y);
int main(){
int i=2,j=3,k;
k=moltiplica(i,j);
k=potenza(i,j);
}
int potenza(int a,int b){
int i, p=1;
for(i=1;i<=b;i++)
p=moltiplica(p,a);
return p;
}
int moltiplica(int x,int y){
return x*y;
}

i=2

j=3

k
Stack
int potenza(int a,int b);
int moltiplica(int x,int y);
int main(){
int i=2,j=3,k;
k=moltiplica(i,j);
k=potenza(i,j);
}
int potenza(int a,int b){
int i, p=1;
for(i=1;i<=b;i++)
p=moltiplica(p,a);
return p;
}
int moltiplica(int x,int y){
return x*y;
}

i=2

j=3

x=2 y=3

k
Stack
int potenza(int a,int b);
int moltiplica(int x,int y);

i=2

j=3

k=6

int main(){
int i=2,j=3,k;
k=moltiplica(i,j);
k=potenza(i,j);
}
int potenza(int a,int b){
int i, p=1;
for(i=1;i<=b;i++)
p=moltiplica(p,a);
return p;
}
int moltiplica(int x,int y){
return x*y;
}
47
Stack
int potenza(int a,int b);
int moltiplica(int x,int y);

i=2

j=3

k=6

a=2 b=3 i

p=1

int main(){
int i=2,j=3,k;
k=moltiplica(i,j);
k=potenza(i,j);
}
int potenza(int a,int b){
int i, p=1;
for(i=1;i<=b;i++)
p=moltiplica(p,a);
return p;
}
int moltiplica(int x,int y){
return x*y;
}
48
Stack
int potenza(int a,int b);
int moltiplica(int x,int y);

i=2

j=3

k=6

a=2 b=3 i=1

p=1

int main(){
int i=2,j=3,k;
k=moltiplica(i,j);
k=potenza(i,j);
}
int potenza(int a,int b){
int i, p=1;
for(i=1;i<=b;i++)
p=moltiplica(p,a);
return p;
}
int moltiplica(int x,int y){
return x*y;
}
49
Stack
int potenza(int a,int b);
int moltiplica(int x,int y);

i=2

j=3

int main(){
int i=2,j=3,k;
k=moltiplica(i,j);
k=potenza(i,j);
}

k=6

x=1 y=2

a=2 b=3 i=1

p=1

int potenza(int a,int b){
int i, p=1;
for(i=1;i<=b;i++)
p=moltiplica(p,a);
return p;
}
int moltiplica(int x,int y){
return x*y;
}
50
Stack
int potenza(int a,int b);
int moltiplica(int x,int y);

i=2

j=3

k=6

a=2 b=3 i=1

p=2

int main(){
int i=2,j=3,k;
k=moltiplica(i,j);
k=potenza(i,j);
}
int potenza(int a,int b){
int i, p=1;
for(i=1;i<=b;i++)
p=moltiplica(p,a);
return p;
}
int moltiplica(int x,int y){
return x*y;
}
51
Stack
int potenza(int a,int b);
int moltiplica(int x,int y);

i=2

j=3

k=6

a=2 b=3 i=2

p=2

int main(){
int i=2,j=3,k;
k=moltiplica(i,j);
k=potenza(i,j);
}
int potenza(int a,int b){
int i, p=1;
for(i=1;i<=b;i++)
p=moltiplica(p,a);
return p;
}
int moltiplica(int x,int y){
return x*y;
}
52
Stack
int potenza(int a,int b);
int moltiplica(int x,int y);

i=2

j=3

int main(){
int i=2,j=3,k;
k=moltiplica(i,j);
k=potenza(i,j);
}

k=6

x=2 y=2

a=2 b=3 i=2

p=2

int potenza(int a,int b){
int i, p=1;
for(i=1;i<=b;i++)
p=moltiplica(p,a);
return p;
}
int moltiplica(int x,int y){
return x*y;
}
53
Stack
int potenza(int a,int b);
int moltiplica(int x,int y);

i=2

j=3

k=6

a=2 b=3 i=2

p=4

int main(){
int i=2,j=3,k;
k=moltiplica(i,j);
k=potenza(i,j);
}
int potenza(int a,int b){
int i, p=1;
for(i=1;i<=b;i++)
p=moltiplica(p,a);
return p;
}
int moltiplica(int x,int y){
return x*y;
}
54
Stack
int potenza(int a,int b);
int moltiplica(int x,int y);

i=2

j=3

int main(){
int i=2,j=3,k;
k=moltiplica(i,j);
k=potenza(i,j);
}

k=6

x=4 y=2

a=2 b=3 i=3

p=4

int potenza(int a,int b){
int i, p=1;
for(i=1;i<=b;i++)
p=moltiplica(p,a);
return p;
}
int moltiplica(int x,int y){
return x*y;
}
55
Stack
int potenza(int a,int b);
int moltiplica(int x,int y);

i=2

j=3

k=6

a=2 b=3 i=3

p=8

int main(){
int i=2,j=3,k;
k=moltiplica(i,j);
k=potenza(i,j);
}
int potenza(int a,int b){
int i, p=1;
for(i=1;i<=b;i++)
p=moltiplica(p,a);
return p;
}
int moltiplica(int x,int y){
return x*y;
}
56
Stack
int potenza(int a,int b);
int moltiplica(int x,int y);

i=2

j=3

k=8

int main(){
int i=2,j=3,k;
k=moltiplica(i,j);
k=potenza(i,j);
}
int potenza(int a,int b){
int i, p=1;
for(i=1;i<=b;i++)
p=moltiplica(p,a);
return p;
}
int moltiplica(int x,int y){
return x*y;
}
57
Call stack in Eclipse

Activation record del main

Stato delle variabili

58
Chiamata di funzione

Call stack con funzione chiamata

Stato del chiamato, parametri
passati per copia

59
Passaggio per riferimento

Parametri passato per riferimento

60
Struttura di un programma C++
parte dichiarativa globale

inclusione librerie /∗ per poter richiamare funzioni utili (i/o, ...) ∗/
dichiarazione di variabili e tipi globali e funzioni
int main ( ) {

parte dichiarativa locale

dichiarazione di variabili locali
istruzione 1; /∗ tutti i tipi di operazioni, e cioè: ∗/
istruzione 2;
/∗ istr. di assegnamento ∗/
istruzione 3;
/∗ istr. di input / output ∗/
istruzione 4;
/∗ istr. di controllo (condizionali, cicli) ∗/
/∗ dich. altre variabili locali ∗/
istruzione N;
}
parte esecutiva

61
#include <iostream>
using namespace std;

Esempio mono-file

double potenza (double base, int esp) {
double ris = 1.0;
for (int i = 0; i < esp; ++i)
ris = ris * base;
return ris;
}

definizione
di funzione
chiamata
di funzione

int main() {
float numero;
cout << "Inserire un numero reale come base" << endl;
cin >> numero;
int esponente;
cout << "Inserire un numero reale come base" << endl;
cin >> esponente;
cout << endl << numero << " elevato a " << esponente
<< " vale " << potenza(numero, esponente) << endl;
return 0;
}

Definizione di variabili
locali alla funzione

definizione
di funzione
#include <iostream>
using namespace std;

Esempio mono-file

double potenza (double base, int esp);

dichiarazione
di funzione

int main() {
float numero;
cout << "Inserire un numero reale come base" << endl;
cin >> numero;
int esponente;
cout << "Inserire un numero intero come exp" << endl;
cin >> esponente;
cout << endl << numero << " elevato a " << esponente
<< " vale " << potenza(numero, esponente) << endl;
return 0;
}
double potenza (double base, int esp) {
double ris = 1.0;
for (int i = 0; i < esp; ++i)
ris = ris * base;
return ris;
}

definizione
di funzione
Un errore comune

64
Segnatura (definizione) di una
funzione
• Come per le variabili le funzioni
– devono essere dichiarate e definite prima di essere usate
– possono essere definite una volta e dichiarate più volte
– Una funzione può essere dichiarata in un file di header (.h)
per poi venir inclusa da un altro programma

• La dichiarazione avviene specificando la segnatura
(o interfaccia, prototipo) della funzione (function
header), cioè:
– tipo del risultato
– identificatore del sottoprogramma
– elenco delle dichiarazioni dei parametri formali
double potenza (double base, int esp);
Esempio di programma multifile
/* File contenente funzioni di utilità sui cerchi
* dichiarazione e definizione di una costante
* riusabile : pigreco.cpp */
extern const float pigreco = 3.14159265;
float area(float r){
return r*r*pigreco;
}
float perimeter(float r){
return 2*r*pigreco;
}
File di intestazione della libreria
/*
*
*
*
*

File contenente solo le dichiarazioni delle
funzioni di utilità sui cerchi
e della costante riusabile: pigreco.h
Usato dal compilatore per controllare la
correttezza del programma */

extern const float pigreco;
float area(float r);
float perimeter(float r);
Programma principale
#include <iostream>
#include "pigreco.h"
using namespace std;
int main() {
float radius;
cout << "Insert the value of the radius: " << endl;
cin >> radius;
cout << "Value of pigreco: " << pigreco << endl;
cout << "Perimeter of circle: " << perimeter(radius) <<
cout << "Area of the circle: " << area(radius) << endl;
return 0;
}

endl;
Esempio
• Ristrutturiamo (rifattorizziamo) il
programma di evidenziazione del testo
• Spostiamo in una funzione:
– Le lettura del file
– La stampa del testo
– La stampa degli indici di riga delle occorrenze
– L'evidenziazione del testo

69
Progettazione della
comunicazione
vector<string>
crea

leggitesto

Main
vector<string>
vector<int>
usa
vector<string>
stampatesto

usa

modifica, crea vector<string>
vector<int>

stamparighe

usa
string

vector<int>

evidenzia

70
Passaggio argomenti
• Per non copiare nel parametro del chiamato
argomenti di grandi dimensioni (testo, righe)
usiamo passaggio per riferimento
• Prototipi delle funzioni:
void leggitesto(vector<string> &t);
void stampatesto(vector<string> &t);
void stamparighe(vector<int> &rr);
void evidenzia(vector<string> &t, string &parola,
vector<int> &righe)

71
Passaggio per riferimento
costante
• Laddove
– il chiamato non modifica l'argomento ricevuto dal
chiamante, ma
– L'argomento è di dimensioni non trascurabili

• Meglio usare const &
void stampatesto(const vector<string> &t);
void stamparighe(const vector<int> &rr);
void evidenzia(vector<string> &t, const string &parola,
vector<int> &righe)

• Le funzioni così non possono modificare
l'argomento ricevuto dal chiamante
72
Programma principale
#include <iostream>
#include <vector>
#include <string>
#include <fstream>
using namespace std;
int main() {
vector<string> testo; // il testo vettorializzato, inizialm. vuoto
vector<int> righe;
// l'indice delle righe, inizialmente vuoto
string par;
// la parola da cercare
leggitesto(testo);
// crea tutte le righe all'inizio
cout << "Inserisci parola da ricercare" << endl;
cin >> par;
evidenzia(testo, par, righe);
stampatesto(testo);
stamparighe(righe);
return 0;
}

73
Parametri array
• Gli array hanno una importante differenza
rispetto agli altri tipi primitivi o costruiti:
non è definita la copia né
all'inizializzazione ne all'assegnamento
• Pertanto non si possono passare come
argomenti per copia a una funzione
• Tuttavia si possono lo stesso definire
parametri e argomenti per copia, ma
bisogna capire che cosa succede
74
Parametri di tipo array
void stampa(char[10]);
•Apparentemente la funzione riceve in ingresso
un array di 10 caratteri
•In realtà riceve l'indirizzo (puntatore) al primo
elemento di un array di lunghezza ignota!
•E' equivalente a
void stampa(char[]);
void stampa(char *); // vedremo in seguito
75
Cautele
• Se il parametro denota l'indirizzo del primo
elemento allora di solito NON va
modificato
• Meglio allora renderlo const
void stampa(const char[10]);
void stampa(const char[]);
void stampa(const char *);

76
Passaggio di argomenti di tipo
array
void stampaArray(const char regione[6]){
cout << regione << endl;
}
int main() {
char array1[10]="Lombardia";
char array2[6]="Lazio";
stampaArray(array1);
stampaArray(array2);
return 0;
}

•Le dimensioni del parametro vengono ignorate
•Pericoloso: se manca il terminatore dell'array il
comportamento è indefinito
•Usato spesso comunque, perché apparentemente
"flessibile"
77
Typedef e parametri array
• Usare typdef non cambia
typedef char arraycorto[6];
typedef char arraylungo[10];
void stampaArray(arraycorto regione) {
cout << regione << endl;
}
int main() {
arraylungo array1 = "Lombardia";
arraycorto array2 = "Lazio";
stampaArray(array1);
stampaArray(array2);
return 0;
}
78
Passaggio esplicito delle
dimensioni
• Per ovviare al pericolo di fuoriuscita, si può
aggiungere un parametro esplicito che
rappresenta le dimensioni
– E' l'unica soluzione per array non terminati da '0'

• Difetto: i due parametri sono indipendenti e il
valore delle dimensioni deve essere gestito
con cura dal programmatore
– Se la dimensione passata è maggiore di quella
reale, il comportamento del chiamato è indefinito
79
Stampare un array di interi
const int corto = 2;
const int lungo = 4;
void stampaArray(const int array[], int dim) {
for (int i = 0; i < dim; ++i)
cout << array[i] << endl;
}
int main() {
int arraylungo[lungo]={1,2,3,4};
int arraycorto[corto]={5,6};
stampaArray(arraylungo, lungo);
stampaArray(arraycorto, corto);
return 0;
}
80
Warning
• Non si stampa con cout << un array
che non sia una stringa terminata da '0'
const int lungo = 4;
int main() {
int arraylungo[lungo]={1,2,3,4};
cout << arraylungo; // errato !!
return 0;
}

81
Modificabilità degli array
• Gli array non si possono passare per
copia
• Il chiamato riceve l'indirizzo del primo
elemento dell'array passato come
argomento
• Pertanto il chiamato modifica direttamente
i valori dell'array del chiamante
• Da considerare con cautela
82
Modifica di un parametro array
const int size = 4;
void modificaInt(int i) {
i = i + 100;
}
void modificaArray(int vett[], int dim) {
for (int i = 0; i < dim; i++)
vett[i] += 100;
}

int main() {
int a = 0, v[size] = { 0, 1, 2, 3 };
cout << "Valori prima" << endl;
cout << "a = " << a << endl;
for (int i = 0; i < size; ++i)
cout << "v[" << i << "] = " << v[i] << endl;
cout << "Chiamo le funzioni..." << endl;
modificaInt(a);
modificaArray(v, 4);
cout << "Valori dopo" << endl;
cout << " a = " << a << endl;
for (int i = 0; i < size; ++i)
cout << "v[" << i << "] = " << v[i] << endl;
return 0;
}

83
Passaggio di array
multidimensionali
• Ricordando che una matrice è in realtà un
array di array
f(int [][maxcol], int r, int c);

• Dichiara una funzione che riceve in input
una matrice, cioè maxcol array di interi
• Tutte le dimensioni successive alla prima
fanno parte del tipo e devono essere
fissate
84
Esempio
void stampaMatrice(int mat[][2], int r, int c) {
for (int i = 0; i < r; ++i) {
for (int j = 0; j < c; ++j) // c è cmque fissato a 2
cout << mat[i][j] << " ";
cout << 'n';
}
}
// il secondo indice DEVE valere 2
int main() {
int matrice[2][2] = { 1, 2, 3, 4 };
int matricebis[3][2] = {5,6,7,8,9,10};
stampaMatrice(matrice, 2, 2);
cout << endl;
stampaMatrice(matricebis, 3, 2);
return 0;
}

85
Perché la prima dimensione è
libera?
a[0,0]

a[0,1]

a[0]

a[0,2]

a[1,0]

a[1,1]

a[1]

a[1,2]

a[3,0]

a[3,1]

a[3]

a[3,2]

…

int a [][3]

• Matrice = Array di array di interi
• Ogni valore dell'array, tranne quelli
dell'array "più esterno" deve avere una
dimensione fissa e nota
– 3 celle nel caso presente

86
Forme sintattiche equivalenti

• Sono definite 3 forme sintattiche diverse
per rappresentare una matrice e quindi
anche un parametro di tipo matrice
• Si capirà meglio dopo aver studiato il
costruttore di tipo puntatore (*)
• http://stackoverflow.com/questions/8767166/p

87
Parametri di tipo array e struct
• Un parametro di tipo struct si può passare sia
per riferimento sia per valore (anche se la struct
contiene campi di tipo array!)
• Analogamente, una funzione può restituire una
struct (anche se la struct contiene degli array)

88

More Related Content

What's hot

Kotlin - scope functions and collections
Kotlin - scope functions and collectionsKotlin - scope functions and collections
Kotlin - scope functions and collectionsWei-Shen Lu
 
Best Practices in Qt Quick/QML - Part II
Best Practices in Qt Quick/QML - Part IIBest Practices in Qt Quick/QML - Part II
Best Practices in Qt Quick/QML - Part IIICS
 
Gestione della memoria in C++
Gestione della memoria in C++Gestione della memoria in C++
Gestione della memoria in C++Ilio Catallo
 
Introduction to Coroutines @ KotlinConf 2017
Introduction to Coroutines @ KotlinConf 2017Introduction to Coroutines @ KotlinConf 2017
Introduction to Coroutines @ KotlinConf 2017Roman Elizarov
 
Operator overloading
Operator overloadingOperator overloading
Operator overloadingBurhan Ahmed
 

What's hot (8)

Kotlin - scope functions and collections
Kotlin - scope functions and collectionsKotlin - scope functions and collections
Kotlin - scope functions and collections
 
Best Practices in Qt Quick/QML - Part II
Best Practices in Qt Quick/QML - Part IIBest Practices in Qt Quick/QML - Part II
Best Practices in Qt Quick/QML - Part II
 
Compiti autentici
Compiti autenticiCompiti autentici
Compiti autentici
 
Python list
Python listPython list
Python list
 
Functions in C++
Functions in C++Functions in C++
Functions in C++
 
Gestione della memoria in C++
Gestione della memoria in C++Gestione della memoria in C++
Gestione della memoria in C++
 
Introduction to Coroutines @ KotlinConf 2017
Introduction to Coroutines @ KotlinConf 2017Introduction to Coroutines @ KotlinConf 2017
Introduction to Coroutines @ KotlinConf 2017
 
Operator overloading
Operator overloadingOperator overloading
Operator overloading
 

Similar to 07 1 funzioni

Lezione 12 (28 marzo 2012)
Lezione 12 (28 marzo 2012)Lezione 12 (28 marzo 2012)
Lezione 12 (28 marzo 2012)STELITANO
 
Lezione 12 (28 marzo 2012)
Lezione 12 (28 marzo 2012)Lezione 12 (28 marzo 2012)
Lezione 12 (28 marzo 2012)STELITANO
 
Lezione 5 (7 marzo 2012)
Lezione 5 (7 marzo 2012)Lezione 5 (7 marzo 2012)
Lezione 5 (7 marzo 2012)STELITANO
 
Lezione 16 (2 aprile 2012)
Lezione 16 (2 aprile 2012)Lezione 16 (2 aprile 2012)
Lezione 16 (2 aprile 2012)STELITANO
 
05 3 istruzioni-selezione-iterazione-condizioni
05 3 istruzioni-selezione-iterazione-condizioni05 3 istruzioni-selezione-iterazione-condizioni
05 3 istruzioni-selezione-iterazione-condizioniPiero Fraternali
 
Lezione 11 (26 marzo 2012)
Lezione 11 (26 marzo 2012)Lezione 11 (26 marzo 2012)
Lezione 11 (26 marzo 2012)STELITANO
 
Lezione 11 (26 marzo 2012)
Lezione 11 (26 marzo 2012)Lezione 11 (26 marzo 2012)
Lezione 11 (26 marzo 2012)STELITANO
 
Lezione 21 (2 maggio 2012)
Lezione 21 (2 maggio 2012)Lezione 21 (2 maggio 2012)
Lezione 21 (2 maggio 2012)STELITANO
 
Python - Primi passi
Python - Primi passi Python - Primi passi
Python - Primi passi orestJump
 
08 - Programmazione: Passaggio valori tra funzioni per riferimenti
08 - Programmazione: Passaggio valori tra funzioni per riferimenti08 - Programmazione: Passaggio valori tra funzioni per riferimenti
08 - Programmazione: Passaggio valori tra funzioni per riferimentiMajong DevJfu
 
Esercitazione 1 (27 febbraio 2012)
Esercitazione 1 (27 febbraio 2012)Esercitazione 1 (27 febbraio 2012)
Esercitazione 1 (27 febbraio 2012)STELITANO
 
Laboratorio Programmazione: In - Out variabili
Laboratorio Programmazione: In - Out variabiliLaboratorio Programmazione: In - Out variabili
Laboratorio Programmazione: In - Out variabiliMajong DevJfu
 

Similar to 07 1 funzioni (20)

Lezione 12 (28 marzo 2012)
Lezione 12 (28 marzo 2012)Lezione 12 (28 marzo 2012)
Lezione 12 (28 marzo 2012)
 
Lezione 12 (28 marzo 2012)
Lezione 12 (28 marzo 2012)Lezione 12 (28 marzo 2012)
Lezione 12 (28 marzo 2012)
 
Lezione 5 (7 marzo 2012)
Lezione 5 (7 marzo 2012)Lezione 5 (7 marzo 2012)
Lezione 5 (7 marzo 2012)
 
Lezione 16 (2 aprile 2012)
Lezione 16 (2 aprile 2012)Lezione 16 (2 aprile 2012)
Lezione 16 (2 aprile 2012)
 
Programmazione Top Down in C++
Programmazione Top Down in C++Programmazione Top Down in C++
Programmazione Top Down in C++
 
05 3 istruzioni-selezione-iterazione-condizioni
05 3 istruzioni-selezione-iterazione-condizioni05 3 istruzioni-selezione-iterazione-condizioni
05 3 istruzioni-selezione-iterazione-condizioni
 
La metodologia Top - Down - applicazione al C++
La metodologia Top - Down - applicazione al C++La metodologia Top - Down - applicazione al C++
La metodologia Top - Down - applicazione al C++
 
La scomposizione in sotto programmi in C++.pptx
La scomposizione in sotto programmi in C++.pptxLa scomposizione in sotto programmi in C++.pptx
La scomposizione in sotto programmi in C++.pptx
 
06 2 vector_matrici
06 2 vector_matrici06 2 vector_matrici
06 2 vector_matrici
 
Lezione 11 (26 marzo 2012)
Lezione 11 (26 marzo 2012)Lezione 11 (26 marzo 2012)
Lezione 11 (26 marzo 2012)
 
Lezione 11 (26 marzo 2012)
Lezione 11 (26 marzo 2012)Lezione 11 (26 marzo 2012)
Lezione 11 (26 marzo 2012)
 
Lezione 21 (2 maggio 2012)
Lezione 21 (2 maggio 2012)Lezione 21 (2 maggio 2012)
Lezione 21 (2 maggio 2012)
 
PHP
PHPPHP
PHP
 
Python - Primi passi
Python - Primi passi Python - Primi passi
Python - Primi passi
 
7 Sottoprogrammi
7   Sottoprogrammi7   Sottoprogrammi
7 Sottoprogrammi
 
05 1 intro-struttura
05 1 intro-struttura05 1 intro-struttura
05 1 intro-struttura
 
Sql stored procedures
Sql stored proceduresSql stored procedures
Sql stored procedures
 
08 - Programmazione: Passaggio valori tra funzioni per riferimenti
08 - Programmazione: Passaggio valori tra funzioni per riferimenti08 - Programmazione: Passaggio valori tra funzioni per riferimenti
08 - Programmazione: Passaggio valori tra funzioni per riferimenti
 
Esercitazione 1 (27 febbraio 2012)
Esercitazione 1 (27 febbraio 2012)Esercitazione 1 (27 febbraio 2012)
Esercitazione 1 (27 febbraio 2012)
 
Laboratorio Programmazione: In - Out variabili
Laboratorio Programmazione: In - Out variabiliLaboratorio Programmazione: In - Out variabili
Laboratorio Programmazione: In - Out variabili
 

More from Piero Fraternali

Multimedia on the mountaintop: presentation at ACM MM2016
Multimedia on the mountaintop: presentation at ACM MM2016Multimedia on the mountaintop: presentation at ACM MM2016
Multimedia on the mountaintop: presentation at ACM MM2016Piero Fraternali
 
presentation at European Utility Week, Vienna, Nov. 2015
presentation at European Utility Week, Vienna, Nov. 2015presentation at European Utility Week, Vienna, Nov. 2015
presentation at European Utility Week, Vienna, Nov. 2015Piero Fraternali
 
Fraternali concertation june25bruxelles
Fraternali concertation june25bruxellesFraternali concertation june25bruxelles
Fraternali concertation june25bruxellesPiero Fraternali
 
Crowsourcing (anche) per le aziende del settore tessile e della moda
Crowsourcing (anche) per le aziende del settore tessile e della modaCrowsourcing (anche) per le aziende del settore tessile e della moda
Crowsourcing (anche) per le aziende del settore tessile e della modaPiero Fraternali
 
06 1 array_stringhe_typedef
06 1 array_stringhe_typedef06 1 array_stringhe_typedef
06 1 array_stringhe_typedefPiero Fraternali
 
05 2 integrali-conversioni-costanti-preproc-inclusione
05 2 integrali-conversioni-costanti-preproc-inclusione05 2 integrali-conversioni-costanti-preproc-inclusione
05 2 integrali-conversioni-costanti-preproc-inclusionePiero Fraternali
 
Human computation and participatory systems
Human computation and participatory systems Human computation and participatory systems
Human computation and participatory systems Piero Fraternali
 
Better society: Meet us at #ICT2013eu for #trustedsocialmedia http://bit.ly/1...
Better society: Meet us at #ICT2013eu for #trustedsocialmedia http://bit.ly/1...Better society: Meet us at #ICT2013eu for #trustedsocialmedia http://bit.ly/1...
Better society: Meet us at #ICT2013eu for #trustedsocialmedia http://bit.ly/1...Piero Fraternali
 
Human and social computation
Human and social computation Human and social computation
Human and social computation Piero Fraternali
 
Web technologies: Model Driven Engineering
Web technologies: Model Driven EngineeringWeb technologies: Model Driven Engineering
Web technologies: Model Driven EngineeringPiero Fraternali
 
Web technologies: recap on TCP-IP
Web technologies: recap on TCP-IPWeb technologies: recap on TCP-IP
Web technologies: recap on TCP-IPPiero Fraternali
 
Web technologies course, an introduction
Web technologies course, an introductionWeb technologies course, an introduction
Web technologies course, an introductionPiero Fraternali
 

More from Piero Fraternali (20)

Multimedia on the mountaintop: presentation at ACM MM2016
Multimedia on the mountaintop: presentation at ACM MM2016Multimedia on the mountaintop: presentation at ACM MM2016
Multimedia on the mountaintop: presentation at ACM MM2016
 
presentation at European Utility Week, Vienna, Nov. 2015
presentation at European Utility Week, Vienna, Nov. 2015presentation at European Utility Week, Vienna, Nov. 2015
presentation at European Utility Week, Vienna, Nov. 2015
 
Fraternali concertation june25bruxelles
Fraternali concertation june25bruxellesFraternali concertation june25bruxelles
Fraternali concertation june25bruxelles
 
Crowsourcing (anche) per le aziende del settore tessile e della moda
Crowsourcing (anche) per le aziende del settore tessile e della modaCrowsourcing (anche) per le aziende del settore tessile e della moda
Crowsourcing (anche) per le aziende del settore tessile e della moda
 
07 2 ricorsione
07 2 ricorsione07 2 ricorsione
07 2 ricorsione
 
06 1 array_stringhe_typedef
06 1 array_stringhe_typedef06 1 array_stringhe_typedef
06 1 array_stringhe_typedef
 
05 2 integrali-conversioni-costanti-preproc-inclusione
05 2 integrali-conversioni-costanti-preproc-inclusione05 2 integrali-conversioni-costanti-preproc-inclusione
05 2 integrali-conversioni-costanti-preproc-inclusione
 
03 2 arit_bin
03 2 arit_bin03 2 arit_bin
03 2 arit_bin
 
03 1 alg_bool
03 1 alg_bool03 1 alg_bool
03 1 alg_bool
 
02 algo programmi
02 algo programmi02 algo programmi
02 algo programmi
 
06 3 struct
06 3 struct06 3 struct
06 3 struct
 
Human computation and participatory systems
Human computation and participatory systems Human computation and participatory systems
Human computation and participatory systems
 
Better society: Meet us at #ICT2013eu for #trustedsocialmedia http://bit.ly/1...
Better society: Meet us at #ICT2013eu for #trustedsocialmedia http://bit.ly/1...Better society: Meet us at #ICT2013eu for #trustedsocialmedia http://bit.ly/1...
Better society: Meet us at #ICT2013eu for #trustedsocialmedia http://bit.ly/1...
 
So human presentation
So human presentationSo human presentation
So human presentation
 
Human and social computation
Human and social computation Human and social computation
Human and social computation
 
Web technologies: Model Driven Engineering
Web technologies: Model Driven EngineeringWeb technologies: Model Driven Engineering
Web technologies: Model Driven Engineering
 
Common Gateway Interface
Common Gateway InterfaceCommon Gateway Interface
Common Gateway Interface
 
Web technologies: HTTP
Web technologies: HTTPWeb technologies: HTTP
Web technologies: HTTP
 
Web technologies: recap on TCP-IP
Web technologies: recap on TCP-IPWeb technologies: recap on TCP-IP
Web technologies: recap on TCP-IP
 
Web technologies course, an introduction
Web technologies course, an introductionWeb technologies course, an introduction
Web technologies course, an introduction
 

07 1 funzioni

  • 1. Fondamenti di informatica 1 Sottoprogrammi (Funzioni) 1
  • 2. Problema • Abbiamo visto sequenze di istruzioni che risolvono particolari “sotto-problemi”: controllo della primalità di un numero, confronto di stringhe, etc. • È possibile riutilizzare queste sequenze? • È possibile consentire ad altri di riutilizzare queste sequenze? • È possibile dare a queste sequenze un nome che ne indichi la funzionalità? • È possibile “svincolare” lo sviluppo di soluzioni a sotto-problemi di questo tipo dallo sviluppo di una soluzione “complessa”?
  • 3. Se fosse possibile… • …fare queste cose, i vantaggi sarebbero molti: • Potendo riutilizzare facilmente le sequenze, aumenterebbe la produttività (potremmo scrivere più codice in meno tempo) • Potremmo consentire ad altri di aumentare la loro produttività • Potremmo rendere il codice più leggibile • Potremmo “spezzare” la risoluzione di un problema in sotto-problemi
  • 4. Le funzioni • I principali linguaggi di programmazione offrono, in risposta a queste esigenze, lo strumento delle procedure o funzioni • In C e C++, una funzione è una sequenza di comandi che: – ha un nome – può essere invocata (cioè può esserne richiesta l’esecuzione) – può ricevere dei valori di parametri che ne influenzano l’esecuzione – può produrre un valore risultato 4
  • 5. Motivazioni • Modularità nello sviluppo del codice – Affrontare il problema per raffinamenti successivi • Riusabilità – Scrivere una sola volta il codice e usarlo più volte – Esempi: • un algoritmo di ordinamento • Le operazioni del calcolo matriciale • La ricerca di una parola in un testo • Astrazione – Esprimere in modo sintetico operazioni complesse – Definire operazioni specifiche dei tipi di dato definiti dal programmatore – Esempi: • Calcolo “totale + iva” di un ordine • Lettura scrittura transazioni di vendita dei libri • Operazioni con entità quali punti, segmenti, poligoni, numeri complessi
  • 6. Esempio: gestione del dialogo con l'utente int main() { char scelta; int valore, risultato; while (true) { // menu, loop infinito cout << "Premere A per inserire un num tra 0 e 10 e calcolarne il cubo" << endl; cout << "Premere B per inserire un num tra 11 e 20 e calcolarne il quadrato" << endl; cout << "Premere C per inserire un num tra 21 e 30 e calcolarne il doppio" << endl; cout << "Premere Q per uscire" << endl; cin >> scelta; switch (scelta) { case 'a': case 'A': cout << "Inserisci valore" << endl; cin >> valore; risultato = valore * valore * valore; cout << "risultato = " << risultato << endl; break; 6
  • 7. … continua case 'b': case 'B': cout << "Inserisci valore" << endl; cin >> valore; risultato = valore * valore; cout << "risultato = " << risultato << endl; break; case 'c': case 'C': cout << "Inserisci valore" << endl; cin >> valore; risultato = valore * 2; cout << "risultato = " << risultato << endl; break; case 'q': case 'Q': cout << "GRAZIE E ARRIVEDERCI"; return 0; // termina il ciclo infinito } } } 7
  • 8. Fattorizzazione del dialogo char menu() { char ch; cout << "Premere cout << "Premere cout << "Premere cout << "Premere cin >> ch; return ch; } A B C Q per per per per inserire un num tra 0 e 10 e calcolarne il cubo" << endl; inserire un num tra 11 e 20 e calcolarne il quadrato" << endl; inserire un num tra 21 e 30 e calcolarne il doppio" << endl; uscire" << endl; int leggi() { int v; cout << "Inserisci valore" << endl; cin >> v; return v; } 8
  • 9. Programma principale int main() { char scelta; int valore, risultato; while (true) { // menu, loop infinito scelta = menu(); switch (scelta) { case 'a': case 'A': valore = leggi(); risultato = valore * valore * valore; cout << "risultato = " << risultato << endl; break; case 'b': case 'B': valore = leggi(); risultato = valore * valore; cout << "risultato = " << risultato << endl; break; case 'c': case 'C': valore = leggi(); risultato = valore * 2; cout << "risultato = " << risultato << endl; break; case 'q': case 'Q': cout << "GRAZIE E ARRIVEDERCI"; return 0; } } } 9
  • 10. Osservazioni • La funzione main() invoca le funzioni menu() e leggi() – main(): funzione chiamante – menu() e leggi(): funzioni chiamate • La comunicazione chiamante-chiamato nel caso visto avviene tramite il valore di ritorno char menu() {.. return ch; } int main () {… .. scelta = menu(); • Il chiamante copia in una propria variabile locale il valore prodotto dall'invocazione della funzione 10
  • 11. Definizione delle funzioni <tipo restituito> <nome della funzione> (<lista parametri>) { } •Tipo restituito: il tipo del valore di ritorno della funzione – Non può essere un tipo array •Nome: un identificatore •Lista parametri: una lista di coppie <tipo-nomeParametro> separate da virgole – Si possono usare 0 o più parametri •{ . . . }: corpo della funzione
  • 12. Definizione di una funzione int leggi(int min, int max) { int v; bool ok = false; while (!ok) { cout << "Inserisci valore" << endl; cin >> v; if (v >= min && v <= max) ok = true; } return v; } 12
  • 13. Chiamata di una funzione int main() { char scelta; int valore, risultato; while (true) { // menu, loop infinito scelta = menu(); switch (scelta) { case 'a': case 'A': valore = leggi(0,10); risultato = valore * valore * valore; cout << "risultato = " << risultato << endl; break; •Leggi(0,10) è un'espressione di chiamata a funzione •0 e 10 sono gli argomenti della chiamata •Valore dell'espressione è il valore restituito dalla funzione •Il tipo dell'espressione è il tipo di ritorno delle funzione 13
  • 14. Esempio • Definiamo il fattoriale come una funzione int fatt(int val) { int ris = 1; while (val > 1) ris *= val--; //assegna ris * val a ris e decrementa val return ris; } int main() { int numero; cout << "Inserire un numero positivo" << endl; cin >> numero; cout << "Il fattoriale di " << numero << " vale " << fatt(numero) << endl; return 0; }
  • 15. Sintassi dell’invocazione • L'invocazione di una funzione è un’espressione – es: possiamo immaginare una funzione somma(a,b) equivalente ad a+b • Quando, nell’esecuzione del codice, è necessario ‘valutare’ un’espressione-funzione, la funzione viene invocata • La sintassi per la formulazione di una espressione-funzione è semplicemente: <nome funzione>(<lista argomenti>) – dove <lista argomenti> è una lista di espressioni di tipo corrispondente ai parametri della definizione della funzione • Corrispondenza posizionale – al primo parametro corrisponde il primo argomento, al secondo il secondo, e così via ...
  • 16. Semantica dell’invocazione • Quando una funzione viene invocata: – Le espressioni corrispondenti agli argomenti vengono valutate, e il valore reso disponibile ("passato") alla funzione – I valori passati alla funzione sono utilizzati per inizializzare i parametri della funzione – Il controllo passa da chiamante alla funzione chiamata, – Questa viene eseguita, fino a quando non viene incontrato un comando return (o il termine del blocco principale }) – Il “valore” dell’espressione-funzione corrispondente alla chiamata è il valore dell'espressione che appare nell'istruzione return che ha causato la terminazione • NB: oppure nessun valore se la funzione ha void come tipo di ritorno
  • 17. Comunicazione chiamantechiamato • L'uso del valore di ritorno non è sufficiente • Può essere necessario: – Che il chiamante comunichi dei valori al chiamato – Che il chiamato restituisca più di un valore al chiamante – Che il chiamato modifichi direttamente un valore del chiamante • Ad esempio, la funzione leggi() deve ricevere dal chiamante i limiti inferiore e superiore del valore da chiedere all'utente 17
  • 18. Modalità di passaggio degli argomenti • Chiamante e chiamato devono comunicare • Ci sono diverse esigenze a cui corrispondono diversi meccanismi di comunicazione – Il chiamante passa informazione al chiamato • Il chiamante passa argomenti al chiamato • Il chiamato opera su una variabile globale visibile al chiamante – Il chiamante riceve informazione dal chiamato • Il chiamato restituisce un valore • Il chiamato modifica una variabile del chiamante • Il chiamato opera su una variabile globale visibile al chiamante • Ulteriore esigenza: – Evitare di copiare argomenti di grandi dimensioni nei parametri del chiamato, se non necessario 18
  • 19. Argomenti e parametri • Gli argomenti fungono da inizializzatori dei parametri di una funzione • Notazione posizionale: – Argomento1  parametro1, argomento2 parametro2 – Tutti gli argomenti richiesti devono essere forniti – Il tipo di un argomento deve corrispondere a quello del parametro, secondo le stesse regole di inizializzazione delle variabili • Nomenclatura alternativa: – Parametri  Parametri formali – Argomenti Parametri attuali 19
  • 20. Passaggio per valore • Call by value • Funziona come nell'inizializzazione di una variabile – Il valore dell'argomento (inizializer) viene copiato nel parametro – Modifiche del parametro NON cambiano l'argomento void callByValue(int i) { i = i * 2; // NON modifica l'argomento passato } 20
  • 21. Passaggio per riferimento • Call by reference • I parametri sono di ripo reference (&) – Il valore dell'argomento (inizializer) è un sinonimo del parametro – Modifiche del parametro CAMBIANO l'argomento del chiamante – L'argomento deve essere una variabile, non una costante void callByRef(int &i) { i = i * 2; // modifica l'argomento passato del chiamante } 21
  • 22. Passaggio per riferimento costante • Call by const reference • I parametri sono di ripo const reference (const &) – Il valore dell'argomento (inizializer) è un sinonimo del parametro – Il costruttore const IMPEDISCE modifiche del parametro, come nel passaggio per copia – Come nel passaggio per riferimento si evita di copiare il valore del parametro – L'argomento deve essere una variabile, non una costante void callByConstRef(const int &i) { i = i * 2; // errore: non si può modifica l'argomento } 22
  • 23. Esempio void callByValue(int i) { i = i * 2; } void callByRef(int &i) { i = i * 2; } int main() { int j=1; callByValue(j); cout << "After call by value j's value is " << j << endl; callByRef(j); cout << "After call by reference j's value is " << j << endl; return 0; } 23
  • 24. Altro esempio void swapByValue(int p, int q) { int temp; temp = p; p = q; q = temp; } void swapByRef(int &p, int &q) { int temp; temp = p; p = q; q = temp; } int main() { int a = 1, b = 2; cout << "Valori iniziali: " << a << ' ' << b << endl; swapByValue(a, b); cout << "Valori dopo swapByValue: " << a << ' ' << b << endl; swapByRef(a, b); cout << "Valori dopo swapByRef: " << a << ' ' << b << endl; return 0; } 24
  • 25. Funzioni vs procedure • Esistono sottoprogrammi che restituiscono valori (funzioni) e sottoprogrammi che non lo fanno (procedure) • In C++ e C è possibile scrivere un sottoprogramma che non restituisce un valore, ma produce solo un effetto sullo stato del programma • Tale fatto è segnalato dal tipo di ritorno void void error_line (int line) { cout << "Errore alla linea" << line << endl; } • Il sottoprogramma viene chiamato come segue: error_line (line_number); • La terminazione della funzione e il ritorno del controllo al chiamante avvengono a fine blocco } 25
  • 26. Intercambiabilità di procedure e funzioni • Funzione: • Procedura: int f (int par1) { /* calcola valore di variabile “risultato” */ return (risultato); } void f (int par1, int & par2) { /* calcola valore di variabile “risultato” */ par2 = risultato; } • chiamata: y = f(x); • chiamata: f (x, y); • y del chiamante riceve il valore di par2 del chiamato
  • 27. Raccomandazioni di stile • Per le funzioni: – passare i parametri per copia – usare passaggio per riferimento per parametri di grandi dimensione, per evitare la copia – evitare l'abuso di variabili non locali, che rendono difficile la comprensione dei rapporti tra chiamante e chiamato • Una funzione che abbia parametri passati per reference e ne modifichi il valore, si dice avere un effetto collaterale (side effect) • Benché ammissibili, gli effetti collaterali si devono usare con prudenza, e in generale sono sconsigliabili quando non strettamente necessari 27
  • 28. Parametri: pro e contro Modi di passaggio per valore ("per copia") per riferimento per riferimento costante Caratteristiche Tempo e spazio necessari a trasferire (copiare) i dati grandi (per parametri di grandi dimensioni) piccoli (si copia un indirizzo, la cui dimensione non dipende dai dati) piccoli (si evita la copia, si definisce un sinonimo dell'argomento) C'è rischio di effetti collaterali indesiderati? No (i parametri attuale e formale sono distinti) Sì (i parametri attuale e formale "di fatto" coincidono) Sì (i parametri attuale e formale sono sinonimi) Permette la restituzione di valori al chiamante? No Sì Sì 28
  • 29. Esempio • Funzione per ricercare un carattere in una stringa e restituire la prima posizione in cui appare e il numero di occorrenze • Progettazione della comunicazione – Input: • La stringa: per riferimento costante • Il carattere da cercare: per copia – Output : • La posizione: valore restituito (o parametro per riferimento) • Numero di occorrenze: parametro per riferimento 29
  • 30. Funzione string::size_type find_char(const string &s, char c, string::size_type &occurs) { auto ret = s.size(); // position of the first occurrence, if any occurs = 0; // set the occurrence count parameter for (decltype(ret) i = 0; i != s.size(); ++i) { if (s[i] == c) { if (ret == s.size()) ret = i; // remember first occurrence of c ++occurs; // increment the occurrence count } } return ret; // count is returned implicitly in occurs } 30
  • 31. Main #include <iostream> #include <string> using namespace std; int main() { string someString; string::size_type count; cout << "Insert the string to search" << endl; getline(cin, someString); char c; cout << "Insert the char to search" << endl; cin >> c; auto index = find_char(someString, c, count); if (index < someString.size()) { cout << "Character " << c << " first occurs at pos: " << index << endl; cout << "Number of occurrences is: " << count << endl; } else cout << "Character not found" << endl; return 0; 31 }
  • 32. Utilità di void e terminazione senza valore • http://stackoverflow.com/questions/11560068/ • http://stackoverflow.com/questions/1610030/w 32
  • 33. Visibilità e durata • Tutte le variabili di un programma hanno una visibilità (scope) e una durata (lifetime) – Scope: porzione del programma in cui il nome è visibile – Lifetime: tempo durante l'esecuzione in cui la variabile esiste • Il corpo della funzione definisce un ambito di visibilità (scope) per cui – Le variabili dichiarate nella funzione e i parametri sono locali alla funzione – Variabili locali e parametri della stessa funzione devono avere nomi diversi – Variabili locali e parametri di funzioni diverse possono avere nomi coincidenti – I nomi locali mascherano eventuali nomi uguali definiti in scope più esterni (nomi di variabili globali) 33
  • 34. Il blocco • E' un delimitatore esplicito sia di scope sia di lifetime • Può comparire ovunque la sintassi preveda un'istruzione • Si compone di due parti racchiuse tra { } – una sequenza di istruzioni – può anche contenere dichiarazioni • Due blocchi possono essere: – annidati (uno è interno all’altro) – paralleli (entrambi interni a un terzo blocco, ma non annidati tra loro)
  • 35. int g1, g2; char g3; int f1(int par1, int int f2(int par3, int par2); // Prototipo di f1 par1); // Prototipo di f2 main () { int a, b; { char a, c; ... {float a; ... } // Fine blocco2 } // Fine blocco1 } // Fine main int f1(int par1, intd; { int e; ... } // Fine { int d; ... } // Fine } // Fine int } int modello “a contorni” par2) { blocco3 par1, par2, blocco4 f1 f2(int par3, int int f; ... // Fine f2 par4) { par3, par4,
  • 36. Mascheramento della visibilità • La dichiarazione di un elemento in una funzione o in un blocco maschera le eventuali entità omonime più “esterne” g1,g2,g3 livello globale a,b main a,c blocco1 a,d blocco2 d blocco3 f1 È possibile (ma sconsigliato) riusare nomi di variabili
  • 37. Denominare le variabili locali alle funzioni #include <iostream> using namespace std; double potenza (double base, int esp) { double ris = 1.0; for (int i = 0; i < esp; ++i) ris = ris * base; return ris; } Le variabili locali e i parametri di funzioni diverse appartengono a "scope" distinti. Per cui possono anche avere lo stesso nome, senza pericolo di confusione. Meglio però evitare tale pratica int main() { float base; cout << "Inserire un numero reale come base" << endl; cin >> base; int esp; cout << "Inserire un numero reale come base" << endl; cin >> esp; cout << endl << base << " elevato a " << esp << " vale " << potenza(base, esp) << endl; return 0; }
  • 38. Durata delle variabili (lifetime) • Va dalla creazione (allocazione della memoria) alla distruzione (rilascio della memoria allocata) • Due classi di variabili – Statiche: • Allocate una volta per tutte • Distrutte solo al termine dell’esecuzione del programma • Sono tutte le variabili globali e quelle locali al main() – Anche le variabili di funzione o blocco dichiarabili static • Possono fungere da ulteriore canale di comunicazione tra funzioni – Automatiche: • • • • • Sono quelle dichiarate nelle funzioni (inclusi i parametri) e nei blocchi Sono create quando il flusso di esecuzione "entra" nel loro ambito di visibilità Sono distrutte all’uscita da tale ambito Sono allocate ad ogni esecusione in celle di memoria differenti Non conservano i valori prodotti da precedenti esecuzioni della funzione o del blocco
  • 39. Oggetti locali statici • • Esigenza: memorizzare informazione locale a una funzione ( o a un blocco) che permane per tutta l'esecuzione del programma (e non solo per una singola chiamata) Meccanismo: variabili locali static int conta_chiamate() { static int ctr = 0; // persiste dopo la chiamata return ++ctr; } int main() { for (int i = 0; i != 10; ++i) cout << conta_chiamate() << endl; return 0; } • Stampa numeri da 1 a 10 incluso 39
  • 40. Modello di esecuzione • In seguito a una chiamata a sottoprogramma, il programma in corso viene sospeso e il controllo passa al sottoprogramma • Al livello dell'ambiente di esecuzione: – Salvataggio dell'indirizzo della prossima istruzione da eseguire (program counter, PC) e del contesto (valori di variabili) del programma chiamante – Assegnazione al PC dell’indirizzo del sottoprogramma – Esecuzione del sottoprogramma – Ritorno al programma chiamante con ripristino del suo contesto 40
  • 41. Record di attivazione • Ogni sottoprogramma (incluso il main) ha associato un record di attivazione. Contiene: – tutti i dati relativi all’ambiente locale del sottoprogramma – l’indirizzo di ritorno nel programma chiamante (serve per poter eseguire l'istruzione del chiamante immediatamente successiva alla invocazione del sotto-programma) • Per ogni attivazione di sottoprogramma si crea un nuovo record di attivazione 41
  • 42. Perché è necessario? • In generale, una funzione può essere chiamata un numero imprecisato di volte • Ogni chiamata a procedura richiede allocazione di spazio di memoria per le sue variabili locali – Il compilatore potrebbe “preparare” un ambiente per ogni funzione definita? In generale no... • Vi sono procedure che richiamano se stesse (vedremo: ricorsione) – Possono esistere più “istanze” di una funzione, “addormentate” in attesa della terminazione di una “gemella” per riprendere l’esecuzione • In questo ultimo caso il compilatore non può sapere quanto spazio allocare per le variabili del programma (nei vari ambienti) 42
  • 43. La pila (stack) di sistema • Una porzione della memoria di lavoro, chiamata stack (pila): modalità LIFO (Last in First Out) permette al sistema operativo di gestire i processi e di eseguire le chiamate a sottoprogramma • Lo Stack Pointer (puntat. alla pila) è un registro che contiene l’indirizzo della parola di memoria da leggere nello stack 312 311 310 • Operazione di inserimento: - incremento SP ... - scrittura nella parola indirizzata da SP • Operazione di estrazione: - lettura da parola indirizzata da SP 303 - decremento SP SP 43 312 Stack pointer = 312
  • 44. Esempio main() ambiente globale Chiama f1 crescita verso l'alto ... r.d.a. di f1(3°) r.d.a. di f2(2°) f2() f1() r.d.a. di f1(2°) r.d.a. di f2(1°) Chiama f2 Chiama f1 r.d.a. di f1(1°) r.d.a. di main() variabili globali 44
  • 45. Esempio di esecuzione int potenza(int a,int b); int moltiplica(int x,int y); int main(){ int i=2,j=3,k; k=moltiplica(i,j); k=potenza(i,j); } int potenza(int a,int b){ int i, p=1; for(i=1;i<=b;i++) p=moltiplica(p,a); return p; } int moltiplica(int x,int y){ return x*y; } i=2 j=3 k
  • 46. Stack int potenza(int a,int b); int moltiplica(int x,int y); int main(){ int i=2,j=3,k; k=moltiplica(i,j); k=potenza(i,j); } int potenza(int a,int b){ int i, p=1; for(i=1;i<=b;i++) p=moltiplica(p,a); return p; } int moltiplica(int x,int y){ return x*y; } i=2 j=3 x=2 y=3 k
  • 47. Stack int potenza(int a,int b); int moltiplica(int x,int y); i=2 j=3 k=6 int main(){ int i=2,j=3,k; k=moltiplica(i,j); k=potenza(i,j); } int potenza(int a,int b){ int i, p=1; for(i=1;i<=b;i++) p=moltiplica(p,a); return p; } int moltiplica(int x,int y){ return x*y; } 47
  • 48. Stack int potenza(int a,int b); int moltiplica(int x,int y); i=2 j=3 k=6 a=2 b=3 i p=1 int main(){ int i=2,j=3,k; k=moltiplica(i,j); k=potenza(i,j); } int potenza(int a,int b){ int i, p=1; for(i=1;i<=b;i++) p=moltiplica(p,a); return p; } int moltiplica(int x,int y){ return x*y; } 48
  • 49. Stack int potenza(int a,int b); int moltiplica(int x,int y); i=2 j=3 k=6 a=2 b=3 i=1 p=1 int main(){ int i=2,j=3,k; k=moltiplica(i,j); k=potenza(i,j); } int potenza(int a,int b){ int i, p=1; for(i=1;i<=b;i++) p=moltiplica(p,a); return p; } int moltiplica(int x,int y){ return x*y; } 49
  • 50. Stack int potenza(int a,int b); int moltiplica(int x,int y); i=2 j=3 int main(){ int i=2,j=3,k; k=moltiplica(i,j); k=potenza(i,j); } k=6 x=1 y=2 a=2 b=3 i=1 p=1 int potenza(int a,int b){ int i, p=1; for(i=1;i<=b;i++) p=moltiplica(p,a); return p; } int moltiplica(int x,int y){ return x*y; } 50
  • 51. Stack int potenza(int a,int b); int moltiplica(int x,int y); i=2 j=3 k=6 a=2 b=3 i=1 p=2 int main(){ int i=2,j=3,k; k=moltiplica(i,j); k=potenza(i,j); } int potenza(int a,int b){ int i, p=1; for(i=1;i<=b;i++) p=moltiplica(p,a); return p; } int moltiplica(int x,int y){ return x*y; } 51
  • 52. Stack int potenza(int a,int b); int moltiplica(int x,int y); i=2 j=3 k=6 a=2 b=3 i=2 p=2 int main(){ int i=2,j=3,k; k=moltiplica(i,j); k=potenza(i,j); } int potenza(int a,int b){ int i, p=1; for(i=1;i<=b;i++) p=moltiplica(p,a); return p; } int moltiplica(int x,int y){ return x*y; } 52
  • 53. Stack int potenza(int a,int b); int moltiplica(int x,int y); i=2 j=3 int main(){ int i=2,j=3,k; k=moltiplica(i,j); k=potenza(i,j); } k=6 x=2 y=2 a=2 b=3 i=2 p=2 int potenza(int a,int b){ int i, p=1; for(i=1;i<=b;i++) p=moltiplica(p,a); return p; } int moltiplica(int x,int y){ return x*y; } 53
  • 54. Stack int potenza(int a,int b); int moltiplica(int x,int y); i=2 j=3 k=6 a=2 b=3 i=2 p=4 int main(){ int i=2,j=3,k; k=moltiplica(i,j); k=potenza(i,j); } int potenza(int a,int b){ int i, p=1; for(i=1;i<=b;i++) p=moltiplica(p,a); return p; } int moltiplica(int x,int y){ return x*y; } 54
  • 55. Stack int potenza(int a,int b); int moltiplica(int x,int y); i=2 j=3 int main(){ int i=2,j=3,k; k=moltiplica(i,j); k=potenza(i,j); } k=6 x=4 y=2 a=2 b=3 i=3 p=4 int potenza(int a,int b){ int i, p=1; for(i=1;i<=b;i++) p=moltiplica(p,a); return p; } int moltiplica(int x,int y){ return x*y; } 55
  • 56. Stack int potenza(int a,int b); int moltiplica(int x,int y); i=2 j=3 k=6 a=2 b=3 i=3 p=8 int main(){ int i=2,j=3,k; k=moltiplica(i,j); k=potenza(i,j); } int potenza(int a,int b){ int i, p=1; for(i=1;i<=b;i++) p=moltiplica(p,a); return p; } int moltiplica(int x,int y){ return x*y; } 56
  • 57. Stack int potenza(int a,int b); int moltiplica(int x,int y); i=2 j=3 k=8 int main(){ int i=2,j=3,k; k=moltiplica(i,j); k=potenza(i,j); } int potenza(int a,int b){ int i, p=1; for(i=1;i<=b;i++) p=moltiplica(p,a); return p; } int moltiplica(int x,int y){ return x*y; } 57
  • 58. Call stack in Eclipse Activation record del main Stato delle variabili 58
  • 59. Chiamata di funzione Call stack con funzione chiamata Stato del chiamato, parametri passati per copia 59
  • 60. Passaggio per riferimento Parametri passato per riferimento 60
  • 61. Struttura di un programma C++ parte dichiarativa globale inclusione librerie /∗ per poter richiamare funzioni utili (i/o, ...) ∗/ dichiarazione di variabili e tipi globali e funzioni int main ( ) { parte dichiarativa locale dichiarazione di variabili locali istruzione 1; /∗ tutti i tipi di operazioni, e cioè: ∗/ istruzione 2; /∗ istr. di assegnamento ∗/ istruzione 3; /∗ istr. di input / output ∗/ istruzione 4; /∗ istr. di controllo (condizionali, cicli) ∗/ /∗ dich. altre variabili locali ∗/ istruzione N; } parte esecutiva 61
  • 62. #include <iostream> using namespace std; Esempio mono-file double potenza (double base, int esp) { double ris = 1.0; for (int i = 0; i < esp; ++i) ris = ris * base; return ris; } definizione di funzione chiamata di funzione int main() { float numero; cout << "Inserire un numero reale come base" << endl; cin >> numero; int esponente; cout << "Inserire un numero reale come base" << endl; cin >> esponente; cout << endl << numero << " elevato a " << esponente << " vale " << potenza(numero, esponente) << endl; return 0; } Definizione di variabili locali alla funzione definizione di funzione
  • 63. #include <iostream> using namespace std; Esempio mono-file double potenza (double base, int esp); dichiarazione di funzione int main() { float numero; cout << "Inserire un numero reale come base" << endl; cin >> numero; int esponente; cout << "Inserire un numero intero come exp" << endl; cin >> esponente; cout << endl << numero << " elevato a " << esponente << " vale " << potenza(numero, esponente) << endl; return 0; } double potenza (double base, int esp) { double ris = 1.0; for (int i = 0; i < esp; ++i) ris = ris * base; return ris; } definizione di funzione
  • 65. Segnatura (definizione) di una funzione • Come per le variabili le funzioni – devono essere dichiarate e definite prima di essere usate – possono essere definite una volta e dichiarate più volte – Una funzione può essere dichiarata in un file di header (.h) per poi venir inclusa da un altro programma • La dichiarazione avviene specificando la segnatura (o interfaccia, prototipo) della funzione (function header), cioè: – tipo del risultato – identificatore del sottoprogramma – elenco delle dichiarazioni dei parametri formali double potenza (double base, int esp);
  • 66. Esempio di programma multifile /* File contenente funzioni di utilità sui cerchi * dichiarazione e definizione di una costante * riusabile : pigreco.cpp */ extern const float pigreco = 3.14159265; float area(float r){ return r*r*pigreco; } float perimeter(float r){ return 2*r*pigreco; }
  • 67. File di intestazione della libreria /* * * * * File contenente solo le dichiarazioni delle funzioni di utilità sui cerchi e della costante riusabile: pigreco.h Usato dal compilatore per controllare la correttezza del programma */ extern const float pigreco; float area(float r); float perimeter(float r);
  • 68. Programma principale #include <iostream> #include "pigreco.h" using namespace std; int main() { float radius; cout << "Insert the value of the radius: " << endl; cin >> radius; cout << "Value of pigreco: " << pigreco << endl; cout << "Perimeter of circle: " << perimeter(radius) << cout << "Area of the circle: " << area(radius) << endl; return 0; } endl;
  • 69. Esempio • Ristrutturiamo (rifattorizziamo) il programma di evidenziazione del testo • Spostiamo in una funzione: – Le lettura del file – La stampa del testo – La stampa degli indici di riga delle occorrenze – L'evidenziazione del testo 69
  • 71. Passaggio argomenti • Per non copiare nel parametro del chiamato argomenti di grandi dimensioni (testo, righe) usiamo passaggio per riferimento • Prototipi delle funzioni: void leggitesto(vector<string> &t); void stampatesto(vector<string> &t); void stamparighe(vector<int> &rr); void evidenzia(vector<string> &t, string &parola, vector<int> &righe) 71
  • 72. Passaggio per riferimento costante • Laddove – il chiamato non modifica l'argomento ricevuto dal chiamante, ma – L'argomento è di dimensioni non trascurabili • Meglio usare const & void stampatesto(const vector<string> &t); void stamparighe(const vector<int> &rr); void evidenzia(vector<string> &t, const string &parola, vector<int> &righe) • Le funzioni così non possono modificare l'argomento ricevuto dal chiamante 72
  • 73. Programma principale #include <iostream> #include <vector> #include <string> #include <fstream> using namespace std; int main() { vector<string> testo; // il testo vettorializzato, inizialm. vuoto vector<int> righe; // l'indice delle righe, inizialmente vuoto string par; // la parola da cercare leggitesto(testo); // crea tutte le righe all'inizio cout << "Inserisci parola da ricercare" << endl; cin >> par; evidenzia(testo, par, righe); stampatesto(testo); stamparighe(righe); return 0; } 73
  • 74. Parametri array • Gli array hanno una importante differenza rispetto agli altri tipi primitivi o costruiti: non è definita la copia né all'inizializzazione ne all'assegnamento • Pertanto non si possono passare come argomenti per copia a una funzione • Tuttavia si possono lo stesso definire parametri e argomenti per copia, ma bisogna capire che cosa succede 74
  • 75. Parametri di tipo array void stampa(char[10]); •Apparentemente la funzione riceve in ingresso un array di 10 caratteri •In realtà riceve l'indirizzo (puntatore) al primo elemento di un array di lunghezza ignota! •E' equivalente a void stampa(char[]); void stampa(char *); // vedremo in seguito 75
  • 76. Cautele • Se il parametro denota l'indirizzo del primo elemento allora di solito NON va modificato • Meglio allora renderlo const void stampa(const char[10]); void stampa(const char[]); void stampa(const char *); 76
  • 77. Passaggio di argomenti di tipo array void stampaArray(const char regione[6]){ cout << regione << endl; } int main() { char array1[10]="Lombardia"; char array2[6]="Lazio"; stampaArray(array1); stampaArray(array2); return 0; } •Le dimensioni del parametro vengono ignorate •Pericoloso: se manca il terminatore dell'array il comportamento è indefinito •Usato spesso comunque, perché apparentemente "flessibile" 77
  • 78. Typedef e parametri array • Usare typdef non cambia typedef char arraycorto[6]; typedef char arraylungo[10]; void stampaArray(arraycorto regione) { cout << regione << endl; } int main() { arraylungo array1 = "Lombardia"; arraycorto array2 = "Lazio"; stampaArray(array1); stampaArray(array2); return 0; } 78
  • 79. Passaggio esplicito delle dimensioni • Per ovviare al pericolo di fuoriuscita, si può aggiungere un parametro esplicito che rappresenta le dimensioni – E' l'unica soluzione per array non terminati da '0' • Difetto: i due parametri sono indipendenti e il valore delle dimensioni deve essere gestito con cura dal programmatore – Se la dimensione passata è maggiore di quella reale, il comportamento del chiamato è indefinito 79
  • 80. Stampare un array di interi const int corto = 2; const int lungo = 4; void stampaArray(const int array[], int dim) { for (int i = 0; i < dim; ++i) cout << array[i] << endl; } int main() { int arraylungo[lungo]={1,2,3,4}; int arraycorto[corto]={5,6}; stampaArray(arraylungo, lungo); stampaArray(arraycorto, corto); return 0; } 80
  • 81. Warning • Non si stampa con cout << un array che non sia una stringa terminata da '0' const int lungo = 4; int main() { int arraylungo[lungo]={1,2,3,4}; cout << arraylungo; // errato !! return 0; } 81
  • 82. Modificabilità degli array • Gli array non si possono passare per copia • Il chiamato riceve l'indirizzo del primo elemento dell'array passato come argomento • Pertanto il chiamato modifica direttamente i valori dell'array del chiamante • Da considerare con cautela 82
  • 83. Modifica di un parametro array const int size = 4; void modificaInt(int i) { i = i + 100; } void modificaArray(int vett[], int dim) { for (int i = 0; i < dim; i++) vett[i] += 100; } int main() { int a = 0, v[size] = { 0, 1, 2, 3 }; cout << "Valori prima" << endl; cout << "a = " << a << endl; for (int i = 0; i < size; ++i) cout << "v[" << i << "] = " << v[i] << endl; cout << "Chiamo le funzioni..." << endl; modificaInt(a); modificaArray(v, 4); cout << "Valori dopo" << endl; cout << " a = " << a << endl; for (int i = 0; i < size; ++i) cout << "v[" << i << "] = " << v[i] << endl; return 0; } 83
  • 84. Passaggio di array multidimensionali • Ricordando che una matrice è in realtà un array di array f(int [][maxcol], int r, int c); • Dichiara una funzione che riceve in input una matrice, cioè maxcol array di interi • Tutte le dimensioni successive alla prima fanno parte del tipo e devono essere fissate 84
  • 85. Esempio void stampaMatrice(int mat[][2], int r, int c) { for (int i = 0; i < r; ++i) { for (int j = 0; j < c; ++j) // c è cmque fissato a 2 cout << mat[i][j] << " "; cout << 'n'; } } // il secondo indice DEVE valere 2 int main() { int matrice[2][2] = { 1, 2, 3, 4 }; int matricebis[3][2] = {5,6,7,8,9,10}; stampaMatrice(matrice, 2, 2); cout << endl; stampaMatrice(matricebis, 3, 2); return 0; } 85
  • 86. Perché la prima dimensione è libera? a[0,0] a[0,1] a[0] a[0,2] a[1,0] a[1,1] a[1] a[1,2] a[3,0] a[3,1] a[3] a[3,2] … int a [][3] • Matrice = Array di array di interi • Ogni valore dell'array, tranne quelli dell'array "più esterno" deve avere una dimensione fissa e nota – 3 celle nel caso presente 86
  • 87. Forme sintattiche equivalenti • Sono definite 3 forme sintattiche diverse per rappresentare una matrice e quindi anche un parametro di tipo matrice • Si capirà meglio dopo aver studiato il costruttore di tipo puntatore (*) • http://stackoverflow.com/questions/8767166/p 87
  • 88. Parametri di tipo array e struct • Un parametro di tipo struct si può passare sia per riferimento sia per valore (anche se la struct contiene campi di tipo array!) • Analogamente, una funzione può restituire una struct (anche se la struct contiene degli array) 88