Guida al C/C++
autore: BlackLight
xx/xx/xx
Funzioni e procedure:
Eccoci dunque ad uno dei momenti cruciali del nostro tutorial: l'uso delle funzioni e delle procedure.
Ogni linguaggio di programmazione ad alto livello mette a disposizione
del programmatore questi strumenti, ed il C non è da meno!
Abbiamo già incontrato nel corso di tutorial un esempio di funzione: il
main(). Il
main()
altro non è che una funzione speciale che viene eseguita all'inizio del
programma, ma ovviamente noi possiamo definire anche altre funzioni
(avevo già accennato che tutto ciò che si fa in C si fa tramite le
funzioni. Anche la
printf() che abbiamo usato nei paragrafi precedenti non è altro che una funzione definita in
stdio.h).
Per capire meglio come lavorano le funzioni in C, ci aiuteremo con la definizione matematica di funzione.
Sappiamo che una funzione matematica è scritta in genere nella forma
y=f(x), ossia
ad ogni valore della variabile indipendente x corrisponde uno ed un solo valore della variabile dipendente y. Prendiamo ad esempio la funzione
f(x)=x+2: ad ogni valore della x corrisponde uno ed un solo valore della funzione
f(x), se x è 0, f(x) è 2, se x è 1, f(x) è 3, e così via.
Però è possibile che in una funzione ci sia più di una variabile indipendente: ad esempio,
f(x,y)=x+y.
Le
"variabili indipendenti" delle funzioni nelle funzioni C sono i
parametri,
ossia i valori che si danno in input alla funzione (anche se è
possibile creare funzioni senza alcun parametro), mentre il "risultato"
della funzione (la "variabile dipendente") si ottiene usando la keyword
return che abbiamo già incontrato.
Ecco la struttura di una funzione in C:
tipo_ritornato nome_funzione(parametro1,parametro2...parametron) {
codice
codice
......
}
Ecco un piccolo esempio:
int square(int x) {
return x*x;
}
Questa funzione calcola il quadrato di un numero intero
x. La variabile
int x è il parametro che passo alla funzione. Ho stabilito all'inizio, dichiarando la funzione come
int, che il valore ritornato dalla funzione (la "variabile dipendente") deve essere di tipo int. Attraverso la direttiva
return
stabilisco quale valore deve ritornare la funzione (in questo caso il
quadrato del numero x, ossia x*x). In matematica, una funzione del
genere la potrei scrivere come
f(x)=x²...
Questa funzione la posso richiamare all'interno del
main() o di qualsiasi altra funzione del programma. Esempio:
int y; // Dichiaro una variabile int
y = square(2); // Passo alla funzione square il valore 2,
// in modo che calcoli il quadrato di 2
printf ("Quadrato di 2: %d\n",y);
Ovviamente, posso dichiarare un'infinità di funzioni in questo modo.
Ecco ad esempio una funzione che calcola la somma di due numeri:
int somma(int a, int b) {
return a+b;
}
Invocazione:
int c;
c = somma(2,3); // c vale 5
La maggior parte delle funzioni matematiche, in ogni modo, sono dichiarate nel file
math.h
(ci sono ad esempio funzioni per calcolare il seno, il coseno o la
tangente di un numero reale, il logaritmo, la radice quadrata, la
potenza n-esima...), quindi se vi interessa fare un programma di
impostazione matematica date un'occhiata a questo file per capire quale
funzione usare.
Ovviamente, è anche possibile creare funzioni senza alcun parametro in input. Esempio (stupido):
int ritorna_zero() {
return 0;
}
Vediamo ora come inserire una funzione nel nostro programma. Le
funzioni in C possono andare in qualsiasi parte del codice, ma l'ANSI-C, per evitare confusione, ha imposto che all'inizio del
programma ci vadano i prototipi delle funzioni usate dal programma
stesso. Un prototipo non è altro che la funzione vera e propria (tipo
ritornato, nome e parametri) senza però la sua implementazione, ossia
senza il codice fra le parentesi graffe {}. Esempio:
int square(int x);
Ecco qui un programmino d'esempio che calcola il quadrato di un numero intero stabilito attraverso la funzione
square():
/* square.c */
#include < stdio.h >
int square(int x); // Prototipo della funzione
int main() {
int y; // Variabile intera
y = square(3); // Ora y vale 9
printf ("Quadrato di 3: %d\n",y);
// Più brevemente, potremo scrivere:
// printf ("Quadrato di 3: %d\n",square(3));
// senza neanche "scomodare" la variabile y
return 0;
}
int square(int x) { // Implementazione della funzione square()
return x*x;
}
Nei programmi di grandi dimensioni, in genere si mette il prototipo
della funzione in un file header (con estensione .h), l'implementazione
in un file .c e poi il programma vero e proprio nel file main.c.
Esempio:
/* Questo è il file square.h */
int square(int x);
/* Questo è il file square.c */
int square(int x) {
return x*x;
}
/* Questo è il file main.c */
#include < stdio.h >
#include "square.h"
// Ovviamente includo il file square.h
int main() {
printf ("Quadrato di 4: %d\n",square(4));
return 0;
}
Ovviamente, quando vado a compilare questo programma devo fare una cosa del genere:
gcc -o square main.c square.c
Ma per i nostri piccoli programmini non è il caso di fare una cosa del genere! Ci va tutto in un file.
Un discorso simile a quello delle funzioni vale anche per le
procedure;
le procedure non solo altro che funzioni "speciali", funzioni che non
hanno un valore ritornato: eseguono un pezzo di codice ed esce. Per
concludere una procedura non è necessario il
return (in quanto non ritorna alcun valore): al massimo ci possiamo mettere un
return;. Per dichiarare una procedura userò la keyword
void:
void hello() {
printf ("Hello world!\n");
return; // Questa riga è opzionale
}
Quando voglio chiamare questa procedura all'interno di una qualsiasi funzione, basterà fare così:
hello();
Esempio:
#include < stdio.h >
void hello(); // Prototipo della procedura
int main() {
hello(); // Stampo la scritta "Hello world!"
// attraverso la procedura hello()
return 0;
}
void hello() { // Implementazione della procedura
printf ("Hello world!\n");
}
Ovviamente anche alle procedure posso passare qualche parametro. Esempio:
void stampa_var(int x) {
printf ("Valore della variabile passata: %d\n",x);
}
Invocazione:
stampa_var(3); // L'output è: "Valore della variabile passata: 3
Nota tecnica: attenzione a non fare cose del genere!
int square(int x);
double square(double x);
Quando vado a chiamare la funzione:
square(3);
il compilatore non sa che funzione chiamare e va in crash! Proprio per
evitare ambiguità del genere, la maggior parte dei compilatori danno un
errore (o almeno un warning) quando nel programma compaiono scritture
del genere (tuttavia, nel C++ cose del genere sono possibili, con
l'overloading delle funzioni,
ossia con la dichiarazione di più funzioni con lo stesso nome MA con la
lista dei parametri differente. In ogni caso, una scrittura come quella
di sopra darà problemi anche in C++, in quanto entrambe le funzioni
hanno un solo parametro e il compilatore, nel momento dell'invocazione,
non sa quale funzione chiamare).