* * 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. */