Le funzioni sono uno degli strumenti più potenti del linguaggio 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)
Funzioni in C: primo esempio
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);
Funzione C void
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 in C
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");
}
}
Esempio Pratico di Funzioni in C
(Visualizza il sorgente completo)
All'interno del programma vengono usate svariate funzioni. Alcune esclusivamente create per inserire/modificare/eliminare i contatti della rubrica, altre, come nel caso della funzione substring, per una funzione di utilità non disponibile in C.
Di seguito mostriamo la dichiarazione dei prototipi delle funzioni all'inizio del programma e la funzione substring()
.
45 // Prototipi delle funzioni
46 void visualizzaContatto(struct elemento* p);
47 struct elemento *aggiungiContatto(struct elemento *p);
48 struct elemento *modificaContatto(struct elemento *p);
49 struct elemento *rimuoviContatto(struct elemento *p);
50 struct elemento *leggiDaFile(struct elemento *p);
51 int salvaSuFile(struct elemento *p);
52 void substring(char *dest, char *source, int i_base, int i_dim);
53 void pausa();
[...]
47 /*
548 * Funzione che prende una sottostringa partendo da un'indice
549 * base per un numero di caratteri pari a dimensione
550 */
551 void substring(char* dest, char *source, int i_base, int i_dim)
552 { // substring() - OPEN
553
554 int i = 0;
555
556 for (i=i_base; i<i_dim+1; i++)
557 { // FOR - OPEN
558
559 dest[i-i_base] = source[i];
560
561 } // FOR - CLOSE
562
563 dest[i]='�';
564
565 return;
566
567 } // substring() - CLOSE