*
* Fino ad ora i nostri programmi sono stati visti come una sequenza di
* istruzioni all'interno della funzione main().
* Pero' abbiamo chiamato funzioni gia' disponibili nella libreria standard,
* come printf() e scanf(). Non abbiamo fatto altro che chiamare delle
* funzioni esterne al blocco di istruzioni main(), passandogli anche degli
* argomenti, ad esempio:
*
* printf("Salve mondo");
*
* L'argomento che passiamo al printf e' "Salve Mondo": non facciamo altro
* che chiamare una subroutine gia' pronta che si occupa di stampare
* caratteri a video, passandogli il testo da stampare.
* Da notare che subroutine si puo' tradurre in "sottoprogramma".
* Ora vedremo come creare delle nostre funzioni, che potremo usare per
* rendere piu' semplice il riutilizzo di routine gia' scritte, e per
* aumentare la leggibilita' del listato.
* La nostra funzione sara' un blocco di istruzioni ISOLATO e posto dopo
* la fine della funzione main (puo' essere messa anche prima, tanto il
* programma parte comunque dalla funzione main).
* Per eseguire quel blocco di istruzioni da un qualsiasi punto all'interno
* della funzione main, dovremo fare una cosa che si puo' tradurre in:
* "Salta ad eseguire le istruzioni che trovi nel blocco della funzione,
* poi ritorna qua a finire di eseguire le istruzioni di Main".
* Per motivi di standard, dovremo anche dichiarare all'inizio del programma
* le nostre funzioni indicandone i parametri input/output (prototipo).
* Di solito si fa in modo che una funzione svolga un unico compito, almeno
* quando occorre svolgere nuovamente quel dato compito basta richiamarla.
* Una funzione normalmente richiede dei dati in ingresso e opzionalmente
* produce un risultato, ossia il valore restituito. Comunque certe funzioni
* possono non richiedere dati in ingresso e/o non restituire alcun valore.
* Facciamo degli esempi: una funzione che non ci restituisce nessun valore,
* ma richiede i dati di ingresso, potrebbe essere una funzione dedicata alla
* stampa di un numero: in entrata vuole il testo da stampare, e non ci da
* niente all'uscita.
* Lo scanf, per esempio, in piu' ci restituisce il valore inserito da
* tastiera, se vi ricordate. Quindi e' una funzione che vuole dei parametri
* in entrata e ce ne restituisce in uscita.
* Una funzione che non richiede dati in ingresso e che non ne restituisce
* in uscita potrebbe essere una funzione che pulisce lo schermo, ad esempio.
* Una che non richiede dati in ingresso e ne da in uscita potrebbe essere
* una routine che ci da un numero a caso.
* Vediamo il formato di una funzione:
*
* TipoRitorno NomeFunzione(Lista tipo e nome argomenti passati)
* {
* corpo della funzione
* }
*
* SpecificaTipo = dichiara il tipo di valore (int, float, ecc.) che la
* funzione restituisce attraverso l'istruzione return.
* NomeFunzione = Il nome della funzione, che scegliamo a nostro piacimento,
* basta che non sia uguale ad una delle parole chiave del C.
* Lista Argomenti = Una lista di nomi di variabili separate da virgole,
* preceduti dal tipo di variabile stesso (int, float...)
* per indicare i parametri in ENTRATA della funzione,
* ovvero i dati che gli vengono passati alla chiamata.
*
* Ecco un esempio:
*
* int somma(int a,int b) * Funzione che restituisce un valore INT *
* * ed ha 2 parametri in entrata sempre INT *
* {
* return(a+b); * Restituisci il risultato di a+b
* }
*
* Per chiamare questa funzione dal corpo di main() si fa in questo modo:
*
* somma(variabile1,variabile2)
*
* Pero' dovremo salvare il valore restituito, in una variabile.
* Ad esempio, potremmo mettere un:
*
* papero = somma(pippo,pluto);
*
* Naturalmente si faceva prima a sommare direttamente, ma per capire come
* fare una funzione e' un'esempio lampante.
* Da notare che non e' necessario passare alla funzione variabili con gli
* stessi nomi di quelle usate nella funzione (che in questo caso si chiamano
* a,b), basta che siano 2 variabili dello stesso tipo (in questo caso int).
* Abbiamo incontrato l'istruzione return, che serve per "ritornare" ed
* eventualmente "restituire" qualcosa al programma che ha chiamato la
* funzione: nel nostro caso restituisce anche un valore.
* Ora sorge un dubbio: anche main() e' una funzione, eppure la dichiariamo
* senza specificarne il tipo, senza argomenti, e la abbiamo fin qui
* terminata senza il return!!!
* Infatti non siamo stati molto rigorosi. I compilatori solitamente assumono
* che se non si specifica il tipo, questo e' sempre INT, e se non si mettono
* argomenti, questi non ci sono. Per essere pignoli si dovrebbe mettere un
* (void) al posto di (), infatti void significa "vuoto".
* Inoltre, se non e' presente il return, il compilatore restituisce 0.
* Riscriviamo rigorosamente il nostro primo programmino:
*
* int main(void)
* {
* printf("Pippo\n");
* return(0);
* }
*
* In questo modo dichiariamo esplicitamente quello che prima era stato
* assunto per default dal compilatore: siamo piu' coscenti della situazione.
* Da notare che lo 0 e' restituito al DOS alla fine del programma, per
* tranquillizzarlo sulla corretta riuscita del nostro programmino.
* Il DOS comunque non si interessa molto a questo valore...
* Fino ad ora siamo stati poco rigidi sullo standard ANSI per semplificare
* le cose, ma da ora in avanti non sgarriamo piu'.
* Proprio per questo, occorre precisare un'altra cosa: se nel listato
* mettiamo prima le nostre funzioni, poi il main() che le chiama, non ci
* saranno errori, infatti quando il compilatore trova il riferimento alla
* nostra funzione somma() all'interno della funz. principale main(), sa gia' i
* i suoi parametri in entrata e in uscita, perche' ha gia' letto la sua
* dichiarazione (la compilazione parte dall'alto e scende!).
* Se invece mettessimo prima il main(), poi la nostra funzione, il povero
* compilatore si troverebbe di fronte un fantomatico somma() di cui non sa
* niente, perche' viene definito piu' sotto, dove non e' ancora arrivato
* a compilare. Non potendo verificare se a somma() sono passati i tipi di
* variabile giusti, interrompe la compilazione.
* Per questo e' stato reso possibile (con lo standard ANSI) dichiarare
* anticipatamente le nostre funzioni, all'inizio del listato, in modo da
* non "spaventare" il compilatore. Atrimenti avremmo dovuto sempre mettere
* prima le nostre funzioni, infine, in fondo al listato, il main().
* Questa anticipazione di dichiarazione e' detta "prototipo", ed ha questa
* forma:
*
* TipoRitorno NomeFunzione(TipoParametro1, TipoParametro2);
*
* Ecco il nostro esempio pratico:
*
* int somma(int, int);
*
* In pratica e' come la dichiarazione "vera", ma si termina con un punto e
* virgola e al posto dei parametri (tipo+nomevariabile) si mette solo il
* loro tipo: in questo modo il compilatore sa da subito che la funzione
* somma() ha 2 parametri di tipo INT in entrata ed uno in uscita, sempre di
* tipo INT.
* Un prototipo e' un modello limitato di un'entita' piu' completa che
* deve arrivare dopo.
* Ora vediamo un programma che, oltre a chiamare una funzione, e' anche
* scritto in modo rigoroso:
*/
#include <stdio.h> /* Includiamo la libreria standard */
/* Forniamo una dichiarazione anticipata (prototipo) delle nostre funzioni */
int somma(int,int);
/* Funzione principale e inizio del programma */
int main(void) /* Funzione principale, eseguita per prima */
{ /* Inizio della funzione main() */
int pippo,pluto,papero; /* Definiamo 3 variabili intere */
printf("\nDammi un numero: ");
scanf("%d",&pippo);
printf("\nDammi un'altro numero: ");
scanf("%d",&pluto);
papero = somma(pippo,pluto); /* Chiamata della funzione! */
printf("\nLa somma dei due e' %d\n", papero);
return(0); /* la funzione main restituisce uno 0 intero */
} /* Fine della funzione main() */
/* Ora definiamo una funzione chiamata somma(), che in entrata richiede 2
* variabili di tipo intero, e in uscita restituisce un'altro numero intero,
* somma dei 2 in entrata:
*/
int somma(int a,int b)
{
printf("Sto calcolando la somma... com'e' difficile!!!");
return(a+b);
}
/* Comunque, se non vi disturba mettere il main() in fondo al listato, e'
* preferibile mettere all'inizio le funzioni, in modo da risparmiarci di
* scrivere i prototipi, e soprattutto di cambiarli ogni volta che si
* cambiano i parametri delle nostre funzioni, che naturalmente non saranno
* le definitive al primo colpo.
* Una cosa da notare e' che la funzione somma() usa 2 variabili con nome
* diverso rispetto a quello delle variabili usate dalla funzione main()
* per chiamarla.
* Infatti le due variabili a,b sono LOCALI, usate solo dalla funzione, ed
* assumeranno i valori passati con "somma(pippo,pluto)": il valore di pippo
* sara' copiato in a, quello di pluto in b e la loro somma sara' data in
* uscita, e copiata in papero.
* Altra cosa da notare e' che in vecchi programmi C, scritti prima che si
* consolidasse lo standard ANSI, si potrebbero trovare dichiarazioni
* di questo tipo:
*
* int somma(a,b)
* int a,b;
* {
* ...
* }
*
* Il nuovo sistema e' molto piu' compatto, dato che si evita di definire
* a parte il tipo delle variabili usate come parametri in entrata.
*/
