Le funzioni sono uno degli strumenti più potenti del C; esse infatti permettono un notevole risparmio e riuso del codice, con vantaggi piuttosto evidenti. Se prendiamo solo in considerazione questo aspetto modulare le funzioni ereditano i concetti di procedure o subroutine.
Alcuni linguaggi fanno distinzione tra funzioni che ritornano un valore e quelle che, invece, non ritornano valori; il C assume che ogni funzione ritorni un valore, questo accade utilizzando l’istruzione return seguita, eventualmente, da un valore; se omettiamo l’istruzione
return
, essa non ritornerà alcun valore e non potrà ad esempio essere utilizzata nella parte destra di un assegnamento o come parametro per altre chiamate a funzione.Prendiamo ad esempio, la definizione di una funzione che prende un
double
e un int
e opera un elevamento a potenza, restituendo il risultato:
double
elevamento_potenza(
double
valore,
int
potenza)
{
double
valore_ritorno = 1.0;
int
i;
for
(i=0; i<potenza; i++)
{
valore_ritorno *= valore;
}
return
(valore_ritorno);
}
Analizzando questa funzione possiamo innanzitutto far notare che il calcolo eseguito, per quanto complesso e poco ortodosso, restituisce il valore corretto anche quando la potenza vale zero; iniziamo, dunque, a studiare il codice nel dettaglio:
double
elevamento_potenza(
double
valore,
int
potenza)
Questa è la definizione della funzione, che ci dice il tipo del valore di ritorno (
double
), il nome della funzione (elevamento_potenza()
) e la lista di argomenti usati dalla funzione con il tipo (double
e int
) ed il nome (valore e potenza) corrispondente;
return
(valore_ritorno);
Quando si raggiunge un’istruzione
return
, il controllo del programma ritorna a chi ha chiamato la funzione. Il valore ritornato è quello posto dopo la parola return
;se si chiude la funzione prima di mettere un’istruzione “return
“, la funzione ritorna automaticamente, ed il valore ritornato potrebbe non avere un significato valido.Il valore ritornato può essere manipolato a piacimento, infatti se una funzione restituisce un risultato, possiamo assegnarlo, ad esempio, ad una variabile che poi potremmo usare all’interno del programma come qualsiasi altra variabile;
double
val = 100.0;
int
pot = 3;
double
risultato = elevamento_potenza(val, pot);
Esistono anche funzioni che non ritornano alcun valore, in questo caso si parla di funzioni void, come mostrato di seguito:
void
stampa_errore(
int
linea)
{
fprintf
(stderr,
"Errore: linea %dn"
, linea);
}
Una funzione
void
non deve contenere necessariamente un’istruzione return
, anche se essa può essere usata per uscire dalla funzione in maniera opportuna, un po’ come l’uso del break
all’interno dei cicli.In questo caso abbiamo presentato anche l’uso dell’istruzione
fprintf()
, una variante della printf()
, che spiegheremo in dettaglio nel corso della guida; basti sapere che in questo caso abbiamo stampato il messaggio di errore sullo “stderr
” che può essere un file o un messaggio a video (generalmente quest’ultimo).Per richiamare la funzione, basta, ovviamente, scrivere il nome con, tra gli argomenti, il numero di linea in cui si è verificato l’errore;
int
linea_err = 14;
stampa_errore(linea_err);
Prototipi di funzioni
Va fatta una piccola nota riguardante le funzioni, in merito alla prototipazione delle funzioni, ovvero la creazione di prototipi.
In un pezzo di codice non possiamo utilizzare una funzione prima di averla dichiarata, perché per il compilatore essa non è stata ancora “creata”. Per ovviare a questo problema utilizziamo i prototipi che ci permettono una migliore organizzazione del codice.
Un prototipo di una funzione non è altro che la sua dichiarazione, fatta senza specificare il corpo della funzione stessa. Si scrive, quindi, solo la dichiarazione iniziale comprendente il nome ed il tipo restituito dalla funzione, e gli argomenti passati come parametro; questo avviene perché ogni funzione è utilizzabile solamente quanto è stata dichiarata.
Ecco due esempi, il primo errato, il secondo corretto:
// questo non funziona
#include <stdio.h>
int
main()
{
int
var = 5;
stampa_doppio(var);
}
void
stampa_doppio(
int
variabile)
{
printf
(
"Il doppio di %d è %d "
, variabile, 2*variabile);
}
// questo funziona
#include <stdio.h>
void
stampa_doppio(
int
variabile);
// prototipo della funzione
int
main()
{
int
var = 5;
stampa_doppio(var);
}
void
stampa_doppio(
int
variabile)
{
printf
(
"Il doppio di %d è %d "
, variabile, 2*variabile);
}
Passaggio di parametri alle funzioni
In C, quando si passano gli argomenti (variabili) alle funzioni, bisogna prestare attenzione al passaggio di array ad una o più dimensioni; la regola dice che la prima dimensione dell’array può non essere specificata, mentre la seconda e le altre devono esserlo. Un esempio chiarirà meglio il concetto:
void
stampa_array_uni(
int
dim,
int
array_uni[])
{
int
i;
for
(i=0; i < dim; i++)
{
printf
(
"%d "
, array_uni[i]);
}
}
void
stampa_array_bid(ind dimx,
int
dimy,
int
array_bid[][6]);
{
int
i, j;
for
(i=0; i < dimx; i++)
{
for
(j=0; j < dimy; j++)
{
printf
(
"%d "
, array_uni[i][j]);
}
printf
(
"\n"
);
}
}