Funzioni
Una funzione C/C++, analogamente ad una funzione Pascal, e` caratterizzata
da un nome che la distingue univocamente nel suo scope (le regole di visibilita`
di una funzione sono analoghe a quelle viste per le variabili), da un insieme
(eventualmente vuoto) di argomenti (parametri della funzione) separati da virgole,
e eventualmente il tipo del valore ritornato:
// ecco una funzione che riceve due interi
// e restituisce un altro intero
int Sum(int a, int b);
Gli argomenti presi da una funzione sono quelli racchiusi tra le parentesi
tonde, si noti che il tipo dell'argomento deve essere specificato singolarmente
per ogni parametro anche quando piu` argomenti hanno lo stesso tipo; la seguente
dichiarazione e` pertanto errata:
int Sum2(int a, b); // Errore!
Il tipo del valore restituito dalla funzione deve essere specificato prima
del nome della funzione e se omesso si sottointende int ; se una
funzione non ritorna alcun valore va dichiarata void , come mostra
quest'altro esempio:
// ecco una funzione che non ritorna alcun
valore
void Foo(char a, float b);
Non e` necessario che una funzione abbia dei parametri, in questo caso basta
non specificarne oppure indicarlo esplicitamente:
// funzione che non riceve parametri
// e restituisce un int (default)
Funny();
// oppure
Funny2(void);
Il primo esempio vale solo per il C++, in C non specificare alcun argomento
equivale a dire "Qualsiasi numero e tipo di argomenti"; il
secondo metodo invece e` valido in entrambi i linguaggi, in questo caso void
assume il significato "Nessun argomento".
Anche in C++ e` possibile avere funzioni con numero e tipo di argomenti non
specificato:
void Esempio1(...);
void Esempio2(int Args, ...);
Il primo esempio mostra come dichiarare una funzione che prende un numero imprecisato
(eventualmente 0) di parametri; il secondo esempio invece mostra come dichiarare
funzioni che prendono almeno qualche parametro, in questo caso bisogna prima
specificare tutti i parametri necessari e poi mettere ... per indicare eventuali
altri parametri.
Quelle che abbiamo visto finora comunque non sono definizioni di funzioni,
ma solo dichiarazioni, o per utilizzare un termine proprio del C++, prototipi
di funzioni.
I prototipi di funzione sono stati introdotti nel C++ per informare il compilatore
dell'esistenza di una certa funzione e consentire un maggior controllo al fine
di identificare errori di tipo (e non solo) e sono utilizzati soprattutto all'interno
dei file header per la suddivisione di grossi programmi in piu` file e la realizzazione
di librerie di funzioni; infine nei prototipi non e` necessario indicare il
nome degli argomenti della funzione:
// la funzione Sum vista sopra poteva
// essere dichiarata anche cosi`:
int Sum(int, int);
Per implementare (definire) una funzione occorre ripetere il prototipo, specificando
il nome degli argomenti (necessario per poter riferire ad essi, ma non obbligatorio
se l'argomento non viene utilizzato), seguito da una sequenza di istruzioni
racchiusa tra parentesi graffe:
int Sum(int x, int y) {
return x+y;
}
La funzione Sum e` costituita da una sola istruzione che calcola
la somma degli argomenti e restituisce tramite la keyword return il risultato
di tale operazione. Inoltre, benche` non evidente dall'esempio, la keyword return
provoca l'immediata terminazione della funzione; ecco un esempio non del tutto
corretto, che pero` mostra il comportamento di return:
// calcola il quoziente di due numeri
int Div(int a, int b) {
if (b==0) return "errore";
return a/b;
}
Se il divisore e` 0, la prima istruzione return restituisce (erroneamente)
una stringa (anzicche` un intero) e provoca la terminazione della funzione,
le successive istruzioni della funzione quindi non verrebbero eseguite.
Concludiamo questo paragrafo con alcune considerazioni:
- La definizione di una funzione non deve essere seguita da ; (punto
e virgola), cio` tra l'altro consente di distinguere facilmente tra prototipo
(dichiarazione) e definizione di funzione poiche` un prototipo e` terminato
da ; (punto e virgola), mentre in una definizione la lista di argomenti
e` seguita da { (parentesi graffa aperta);
- Ogni funzione dichiarata non void deve restituire un valore, ne segue che
da qualche parte nel corpo della funzione deve esserci una istruzione
return
con un qualche argomento (il valore restituito), in caso contrario viene segnalato
un errore; analogamente l'uso di return in una funzione void
costituisce un errore, salvo il caso in cui la keyword sia utilizzata senza
argomenti (provocando cosi` solo la terminazione della funzione);
- La definizione di una funzione e` anche una dichiarazione per quella funzione
e all'interno del file che definisce la funzione non e` obbligatorio indicarne
il prototipo, vedremo meglio l'importanza dei prototipi piu` avanti;
- Non e` possibile dichiarare una funzione all'interno del corpo di un'altra
funzione.
Ecco ancora qualche esempio relativo alla seconda nota:
int Sum(int a, int b) {
a + b;
} // ERRORE! Nessun valore restituito.
int Sum(int a, int b) {
return;
} // ERRORE! Nessun valore restituito.
int Sum(int a, int b) {
return a + b;
} // OK!
void Sleep(int a) {
for(int i=0; i < a; ++i) {};
} // OK!
void Sleep(int a) {
for(int i=0; i < a; ++i) {};
return;
} // OK!
La chiamata di una funzione puo` essere eseguita solo nell'ambito dello scope
in cui appare la sua dichiarazione (come gia` detto le regole di scoping per
le dichiarazioni di funzioni sono identiche a quelle per le variabili) specificando
il valore assunto da ciascun parametro formale:
void Sleep(int Delay); // definita da
qualche parte
int Sum(int a, int b); // definita da qualche
parte
void main(void) {
int X = 5;
int Y = 7;
int Result = 0;
/* ... */
Sleep(X);
Result = Sum(X, Y);
Sum(X, 8);
// Ok!
Result = Sleep(1000); // Errore!
return 0;
}
La prima e l'ultima chiamata di funzione mostrano come le funzioni void (nel
nostro caso Sleep ) siano identiche alle procedure Pascal, in particolare
l'ultima chiamata a Sleep e` un errore poiche` Sleep
non restituisce alcun valore.
La seconda chiamata di funzione (la prima di Sum ) mostra come recuperare
il valore restituito dalla funzione (esattamente come in Pascal). La chiamata
successiva invece potrebbe sembrare un errore, in realta` si tratta di una chiamata
lecita, semplicemente il valore tornato da Sum viene scartato;
l'unico motivo per scartare il risultato dell'invocazione di una funzione e`
quello di sfruttare eventuali effetti laterali di tale chiamata.
|