Funzioni in C++

Concetti avanzati

Moreno Marzolla

moreno.marzolla@pd.infn.it

Copyright © 2006 Moreno Marzolla

This work is licensed under the Creative Commons Attribution-Noncommercial-Share Alike 2.5 Italy License. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/2.5/it/ or send a letter to Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA.

Passaggio di vettori

Abbiamo visto come passare gli eventuali parametri ad una funzione

Come si fa a passare un vettore (array) come parametro di una funzione?

int somma( int a, int b )

{

int risultato;

risultato = a+b;

return risultato;

}

Sintassi

Es: Scrivere una funzione somma_vet() che riceve come parametro un array a[] di 10 interi. La funzione restituisce come risultato intero, che è la somma dei valori di tutti gli elementi del vettore a[]

int somma_vet( int a[] )

{

int i;

int s = 0;

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

s = s+a[i];

}

return s;

}

Nota: non si deve mai

specificare la dimensione

(numero di elementi) di a!

Osservazione

Quando si scrive una funzione che richiede un parametro di tipo array, si mettono parentesi [] dopo il nome del parametro

Spesso è anche necessario passare la dimensione dell'array alla funzione, perché la funzione non ha altro modo per richiederne la dimensione.

Nell'esempio precedente assumevamo che l'array passato come parametro avesse SEMPRE 10 elementi

Esempio

#include <iostream>

using namespace std;

int somma_vet( int a[], int dim_a )

{

int i;

int s = 0;

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

s = s+a[i];

}

return s;

}

int main( void )

{

int vet[5] = {1, 2, 3, 4, 5};

cout << “La somma vale “ << somma_vet( vet, 5 ) << endl;

return 0;

}

Questo parametro viene usato per indicare la dimensione (numero di elementi) del vettore a[]

Nota

Attenzione che, a differenza di tutti gli altri parametri, i parametri di tipo array vengono modificati se si tenta di scriverci dentro

Esempio

#include <iostream>

using namespace std;

void azzera( int a[], int dim_a )

{

int i;

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

a[i] = 0;

}

}

int main( void )

{

int vet[5] = {1,2,3,4,5};

azzera( vet, 5 );

// a questo punto tutti gli elementi di vet[] valgono zero

return 0;

}

Esercizi

Scrivere una funzione prodotto_scalare() con le seguenti caratteristiche:

La funzione accetta tre parametri: un vettore di double a[], un vettore di double b[] (che devono avere la stessa dimensione), e la loro dimensione dim

La funzione restituisce un numero ottenuto facendo il prodotto scalare tra i vettori a[] e b[], ossia il valore calcolato come:
a[0]*b[0] + a[1]*b[1] + ... + a[n-1]*b[n-1]
assumendo che n sia la dimensione di a[] e b[]

Passaggio per valore

Il modo di passare parametri che abbiamo usato fino ad ora (con l'esclusione dei vettori) si chiama passaggio di parametri per valore

Significa che ad una funzione viene passato una copia del valore del parametro

Se all'interno della funzione modifico il valore di un parametro, la modifica non avrà alcun effetto al di fuori della funzione

#include <iostream>
using namespace std;

void azzera( int a )

{

a = 0;

}

int main( void )

{

int x = 5;

azzera( x );

cout << x << endl; // stampa 5

return 0;

}

Passaggio per riferimento

E' anche possibile fare in modo che la modifica del parametro di una funzione si ripercuota sul valore effettivamente passato

Come succede nel caso del passaggio di vettori

In questo caso usiamo il passaggio di parametri per riferimento

#include <iostream>
using namespace std;

void azzera( int &a )

{

a = 0;

}

int main( void )

{

int x = 5;

azzera( x );

cout << x << endl; // stampa zero!

return 0;

}

Passaggio per riferimento

Per passare un parametro per riferimento (cioè per fare in modo che ogni modifica che la funzione fa a quel parametro venga “vista” anche al di fuori della funzione) è sufficiente precedere il nome del parametro col simbolo &

Esempio

Scrivere una funzione scambia() che accetta due parametri a, b di tipo int. La funzione non restituisce alcun risultato, ma scambia tra loro i valori di a e b. In altre parole, la seguente funzione main():
deve stampare “1 5”;

// Dichiarazione di scambia()

int main( void )

{

int a=5;

int b=1;

scambia( a, b );

cout << a << “ “ << b << endl;

return 0;

}

Soluzione

#include <iostream>

using namespace std;

void scambia( int &a, int &b )

{

int tmp = a;

a = b;

b = tmp;

}

int main( void )

{

int a=5;

int b=1;

scambia( a, b );

cout << a << “ “ << b << endl;

return 0;

}

Esercizi per domani / 1

Scrivere una funzione inverti() che accetta come parametro un vettore di interi a[] e la sua dimensione dim_a; la funzione inverte gli elementi di a[] (cioè scambia il primo elemento di a con l'ultimo, il secondo col penultimo ecc.)

Scrivere una funzione main() per testare la inverti() appena realizzata

Esercizi per domani / 2

Scrivere una funzione somma_alternata che accetta come parametro un vettore di interi a[] e la sua dimensione dim_a. La funzione calcola la somma alternata di a[], ossia calcola la quantità:
a[0] - a[1] + a[2] - a[3] + a[4] ...

Esercizi per domani / 3

Scrivere una funzione fill() che accetta come parametri un vettore di interi a[], la sua dimensione dim_a e un ulteriore intero x compreso tra 0 e 4 (estremi inclusi). La funzione non restituisce alcun risultato.

La funzione modifica il vettore a[] come segue. Il primo elemento di a[] viene posto a x, il secondo a x+1 e così via. Ogni volta che il valore assegnato ad un elemento di a raggiunge 4, l'elemento successivo ricomincia da zero

Esempio: se dim_a=10, x=3, allora dopo la chiamata fill( a, dim_a, x ) il vettore deve contenere questi valori
{ 3, 4, 0, 1, 2, 3, 4, 0, 1, 2 };

Esempio: se dim_a=10, x=4, allora dopo la chiamata fill( a, dim_a, x ) il vettore deve contenere questi valori
{ 4, 0, 1, 2, 3, 4, 0, 1, 2, 3 };

Cenni alle funzioni ricorsive

Ovviamente è possibile che una funzione f(), al suo interno, richiami un'altra funzione g()

int g( int x )

{

return x+1;

}

int f( int x, int y )

{

return g(x) + g(y);

}

// domanda: cosa calcola f( 3, 4 )?

Funzioni ricorsive

E' anche possibile per una funzione f() richiamare se' stessa

Esempio: come si calcola il fattoriale di un numero non negativo n?

fattoriale( 0 ) = 1

se n>0, fattoriale( n ) = n * fattoriale( n-1 )

Tutto questo si può direttamente implementare in C++

Esempio fattoriale

#include <iostream>

using namespace std;

int fattoriale( int n )

{

if ( n == 0 ) {

return 1;

} else {

return n * fattoriale( n-1 );

}

}

int main( void )

{

cout << fattoriale( 3 ) << endl; // stampa 3*2*1 = 6

cout << fattoriale( 4 ) << endl; // stampa 4*3*2*1 = 24

return 0;

}

Osservazione

Perché la ricorsione funziona?

E' sempre necessario definire almeno un caso base, ossia un caso in cui il valore della funzione è univocamente determinato senza invocare nuovamente la funzione stessa

nel caso del fattoriale, 0! = 1, per definizione

Ogni volta che la funzione invoca se stessa, è necessario che lo faccia passando dei parametri che si “avvicinino” al caso base

il calcolo del fattoriale di n richiede il calcolo del fattoriale di n-1, che a sua volta richiede il fattoriale di n-2 finché inevitabilmente si arriva a calcolare il fattoriale di 0, e qui la ricorsione si ferma

Calcolo del Massimo Comune Divisore

Definiamo la funzione MCD( a, b ) che calcola il massimo comune divisore tra due interi non negativi a e b

Se a=b, allora MCD( a, b ) = a (oppure b, tanto sono uguali!)

Se a>b, allora MCD( a, b ) = MCD( a-b, b )

Se b>a, allora MCD( a, b ) = MCD( a, b-a )

Soluzione

#include <iostream>

using namespace std;

int MCD( int a, int b )

{

if ( a == b ) {

return a;

} else {

if ( a > b ) {

return MCD( a-b, b );

} else {

return MCD( a, b-a );

}

}

}

int main( void )

{

cout << MCD( 3, 4 ) << endl; // stampa 1

cout << MCD( 12, 4 ) << endl; // stampa 4

cout << MCD( 18, 12 ) << endl; // stampa 6

return 0;

}

Esercizio per domani

Scrivere una funzione ricorsiva che calcola il prodotto tra due interi non negativi a e b come segue:

Se a=1, prodotto( a, b ) = b

Se a>1, prodotto( a, b ) = b + prodotto( a-1, b );

Come modifichereste la funzione perché si comporti correttamente anche se a=0?

Moreno Mazolla

2005-09-25T15:46:34